forms old - pkirlin/lab-flask GitHub Wiki
So far we have no way to get any input from the user. We can't use Python's input() function because that is tied
to the keyboard or standard input. What we will do is use HTML forms to display fields that the user can type into.
There are a number of complications that will come into play here. The first one deals with the way web browsers and web
servers exchange information. This is done through what are called "requests" or "transactions." When a web browser connects
to a web server, it makes a "request" of that server by saying something like "Give me the webpage at URL /time". Then
the web server sends the HTML code of that webpage back to the browser (or HTML code for an error message) and the browser
displays it. If the browser reloads the page, or visits another page, that will generate a second request and a second
response.
Normally, when we use HTML forms, we will need two complete rounds of request-response to generate something useful.
The reason is that the first request the web browser makes will be for the server to send back the HTML code with the (blank) form. Then the user fills out the form, presses the Submit button, and then a second request is made to the server, including the contents of the form, and a second response is generated, this time usually by processing the form
and sending back some kind of webpage indicating the result.
This is analogous to going to Rhodes Express and filling out paperwork there. First you walk up to the desk and ask for a specific form (Request #1). The form is handed back to you, blank (Response #1). You then fill out the form and hand it in (Request #2). The person behind the desk then response in an appropriate way, for instance, by saying "We will process this form ASAP," or "This form is filled out incorrectly" (Response #2).
The second complication is that HTTP (Hypertext Transfer Protocol), the set of standards that web browsers and web servers use to talk to each other, is stateless. That means that by default, when your web browser makes multiple requests of a web server, there is no way to maintain a set of variables automatically between the first and second request-response cycles. It's like having a conversation with a person where you have to constantly remind them the subject that you are talking about.
There are a number of technologies that have been developed to get around this problem (cookies is the best known one), and we will see how to use them later. For now, just remember that every time you want to send information to the web server, it will have forgotten any previous information you sent.
We will see how to create a form that lets the user generate random numbers between 1 and a number they choose.
First we will create the HTML template. Make a new HTML template or edit your old one for random.html:
<html>
<head>
<title>Random</title>
</head>
<body>
<h1>Random numbers are fun!</h1>
{% if number is not defined %}
<form action="{{ url_for('pick_number') }}" method="post">
Pick an upper limit: <input type="text" name="upperlim">
<input type="submit" value="Generate Number">
</form>
{% else %}
Your random number is: {{number}}
{% endif %}
</body>
</html>Edit your Python code. First change the Flask import line to
from flask import Flask, render_template, request
Next, change the random route:
@app.route("/random", methods=['get', 'post'])
def pick_number():
if "upperlim" not in request.form:
return render_template("random.html")
else:
upper = int(request.form['upperlim'])
n = random.randint(1, upper + 1)
return render_template("random.html", number=n)Visit http://localhost:5000/random and give it a try!
Before we talk about that, we need to talk about...
When a browser makes a request to a web server, this is usually done via either a "GET" request or a "POST" request. The original reasons for choosing these words are not as relevant as they used to be. The two methods differ in how the browser sends information in an HTML form to a web server (if no HTML form information is being sent from the browser to the server, the distinction is irrelevant).
In a GET request, the HTML form information is encoded as part of the URL. If you have ever glanced at a URL and seen
something like http://www.server.com/page.html?name=Snoopy&type=dog&friend=CharlieBrown, that happens when the user submits
a form via GET with three HTML form variables, name, type, and friend.
GET requests can become problematic in a number of ways. Some web servers limit the length that a URL can be, so it can be dangerous to submit the results of a long HTML form via GET, because the URL might get truncated. Furthermore, because all data in the form is passed via the URL without any encryption or encoding, this is a very poor method for submitting any kind of sensitive information, like passwords or credit card numbers.
In a POST request, the information in the HTML form is submitted separately from the URL. The information is still part of the same request as the URL, but arrives separately. This has advantages and disadvantages from the GET method. The advantages are that there is typically no limit on the length of data submitted through POST, so no truncating will ever happen if there is a lot of data to submit (for example, uploading a file). Furthermore, the data isn't part of the URL, so sensitive information isn't shown in the browser's address bar (it is still not encrypted, however, so it's not that much more secure). The disadvantage of POST is that the results of a POST request can't be bookmarked in a browser, because a bookmark only stores a specific URL.
All you should remember for this tutorial is that the default HTTP method is GET, but we will use POST to submit forms.
Let's break this down in the order that the browser and server talk to each other.
- When you visit
http://localhost:5000/random, Python runspick_number(), because that's the function connected to the/randomroute. - [Python:] Inside
pick_number(), if a form has been submitted, then there is a special variable calledrequest.formthat holds all the information the user typed into the form. However, the form hasn't been submitted yet, so this variable is not defined, so therandom.htmltemplate is rendered with no additional variables. - [HTML:] Now the template starts generating. Notice the template now has an
ifstatement! Yes, this is possible! The syntax is similar to Python, but slightly different. - Since no additional information was sent to the template, there is no variable called
number, so the if statement is true, so the first part of theifis generated, and the second part is skipped. - The
actionparameter for the HTML form specifies the URL that should receive the information in the form. In this case, we use the special functionurl_forto automatically generate the URL connected to thepick_numberfunction, which in this case, is/random. You should always useurl_forto generate these URLs, this way you can easily change the route to a different name if desired later. - The HTML from the template contains a form that is presented to the user. The user fills in the value for the field and clicks Submit.
- A second request to the server begins, again with the route
/random. However, in this second request, we are submitting via POST, and we are submitting form data. Specifically, each item in the form is turned into a variable/value pair that will appear in therequest.formvariable in Python. Notice that in the HTML form, the input field has a specifiesname="upperlim". That means,request.form(a Python dictionary) will contain an entry form the keyupperlim. - [Python:] We run
pick_random()again. This time, however, there is a key calledupperlimin therequest.formdictionary, so we skip to the else part of the code. We create a Python variable calledupper(again, there is no rule that Python variables have to have the same names as the form variables), and cast it to an int. Notice this is just like sayingint(input(...))in Python when we want the user to type something in and it must be an integer. - [Python:] We generate a random integer
nin Python and callrender_template, passing the value ofnas the value of the keyword argumentnumber. - [HTML:] The
random.htmltemplate starts generating again. This time theifstatement is false, becausenumberis defined. So we run theelsepart and print out our random number.
Whew! This was a lot of work to follow. But hopefully it makes sense.
Flask does not do input validation, just as Python doesn't (at least not automatically). Try submitting the form with a blank entry, or with text instead of numbers, or a decimal point. You will get the same ValueError: invalid literal for int() with base 10 error you probably remember from CS141.
Edit your HTML template and the Python code so the user can pick the upper and lower bounds on the random number. If you get that, try making it so the page that prints the random number also prints the lower and upper bounds the user put into the form.
Check your answer below when you're done.
.
.
.
.
.
.
.
. .
.
.
.
. .
.
.
.
.
.
.
. .
.
.
.
.
.
.
. .
.
.
.
.
.
.
. .
.
.
.
.
.
.
. .
.
.
.
.
.
.
. .
.
.
.
.
.
.
. .
.
.
.
.
.
.
. .
.
.
.
.
.
.
. .
.
.
.
.
.
.
. .
.
.
.
.
.
.
. .
.
.
.
.
.
.
.
<html>
<head>
<title>Random</title>
</head>
<body>
<h1>Random numbers are fun!</h1>
{% if number is not defined %}
<form action="{{ url_for('pick_number') }}" method="post">
Pick a lower limit: <input type="text" name="lowerlim"><br>
Pick an upper limit: <input type="text" name="upperlim"><br>
<input type="submit" value="Generate Number">
</form>
{% else %}
Your random number is: {{number}}.<br>
As a reminder, your desired range was [{{low}}, {{high}}].
{% endif %}
</body>
</html>from flask import Flask, render_template, request
import random
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!'
@app.route("/random", methods=['get', 'post'])
def pick_number():
if "upperlim" not in request.form:
return render_template("random.html")
else:
upper = int(request.form['upperlim'])
lower = int(request.form['lowerlim'])
n = random.randint(lower, upper + 1)
return render_template("random.html", number=n, low=lower, high=upper)