Lab -- A Flask app to run model predictions via JSON
By Dave Eargle
In this lab, you will create a Flask app which will enable users to send HTTP POST requests with JSON requesting a prediction from a pre-fit ML model.
Install Flask
Install Flask into your virtual environment.
Read the Flask Quickstart
Windows users, don't use cmd
for flask run
! Instead, use powershell. If you use cmd
, then your flask app
will randomly hang on requests until you send the interrupt signal (ctrl+c
).
See this SO post
Complete the Flask Quickstart, through the Routing section.
Later sections of this lab will reference later sections of the flask quickstart.
Using environment variables
In this and future labs, you will use environment variables.
Environment variables are key-value pairs that are not explicitly defined in code.
Rather, they exist only in the environment available to your running application.
But they are accessible via code, such as via python’s os.environ
.
When you install Flask via pip, a flask
command is made available from your shell.
The quickstart tells you that you can configure flask run
by setting environment
variables such as FLASK_APP
. The flask docs tell several ways to export
the
variables. The export KEY=VALUE
shell command makes the variable available for any command run for the
remainder of the shell session in which that command was run. But! You do
not have to export variables in order to use them. You can instead just specify them
in front of each command you run.
For example, I can either run export once, making it so that I do not have to set FLASK_APP ever again for that shell session:
export FLASK_APP=hello.py
flask run
flask run
flask run
Or, I can specify it each time I run flask run
:
FLASK_APP=hello.py flask run
FLASK_APP=hello.py flask run
FLASK_APP=hello.py flask run
The second form may confuse you because the flask
command is no longer the first
entry in the entered command. But that’s allowed. You can set as many KEY=VALUE
pairs as you like before you begin to invoke your command.
Environment variables are useful when you are running your app on different servers:
Consider if you are making an app that connects to a database. Perhaps when you develop locally, your app should use a testing database, but when you
deploy your app, it should use a production database. Rather than hard-coding
different database urls in your code, you can instead write code that reads a
DATABASE_URL
value from the environment. Then you can set that environment
variable differently on various servers.
This method can also be used to keep from hard-coding access credentials into code that you deploy to github and the like.
Some python apps such as python-dotenv
let you write key-value pairs in files that get read by your code. This lets you
avoid having to export
any env vars or specify them each time you want to run
a command. .env
is a common name for such files – hence the package name.
You typically should not commit any .env
files
to source code control – that defeats the purpose! They’re used to specify things
that are specific to where the code is run, or to who runs it. Things that are consistent regardless
of where the code is run or who runs it should perhaps not be env vars.
Now, read this section about how flask uses env vars. Install python-dotenv
into your project, and do the quickstart
again trying some of the ideas. Add notes
to your README addressed to your future self describing how to use your app.
Make some flask JSON routes
Read the About Responses section and the APIs with JSON section to see how you can return JSON from a flask route.
Make a GET-method JSON route
Practice by making a route that returns JSON. Baby steps – first, write a GET route that returns any JSON you like.
Test your flask JSON route
First, read the HTTP Methods section.
If your flask app is running, you can test its routes using anything that can make HTTP requests. You have many options.
- You could use a web browser’s address bar – making http requests is a brower’s primary job! Entering an address in a browser’s address bar makes a GET request.
- You could use https://reqbin.com/ to make a request to your localhost flask server. Sites like this let you set custom headers and use other HTTP methods such as POST.
- You can use python
requests
package to make the request.
For purposes of this lab, write a new python file called test_flask.py
that
uses the python requests
library to make requests against your running
flask app. Add a note to the README about how to use your test script.
Make a POST-method JSON route
Read the Accessing Request Data section to start to get an idea of what the flask.reqeust
object is, and how it stores user-sent data. Also read the The Request Object. Note the note at the bottom
of that section that points to the full documentation on the Requests
object –
follow the link, and ctrl+f to find the get_json
method. Read it.
Now, make a flask route called /predict
that receives data as json and returns a prediction. Return a list of predictions – one prediction for each requested prediction. Some general hints and pointers:
- Load your testing data into your
test_flask.py
script. - use the
.post()
method on therequests
package, demonstrated in the requests quickstart. Use thejson=
keyword to that method to make it so that theapplication/json
content-type header is added to your request – see this section of the requests quickstart for more details. - Load your pickled model in flask. You should load your model at the top of your script – outside of any route. This way, code will run once when the app is launched, and not repeatedly whenever a route is requested.
- Remember from the reading that if you return a
dict
from a flask route, then Flask will call itsjsonify()
method on it and return it as a json response. Not all objects are jsonify-able! Predictions returned from sklearn models are typically of typendarray
. This is not jsonify-able! But regular pythonlist
s are. You can convert a ndarray-type objects to a list using ndarray’s tolist() - Remember from previous labs that sklearn model
predict
functions expect 2d arrays. If the POSTed data is 1d, you can wrap it in a list to make it 2d. - Use the requests library’s
json()
method on your request’s response to get a python object representation of the flask route’s JSON response.
Make your test_flask.py
script print
the returned prediction. Use python
f-strings to print a message that
says: “Good day to you. The prediction for the data is: “ followed by the prediction.
Remember!
- keep your requirements.txt up to date
- keep your README.md up to date (with how to start the flask app, etc)
Deliverable:
Below is a summary list of what was asked for in this lab. Submit a link to a github repo that implements the below.
- A flask app called
app.py
that has a route called/predict
that receives POSTed JSON data.- The route can assume that the POSTed data is in an array, but it should be able to handle either 1d or 2d arrays (one requested prediction or many).
- It should use a pickled model fit against your project data.
- It can also assume that all data is valid data.
- It should return a list having one prediction (positive class only for classification) per requested prediction.
- A python script called
test_flask.py
- It should use the
requests
library - It should print “Good day to you. The prediction for the data is: “ followed by the prediction.
- It should use the
Next lab:
- we will deploy our flask apps to the cloud