1. Flask

1.1. Introduction

In this chapter, we will learn the basics of Flask framework. More specifically, we will see the URLs, HTML templates, Forms and Sessions in Flask. Below are the required libraries for this tutorial,

Flask==0.12.2
Flask-WTF==0.11
Jinja2==2.7.3
MarkupSafe==0.23
WTForms==2.0.2
Werkzeug==0.10.1
itsdangerous==0.24

1.2. Hello World

  • Below is the code for which writes the “Hello World!” on the browser. Read comments for more details,
# app.py

from flask import Flask

app = Flask(__name__) # application 'app' is object of class 'Flask'

# decorator 'app.route' binds the 'url' with 'function',
# i.e. url of 'home page (/)' will call function 'index' and
# 'return' value will be send back to the browser.
@app.route('/')  # root : main page
def index():
    return '<p>Hello World!</p>'

if __name__ == '__main__':
    app.run(debug=True) # local webserver : app.run()
  • Run the code as below,
$ python app.py
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
[...]
  • Now, open browser and go to link ‘http://127.0.0.1:5000/’ and Hello World! will be displayed on the browser.

1.3. Render HTML

  • In the below code, the ‘index.html’ file is called by the function ‘index’ (Line 13).
  • Also, we can define the port number for localhost as shown in Line 18.

Note

By default, ‘render_template’ looks inside the folder ‘template’

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# app.py

from flask import Flask, render_template

app = Flask(__name__) # application 'app' is object of class 'Flask'

# decorator 'app.route' binds the 'url' with 'function',
# i.e. url of 'home page (/)' will call function 'index' and
# 'return' value will be send back to the browser.
@app.route('/')  # root : main page
def index():
    # by default, 'render_template' looks inside the folder 'template'
    return render_template('index.html')

if __name__ == '__main__':
    # '0.0.0.0' = 127.0.0.1 i.e. localhost
    # port = 5000 : we can modify it for localhost
    app.run(host='0.0.0.0', port=5000, debug=True) # local webserver : app.run()
  • Below is the content of ‘index.html’ file,
<!-- index.html -->

<!DOCTYPE html>
<html>
<head>
    <title>Home</title>
</head>
<body>
    <h1>Hello World!</h1>
</body>
</html>
  • Fig. 1.1 is the output of above codes,
../_images/flask1.png

Fig. 1.1 Hello World!

1.4. Variables in HTML

We can use the variables in the HTML with the help of Jinja2-templates; and the pass the value of variable from the ‘python code’,

  • Below is the HTML code, where the variable ‘sub’ is used at Line 10. Note that variables are written betwee {{ }}.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<!-- subject.html -->

<!DOCTYPE html>
<html>
<head>
    <title>Subject</title>
</head>
<body>
    <h1> Subject selection </h1>
    <p> Recommended subject : <b> {{sub}} </b></p>
</body>
</html>
  • Below is the Python code where the value for the variable ‘sub’ is passed through Line 19. Further, the value of the ‘id’ is provided by the ‘url’ e.g. if url is ‘subjects/1’, then ‘id’ will be set to ‘1’ by Line 17.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# app.py

from flask import Flask, render_template

app = Flask(__name__) # application 'app' is object of class 'Flask'

subjects = ["English", "Dance", "Maths", "Geography"]

# decorator 'app.route' binds the 'url' with 'function',
# i.e. url of 'home page (/)' will call function 'index' and
# 'return' value will be send back to the browser.
@app.route('/')  # root : main page
def index():
    # by default, 'render_template' looks inside the folder 'template'
    return render_template('index.html')

@app.route('/subject/<int:id>')
def subject(id):
    return render_template('subject.html', sub="subjects[id]")


if __name__ == '__main__':
    # '0.0.0.0' = 127.0.0.1 i.e. localhost
    # port = 5000 : we can modify it for localhost
    app.run(host='0.0.0.0', port=5000, debug=True) # local webserver : app.run()
Subject selection

Recommended subject : Dance

1.6. Forms

In previous section, we created links to go from one page to another. In this section, we will add a form to get the input from the user; and based on input-value, the page will go to next page.

  • In the below code, the form is added at Lines 13-19, whose output is shown in Fig. 1.2.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!-- interest.html -->

<!DOCTYPE html>
<html>
<head>
    <title>Interest</title>
</head>
<body>
    <h1> Select your interest? </h1>

    <p> {{interest}} </p>

    <form method="post">
        <p>
            <input type="radio" name="choice" value="yes"> Yes <br>
            <input type="radio" name="choice" value="no"> No <br>
        </p>
        <input type="submit" value="Submit">
    </form>

    <p><a href="{{ url_for('index')}}"> Begin again </a></p>

