Deploying a predictive python ML model with Flask and Heroku: Part 2
In this post, we will create a form to get user inputs that will later be fed into our model.
In Part 1, we created a repo, a virtual environment, installed flask and gunicorn and set up a starter app.py file that looks like this:
from flask ipmort Flask
app = Flask(__name__)@app.route('/', methods=['GET'])
def test():
return 'Hello World'
We are able to view ‘Hello World’ on our localhost when we execute this command:
flask run
Now, we have our app working in its most basic form. Next, let’s add two jinja2-html templates. How do we name these? Jinja2 does not have a default file extension so we will just use .html as our file extension. You can use a different one, for example .jinja. Let’s also create a folder called templates to store them. In the root of your app, create the templates directory then move into that to create a main.html and res.html file.
mkdir templates
cd templates
touch main.html res.html
What is the purpose of the jinja-html files?
The main.html file will be displayed when someone navigates to the app’s URL. It will include a form. The user will complete this form and submit it to be run through the model. The res.html file will display the model’s output. For example, if you were predicting whether a person was affiliated with the Republican or Democratic party, you would display the model’s prediction of “Republican” or “Democratic” on res.html. This model output is passed to the file as a jinja variable which is the reason we are using a jinja2-html template rather than basic HTML.
How do we create our HTML5 form with fields for user responses?
Open main.html. Start by declaring the doctype and setting up the scaffolding. Add an HTML5 template like this:
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title></head><body></body></html>
If you are using VSCode as your text editor, you can generate this template by typing html:5 on the first line of the blank file and then clicking enter. Alternatively, you can manually type it.
Within the body tags, add form tags. Within the form tags, add fieldset tags to contain a group of fields. Our form will have two fieldset tag pairs. The first will house the inputs related to demographic information and the second will house inputs related to brand preferences.
Here’s the code that I added within the body tags for the Demographic Info fieldset:
<form><fieldset><legend>Demographic Info</legend><label for='age'>Age:</label><input type='number' name='age' required><p>Select one:</p><label for='female'>Female</label><input type='radio' name='sex' value='female'><label for='male'>Male</label><input type='radio' name='sex' value='male'></fieldset></form>
Let’s walk through it. Within the form tags, I have opening and closing fieldset tags. Within the fieldset tags, I have a legend that has the title of the container. Since these questions are about demographics, I have named it Demographic Info. Next, I have label tags for age followed by an input tag. After that, there is a p tag which allows me to add instructions to the user followed by two pairs of label and input tags.
The functionality of the form is created by the input tags. When we specify the attribute type equal to the string number, we are creating a field for an integer input. The usability of this form for humans is created by the label tags and the p tags. For our app to work, we don’t need those bits but if we don’t include them, the forms is going to be unusable by people.
In our app.py file, we will write a line of code to grab the value of the inputs by calling the name attribute. The first input type is number so the value that will be passed into our model (or into a data cleaning function prior to being passed into our model) is the number a user enters into that field. It is different for the radio buttons. For the radio buttons, if a user clicks the first button with the label Female, the value that will be passed to our model is currently ‘female’ because the value attribute is ‘female.’ Alternatively we could change it to 1 or 0 which might save us a few steps in data cleaning. Let’s do that. See below that I have set the value of female to 1 and the value of male to 0.
<form><fieldset><legend>Demographic Info</legend><label for='age'>Age:</label><input type='number' name='age' required><p>Select one:</p><label for='female'>Female</label><input type='radio' name='sex' value='1'><label for='male'>Male</label><input type='radio' name='sex' value='0'></fieldset></form>
Great, let’s create our second fieldset. This will ask the user about their brand preferences. I have added the second fieldset after the first fieldset so you are able to see the entire form:
<form><fieldset><legend>Demographic Info</legend><label for='age'>Age:</label><input type='number' name='age'><p>Select one:</p><label for='female'>Female</label><input type='radio' name='sex' value='female'><label for='male'>Male</label><input type='radio' name='sex' value='male'></fieldset><fieldset><legend> Brand Preferences</legend><label for='cheese'>Favorite brand of cheese:</label><input type='text' name='cheese'><br><p>Which brand(s) of soy milk would you buy?</p><input type='checkbox' name='milk1' value='tj'>Trader Joe's<br><input type='checkbox' name='milk2' value='silk'>Silk<br><input type='checkbox' name='milk3' value='zen'>Zensoy<br><input type='checkbox' name='milk4' value='wf'>Whole Foods 365<br></fieldset></form>
We used a text box for our question about cheese and a set of checkbox options (users can select more than one) for our question about soy milk. We have now covered four input types so you have a good number to select from in creating your form. You can also check other resources like w3schools to learn about other input types. Quick note — the br tags are for line breaks and not needed.
Now that we’ve created our jinja-html template with our user input fields, what’s next?
We need a way for the user to submit this information to us. Before we create a submit button that triggers a POST request, we won’t be able to do anything with the user’s answers as we don’t have access to them yet!
Add this code right before the closing tag of the form:
<button type='submit' value='Submit' method='POST'>Submit</button>
The method attribute is POST which will trigger the post request that will pass these inputs to our app Flask object.
Oh, also go ahead and change the text within the title tags in your head to name the page something other than “Document”:
<title>My Survey</title>
You can view this file in your browser to see if you’re happy with how it looks.
Next, we will define a function in app.py that gets the user responses from this survey and passes them to our data science model! Here’s part 3.