Welcome!
Everything is fine.

Flask Part 3b

Last time, we went over the people_app and tried to understand all of it, before we go on to newer topics. Today, we'll make double-sure that the people_app is clear, and then we'll go on to those topics.

Goal

By the end of today, you will be able to use redirects and flashing in Flask applications. You'll be completely prepared for H6 Lookup and almost prepared for H7 CRUD.

Plan

  1. Announcements:
  2. your questions
  3. Forms in Flask: the request object
  4. redirects
  5. flashing
  6. Breakout: form to specify the birth month for search

Announcements

  • Happy International Women's Day!
  • I'll grade P2 (design and plan) and H4 (ER) ASAP. Don't wait for me
  • I made two mistakes on the Quiz: Q3 had the wrong answer designated as the correct one, and Q7 had zero points. Almost everyone got both questions right. I'm sorry for my errors.
  • Most people feeling better about People App, though there are still a lot of people who are undecided or didn't respond. How can I help?

Starting

To start our work for today, make sure you activate your virtual environment:

source ~/cs304/venv/bin/activate 

Breakout Rooms

Copying the People App

If you didn't do so last week, now is the time to make sure you have a copy of the People app.

cd ~/cs304/
cp -r ~cs304/pub/downloads/people_app people_app
cd people_app

Running the People App

Run the app:

python app.py

VS Code should start up an SSH tunnel automatically, so you should be able to just click on the link.

I'll demo and you'll try this too:

Quiz questions

This is a good time to answer your quiz questions.

Today's Examples

Copy the flask3 folder from the course account to your own, and you can re-create all of today's examples:

cd ~/cs304/ 
cp -r ~cs304/pub/downloads/flask3 flask3

Guide: That folder has these files. We'll do what we can, leaving some for tomorrow.

  • all_examples.py defines the app object, and imports the examples. So, we run it, but read the code in other files.
  • example_request_method.py is our first form example, showing different request methods.
  • example_form_processing_one_route.py is our second form example, showing GET to get an empty form and POST to use it.
  • example_post_redirect_get.py is our third form example, showing POST that redirects to a GET route to do the real work.
  • example_flashing.py is an example of flashing to report errors in an easy way.

TO DO

I will run the all_examples.py app in the guest account. You're welcome to do the same with your copy. We'll look at the code using VSC.

Example: request_method (sqrt)

As you know, forms are critically important in Web applications. Our first example is example_request_method.py, which also refers to templates/sqrt_form.html and templates/msg.html.

<!doctype html>
<html lang='en'>
<head>
    <meta charset='utf-8'>
    <title>{{title}}</title>
</head>
<body>

<h2>GET</h2>
<form method="GET" action="{{processor}}">
    <p><label>input number
            <input name="num" type="number" step="any">
    </label></p>
    <p><input type="submit" value="GET"></p>
</form>

<h2>POST</h2>
<form method="POST" action="{{processor}}">
    <p><label>input number
            <input name="num" type="number" step="any">
    </label></p>
    <p><input type="submit" value="POST"></p>
</form>

<h2>Menu</h2>
<form method="GET" action="{{processor}}">
    <p><label>select number
            <select name="num">
                <option value="2">two</option>
                <option value="3">three</option>
                <option value="3.1416">pi</option>
                <option value="4">four</option>
            </select>
        </label></p>
    <p><input type="submit" value="submit"></p>
</form>

</body>
</html>

Notice:

  • There can be more than one form on a page
  • They can have different methods. Note the difference in how the data is sent.
  • Notice the different behavior on re-submission. (Click the reload button.)
  • the ACTION needs to be filled in. Typically I put this in the template, rather than in the route.
  • How to use a SELECT menu
  • We could use url_for(...) in the template, where we have {{processor}} if it'll always go to the same place, which is typical. I separated them to make the Python code clearer, but it's usually better to put the url_for() in the template.

Here is the Flask code to process it:

from flask import (Flask, url_for, render_template, request,
                   redirect, flash)
import math
from all_examples import app

@app.route('/sqrt_form/')
def sqrt_form():
    # This route just returns the form.
    return render_template('sqrt_form.html',
                           processor=url_for('compute_sqrt'))

@app.route('/compute_sqrt/', methods=['GET','POST'])
def compute_sqrt():
    # the request object is a new import from flask
    if request.method == 'GET':
        num = request.args.get('num')
    else:
        num = request.form.get('num')
    try:
        x = float(num)
        y = math.sqrt(x)
        return render_template(
            'msg.html',
            msg=('Square root of {x} is {y}'
                 .format(x=x,y=y)))
    except:
        return render_template(
            'msg.html',
            msg=('Error computing square root of {num}'
                 .format(num=num)))
  • The request symbol needs to be imported
  • request.method is either GET or POST
  • request.args goes with GET
  • request.form goes with POST
  • Look at the URL with the different options

Redirects

