-
Notifications
You must be signed in to change notification settings - Fork 4
forms
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 (not that anyone does this anymore).
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 to the person behind the desk (Request #2). The person behind the desk then responds 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 of the subject that you are talking about.
There are a number of technologies that have been developed to get around this problem (web browser 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 two limits of their own choosing (rather than always between 1 and 10). Very rudimentary, of course, but we have to start somewhere.
First we will create the HTML template. Make a new HTML template called random2.html:
<html>
<head>
<title>Random numbers</title>
</head>
<body>
<h1>Random number generator</h1>
{% if number is not defined %}
<form action="{{ url_for('random_number2') }}" method="post">
Enter 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, add a new /random_number2 route and corresponding random_number2() function:
@app.route("/random_number2", methods=['get', 'post'])
def random_number2():
if "upperlim" not in request.form:
return render_template("random2.html")
else:
upper = int(request.form['upperlim'])
n = random.randint(1, upper)
return render_template("random2.html", number=n)Visit the new URL /random_number2 and give it a try!
NOTE: I had trouble getting this to work in the replit mini-browser. The only way it fully worked was to load the webpage in a separate browser tab, which you can do by clicking the green {...} area of the URL in the mini-browser, then clicking the "Dev URL," then adding /random_number2 to the URL it takes you to.
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 the names GET and PUT 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 the URL
/random_number2, Python runsrandom_number2(), because that's the function connected to the/random_number2route. - [Python:] Inside
random_number2(), 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 therandom2.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 theifstatement 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 therandom_number2()function, which in this case, is/random_number2. 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 the button.
- A second request to the server begins, again with the route
/random_number2. 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 an attribute that specifiesname="upperlim". That means,request.form(a Python dictionary) will contain an entry with the keyupperlim. - [Python:] We run
random_number2()again. This time, however, there is a key calledupperlimin therequest.formdictionary, so we skip to theelsepart 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, but it will appear in the console area in replit, not in the web browser.
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.
Remember to stop and re-run flask (big green button) every time you change the Python code.
Check your answer below when you're done.
.
.
.
.
.
.
.
. .
.
.
.
. .
.
.
.
.
.
.
. .
.
.
.
.
.
.
. .
.
.
.
.
.
.
. .
.
.
.
.
.
.
. .
.
.
.
.
.
.
. .
.
.
.
.
.
.
. .
.
.
.
.
.
.
. .
.
.
.
.
.
.
. .
.
.
.
.
.
.
. .
.
.
.
.
.
.
. .
.
.
.
.
.
.
. random2.html:
<html>
<head>
<title>Random numbers</title>
</head>
<body>
<h1>Random number generator</h1>
{% if number is not defined %}
<form action="{{ url_for('random_number2') }}" method="post">
Enter an lower limit: <input type="text" name="lowerlim"><br>
Enter an lower limit: <input type="text" name="upperlim"><br>
<input type="submit" value="Generate number">
</form>
{% else %}
Your random number is {{ number }}.
{% endif %}
</body>
</html>main.py:
@app.route("/random_number2", methods=['get', 'post'])
def random_number2():
if "upperlim" not in request.form:
return render_template("random2.html")
else:
lower = int(request.form['lowerlim'])
upper = int(request.form['upperlim'])
n = random.randint(lower, upper)
return render_template("random2.html", number=n)Use the main wiki page to navigate, not the list of pages directly above, because those are out of order.