</body>
</html>
../_images/flask2.png

Fig. 1.2 Form

  • Below is the Python codes, which goes to ‘interest-id’ page (Lines 26-28), if choice is ‘no’ otherwise remains as the same page.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# app.py

from flask import Flask, render_template, request, redirect, url_for

app = Flask(__name__) # application 'app' is object of class 'Flask'

subjects = ["English", "Dance", "Maths", "Geography"]
interests = ["Writing", "Dancing", "Logics", "Earth"]


# decorator 'app.route' binds the 'url' with 'function',
# i.e. url of 'home page (/)' will call function 'index' and
# 'return' value will be send back to the browser.
@app.route('/')  # root : main page
def index():
    # by default, 'render_template' looks inside the folder 'template'
    return render_template('index.html')

@app.route('/subject/<int:id>')
def subject(id):
    return render_template('subject.html', sub=subjects[id])


@app.route('/interest/<int:id>', methods=['GET', 'POST'])
def interest(id):
    if request.method == 'POST':
        if request.form['choice'] == 'no':
            return redirect(url_for('interest', id=id+1))
    return render_template('interest.html', interest=interests[id])

if __name__ == '__main__':
    # '0.0.0.0' = 127.0.0.1 i.e. localhost
    # port = 5000 : we can modify it for localhost
    app.run(host='0.0.0.0', port=5000, debug=True) # local webserver : app.run()

1.7. More about Forms

In previous section, we have created a basic form which does not have any validation/security methods in it. In this section, we will use some built in methods for creating the form,

  • Line 8 is added for avoiding the CSRF attack.
  • Line 13-16 creates a class ‘SelectChoiceForm’ which adds the various choices for the Form.
  • Line 34 creates an object of class SelectChoiceForm.
  • Line 35 validates the submitted data.
  • Lines 38-39 redirects the url to ‘subject.html’ page, if the choice is yes; otherwise Line 37 redirects the page to next interest-option.
  • Lastly, the the object ‘form’ is send to html page through Line 40.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# app.py

from flask import Flask, render_template, request, redirect, url_for
from flask_wtf import Form
from wtforms.fields import RadioField, SubmitField

app = Flask(__name__) # application 'app' is object of class 'Flask'
app.config['SECRET_KEY'] = 'yourKeyHere!' # to avoid CSRF attack

subjects = ["English", "Dance", "Maths", "Geography"]
interests = ["Writing", "Dancing", "Logics", "Earth"]


class SelectChoiceForm(Form):
    choice = RadioField('Select Yes/No', choices=[('yes','Yes'), ('no','No')])
    submit = SubmitField('Submit')

# decorator 'app.route' binds the 'url' with 'function',
# i.e. url of 'home page (/)' will call function 'index' and
# 'return' value will be send back to the browser.
@app.route('/')  # root : main page
def index():
    # by default, 'render_template' looks inside the folder 'template'
    return render_template('index.html')

@app.route('/subject/<int:id>')
def subject(id):
    return render_template('subject.html', sub=subjects[id])


@app.route('/interest/<int:id>', methods=['GET', 'POST'])
def interest(id):
    form = SelectChoiceForm()
    if form.validate_on_submit():
    # if request.method == 'POST':
        if request.form['choice'] == 'no':
            return redirect(url_for('interest', id=id+1))
        else:
            return redirect(url_for('subject', id=id))
    return render_template('interest.html', interest=interests[id], form=form)

if __name__ == '__main__':
    # '0.0.0.0' = 127.0.0.1 i.e. localhost
    # port = 5000 : we can modify it for localhost
    app.run(host='0.0.0.0', port=5000, debug=True) # local webserver : app.run()

In the below code, the form is created in the html using the object ‘form’ (Lines 15-22) which is sent by ‘app.py’,

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!-- interest.html -->

<!DOCTYPE html>
<html>
<head>
    <title>Interest</title>
</head>
<body>
    <h1> Select your interest? </h1>

    <p> {{interest}} </p>

    <form method="post">

        {{ form.hidden_tag() }}
        <p>
            {% for ch in form.choice %}
                {{ ch }} {{ch.label}}<br>
            {% endfor %}
        </p>

        {{ form.submit }}

        <!-- <p>
            <input type="radio" name="choice" value="yes"> Yes <br>
            <input type="radio" name="choice" value="no"> No <br>
        </p>
        <input type="submit" value="Submit">  -->
    </form>

    <p><a href="{{ url_for('index')}}"> Begin again </a></p>

</body>
</html>

Lastly in ‘subject.html’, a link is added to go to ‘index.html’,

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<!-- subject.html -->

<!DOCTYPE html>
<html>
<head>
    <title>Subject</title>