A redirect is a short response to the browser telling it to make a different request. You can say

A redirect is when the server asks the browser to request a different URL

A redirect is when the server asks the browser to request a different URL

Redirects can be used for a variety of reasons.

  • To clean up a URL using query parameters, converting:
    • /find?q=123 to
    • /name/nm123
  • To transfer someone to a more appropriate page:
    • /login?name=fred&pass=ok
    • /user/fred

You don't want to over-use redirects. It should be more about what the user sees rather than convenient coding.

As with render_template() you return the redirect:

return redirect( url_for('index') ) 

Also, you have to import the redirect function; see below.

Flashing

A web application should always let the user understand what's going on:

  • Confirmation of updates
  • Show search criteria as well as search results
  • Appropriate error messages

You can do this by inserting information on the page:

    {% if errmsg %} 

    <p>{{ errmsg }}</p> 

    {% endif %} 

A more general solution is to have a collection of messages that you can iterate over to show them all. Like this:

{% with messages = get_flashed_messages() %} 
    {% if messages %} 
        <div id="messages"> 
        {% for msg in messages %} 
            <p>{{msg}}</p> 
        {% endfor %} 
        </div> 
    {% endif %} 
{% endwith %} 

How you format the messages, of course, is up to you. Here, we've used a bunch of paragraphs in a DIV. Style it with CSS.

To add a message to the list, use the flash() function:

flash('actor inserted') 

You have to remember to import the flash function into your app. See below.

Flask uses a special kind of cookie to implement sessions and flashing, thereby allowing the flashed messages to go across re-directs. We'll discuss the implementation later. However, it's important to know that you have to set a secret key. For security reasons, this should be unguessable. Set it like this (globally in your app).

from flask import (Flask, render_template, make_response, 
                   request, redirect, url_for, flash) 
app = Flask(__name__) 

app.secret_key = 'replace this string' 

In the all_examples.py file, we generate a random secret key each time.

Example of Flashing

The example_flashing.py file demonstrates flashing.

Here are the Flask routes:

from flask import (Flask, url_for, render_template, request,
                   redirect, flash)
import math
from all_examples import app

@app.route('/log2/', methods=['GET','POST'])
def log2():
    if request.method == 'GET':
        # this gets the blank form by visiting the URL
        return render_template('log2.html')
    else:
        if request.form.get('num') is None:
            flash('No "num" in the request')
            return render_template('log2.html')
        num = request.form.get('num')
        try:
            x = float(num)
        except:
            flash('Could not convert {} to float'.format(num))
            return render_template('log2.html')
        if x == 0:
            flash('Definitely cannot compute log of zero.')
            return render_template('log2.html')
        if x < 0:
            flash('''Cannot compute log of negative;
                     using absolute value instead''')
            x = abs(x)
        # finally!
        y = math.log(x,2.0)
        return render_template('log2.html',x=x,y=y)

Breakout Exploration

Spend some time exploring the examples. Here's how to run them:

cd flask3
python all_examples.py

Discussion/experiments:

  • change the URL for the the route in ...one_route.py from /ln/ to /natural-log/. What other things have to change?
  • add a city in the post_redirect_get example.
  • should we use POST for this kind of form?

Breakout Exercise: People Born in Month

Breakout Rooms

Your flask3 folder has a subfolder pa1 which has a copy of the people_app with a few extras in it.

Your goal is to add a page with a form that allows the user to request a list by the birthmonth they specify:

a form to ask for the birth month

You'll also process that form input and respond with the correct list

Part 1 the Form

  1. add a route that sends the empty form to the user. This only needs to be a simple route that renders the template and returns it.
  2. test that; you'll have to type in the URL
  3. add a hyperlink to the main page that goes to the correct URL
  4. test that

Part 2 Processing the Form

  1. add a route that will process the submitted form. It's just vacous for now. Return a boring page, like hello_world
  2. modify the form to send the data to that route
  3. modify the route to print the submitted form input to the console. You'll have to import the request object.
  4. test that
  5. convert the value to an int
  6. Replace the vacuous response with a real response. You can copy/paste the code from earlier in the file (though copy/paste is usually a bad sign). We'll fix this soon.
  7. test that

Part 3 Redirect

  1. Instead of the copy/pasted code, return a redirect to the correct route You'll have to import redirect
  2. test that

Part 4 Flashing

  1. Import flash from Flask
  2. set the secret_key attribute of the app object.
  3. flash the same message as you printed
  4. add the flashed messages incantation to your templates. See below
  5. test this

The flash incantation is something like this. You can modify the HTML, such as using OL and LI instead of DIV and P.

{% with messages = get_flashed_messages() %}
    {% if messages %}
        <div id="messages">
        {% for msg in messages %}
            <p>{{msg}}</p>
        {% endfor %}
        </div>
    {% endif %}
{% endwith %}

Summary

You know about

  • forms
  • request methods
  • redirects
  • flashing