</head>
<body>
    <h1> Subject selection </h1>
    <p> Recommended subject : <b> {{ sub }} </b></p>

    <p>Thank you! Click <a href="{{ url_for('index')}}"> here </a> to try again</p>
</body>
</html>

Below are the outputs of different pages,

1.  http://0.0.0.0:5000/

        Tell your interest!

        Please select one option as 'yes' from the questions ...

        Start

2. Click on 'start', the page will go to 'http://0.0.0.0:5000/interest/0'

        Select your interest?

        Writing

        Yes
        No

        Submit

        Begin again

3. Select 'No' and press submit, the page will go to 'http://0.0.0.0:5000/interest/1'

        Select your interest?

        Dancing

        Yes
        No

        Submit

        Begin again

4. Select 'Yes' and press submit. The page will go to 'http://0.0.0.0:5000/subject/1'

         Subject selection

        Recommended subject : Dance

        Thank you! Click here to try again

5. Click on 'here' to start again.

Warning

Since there are only 4 iterest-choices, therefore if we press ‘No’ four times, then following error will be generated,

IndexError: list index out of range

1.8. Sessions

In previous section, we vaildated the forms; and then ‘interest’ is asked from the user. If the choice is ‘No’, then next interest was shown. However, we can jump over to any interest by providing the correct ‘id’, i.e. in the below link, just provide the correct integer value i.e. 0, 1, 2 or 3 (as we have only 4 questions).

http://0.0.0.0:5000/interest/1'

Now, want that user should read all questions one by one i.e. we do not want that user can jump to any question just by changing the ‘id’. This can be done using ‘Sessions’, as shown in below code,

  • Line 23 creates a new session ‘my_interest’ with initial value 0.
  • In Line 32, we removed the ‘int:id’ option from the URL, therefore user can not jump to any question by providing the ‘id’.
  • Next, the value of ‘id’ is provided by the session variable at Line 34.
  • The value of ‘id’ is incremented at Line 39 (if the choice is ‘No’).
  • Lastly, the ‘id’ is removed from the ‘redirect’ URL as well (see Line 40).
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# app.py

from flask import Flask, render_template, request, redirect, url_for, session
from flask_wtf import Form
from wtforms.fields import RadioField, SubmitField

app = Flask(__name__) # application 'app' is object of class 'Flask'
app.config['SECRET_KEY'] = 'yourKeyHere!' # to avoid CSRF attack

subjects = ["English", "Dance", "Maths", "Geography"]
interests = ["Writing", "Dancing", "Logics", "Earth"]

class SelectChoiceForm(Form): # define elements of form e.g. RadioField
    # choices : (name for python, display on HTML)
    choice = RadioField('Select Yes/No', choices=[('yes','Yes'), ('no','No')])
    submit = SubmitField('Submit')

# decorator 'app.route' binds the 'url' with 'function',
# i.e. url of 'home page (/)' will call function 'index' and
# 'return' value will be send back to the browser.
@app.route('/')  # root : main page
def index():
    session['my_interest'] = 0
    # by default, 'render_template' looks inside the folder 'template'
    return render_template('index.html')

@app.route('/subject/<int:id>')
def subject(id):
    return render_template('subject.html', sub=subjects[id])


@app.route('/interest', methods=['GET', 'POST'])
def interest():
    id = session['my_interest']
    form = SelectChoiceForm()
    if form.validate_on_submit():
    # if request.method == 'POST':
        if request.form['choice'] == 'no':
            session['my_interest'] = id+1
            return redirect(url_for('interest'))
        else:
            return redirect(url_for('subject', id=id))
    return render_template('interest.html', interest=interests[id], form=form)

if __name__ == '__main__':
    # '0.0.0.0' = 127.0.0.1 i.e. localhost
    # port = 5000 : we can modify it for localhost
    app.run(host='0.0.0.0', port=5000, debug=True) # local webserver : app.run()

Now run the code again and observe following,

  • First go to ‘Main Page’ i.e. ‘http://0.0.0.0:5000/’ and click on ‘Start’.

  • Now, select ‘No’ from the option list, and next page question (i.e. Dancing ) will be displayed. But, URL will not have any id i.e. ‘http://0.0.0.0:5000/interest

  • Now, open a new tab in the browser (do not close the browser), and close the previous tab.

  • Type the ‘interest’ URL in new tab i.e. ‘http://0.0.0.0:5000/interest’. This will start the page from where we left i.e. second question Dancing, which is read by the session from the cookies.

  • Next, close the browser and type type the interest link (‘http://0.0.0.0:5000/interest’) again. The error KeyError: ‘my_interest’ will be displayed as ‘session’ is lost after closing the browser.

  • Finally, go to main page (http://0.0.0.0:5000/) to start again.