Post

Python Flask intro

Create venv

Create a new folder

1
2
$ mkdir demo_flask_board
$ cd demo_flask_board

Create a venv environment

On Windows

1
2
python -m venv venv
.\venv\Scripts\activate

On Linux

1
2
3
$ python3 -m venv venv
$ source venv/bin/activate
(venv) $

Example:

1
2
3
gus@ubuntu-vm:~/projects/demo_flask_board$ python3 -m venv venv
gus@ubuntu-vm:~/projects/demo_flask_board$ source venv/bin/activate
(venv) gus@ubuntu-vm:~/projects/demo_flask_board$ 

Install Python Flask using pip

1
python3 -m pip install Flask
Output
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
(venv) gus@ubuntu-vm:~/projects/demo_flask_board$ python3 -m pip install Flask
Collecting Flask
  Downloading flask-3.0.3-py3-none-any.whl (101 kB)
     |████████████████████████████████| 101 kB 4.9 MB/s 
Collecting itsdangerous>=2.1.2
  Downloading itsdangerous-2.2.0-py3-none-any.whl (16 kB)
Collecting Jinja2>=3.1.2
  Downloading jinja2-3.1.4-py3-none-any.whl (133 kB)
     |████████████████████████████████| 133 kB 17.2 MB/s 
Collecting click>=8.1.3
  Downloading click-8.1.7-py3-none-any.whl (97 kB)
     |████████████████████████████████| 97 kB 9.2 MB/s 
Collecting importlib-metadata>=3.6.0; python_version < "3.10"
  Downloading importlib_metadata-8.0.0-py3-none-any.whl (24 kB)
Collecting blinker>=1.6.2
  Downloading blinker-1.8.2-py3-none-any.whl (9.5 kB)
Collecting Werkzeug>=3.0.0
  Downloading werkzeug-3.0.3-py3-none-any.whl (227 kB)
     |████████████████████████████████| 227 kB 8.1 MB/s 
Collecting MarkupSafe>=2.0
  Downloading MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (26 kB)
Collecting zipp>=0.5
  Downloading zipp-3.19.2-py3-none-any.whl (9.0 kB)
Installing collected packages: itsdangerous, MarkupSafe, Jinja2, click, zipp, importlib-metadata, blinker, Werkzeug, Flask
Successfully installed Flask-3.0.3 Jinja2-3.1.4 MarkupSafe-2.1.5 Werkzeug-3.0.3 blinker-1.8.2 click-8.1.7 importlib-metadata-8.0.0 itsdangerous-2.2.0 zipp-3.19.2
(venv) gus@ubuntu-vm:~/projects/demo_flask_board$


Run Flask

Create file app.py

1
2
3
4
5
6
7
8
9
10
from flask import Flask

app = Flask(__name__)

@app.route("/")
def home():
    return "Hello, World!"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8000, debug=True)

Run with:

1
(venv) $ python app.py
Output
1
2
3
4
5
6
7
8
9
10
11
(venv) gus@ubuntu-vm:~/projects/demo_flask_board$ python app.py
 * Serving Flask app 'app'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:8000
 * Running on http://192.168.0.42:8000
Press CTRL+C to quit
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 362-633-512


Run Flask (using Blueprints)

Transform the Project Into a Package

1
2
(venv) $ mkdir board
(venv) $ mv app.py board/__init__.py

The result is that board/__init__.py is exactly the same code as app.py from the previous section.

Directory structure:

1
2
3
4
demo_flask_board/
│
└── board/
    └── __init__.py

Run with:

1
(venv) $ python -m flask --app board run --port 8000 --debug
Output
1
2
3
4
5
6
7
8
9
(venv) gus@ubuntu-vm:~/projects/demo_flask_board$ python -m flask --app board run --port 8000 --debug
 * Serving Flask app 'board'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:8000
Press CTRL+C to quit
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 848-111-764


Application factory design pattern

With an application factory, the project’s structure becomes more organized. It encourages you to separate different parts of your application, like routes, configurations, and initializations, into different files later on. This encourages a cleaner and more maintainable codebase.

board/__init__.py

1
2
3
4
5
6
from flask import Flask

def create_app():
    app = Flask(__name__)

    return app

Using Blueprints

Blueprints are modules that contain related views that can be conveniently imported in __init__.py. For example, you’ll have a blueprint that stores the main pages of your project.

board/pages.py

1
2
3
4
5
6
7
8
9
10
11
from flask import Blueprint

bp = Blueprint("pages", __name__)

@bp.route("/")
def home():
    return "Hello, Home!"

@bp.route("/about")
def about():
    return "Hello, About!"

board/__init__.py

1
2
3
4
5
6
7
8
9
from flask import Flask

from board import pages

def create_app():
    app = Flask(__name__)

    app.register_blueprint(pages.bp)
    return app

Templates

Project structure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(venv) gus@ubuntu-vm:~/projects/demo_flask_board$ tree -I venv
.
└── board
    ├── __init__.py
    ├── pages.py
    ├── static
    │   └── styles.css
    └── templates
        ├── base.html
        ├── _navigation.html
        └── pages
            ├── about.html
            └── home.html

(venv) gus@ubuntu-vm:~/projects/demo_flask_board$ 

board/static/styles.css

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
* {
  box-sizing: border-box;
}

body {
  font-family: sans-serif;
  font-size: 20px;
  margin: 0 auto;
  text-align: center;
}

a,
a:visited {
  color: #007bff;
}

a:hover {
  color: #0056b3;
}

nav ul {
  list-style-type: none;
  padding: 0;
}

nav ul li {
  display: inline;
  margin: 0 5px;
}

main {
  width: 80%;
  margin: 0 auto;
}

board/templates/_navigation.html

1
2
3
4
5
6
<nav>
  <ul>
    <li><a href="{{ url_for('pages.home') }}">Home</a></li>
    <li><a href="{{ url_for('pages.about') }}">About</a></li>
  </ul>
</nav>

board/templates/base.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html lang="en">
<head>
  <title>Message Board - {% block title %}{% endblock title %}</title>
  <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
</head>
<body>
<h1>Message Board</h1>
{% include("_navigation.html") %}
<section>
  <header>
    {% block header %}{% endblock header %}
  </header>
  <main>
    {% block content %}<p>No messages.</p>{% endblock content %}
  </main>
</section>
</body>
</html>

board/templates/pages/home.html

1
2
3
4
5
6
7
8
9
10
11
{% extends 'base.html' %}

{% block header %}
  <h2>{% block title %}Home{% endblock title %}</h2>
{% endblock header %}

{% block content %}
  <p>
    Learn more about this project by visiting the <a href="{{ url_for('pages.about') }}">About page</a>.
  </p>
{% endblock content %}

board/templates/pages/about.html

1
2
3
4
5
6
7
8
9
{% extends 'base.html' %}

{% block header %}
  <h2>{% block title %}About{% endblock title %}</h2>
{% endblock header %}

{% block content %}
  <p>This is a message board for friendly messages.</p>
{% endblock content %}

board/pages.py

1
2
3
4
5
6
7
8
9
10
11
from flask import Blueprint, render_template

bp = Blueprint("pages", __name__)

@bp.route("/")
def home():
    return render_template("pages/home.html")

@bp.route("/about")
def about():
    return render_template("pages/about.html")

Run with:

1
python -m flask --app board run --port 8000 --debug
Output
1
2
3
4
5
6
7
8
9
(venv) gus@ubuntu-vm:~/projects/demo_flask_board$ python -m flask --app board run --port 8000 --debug
 * Serving Flask app 'board'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:8000
Press CTRL+C to quit
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 848-111-764


References

  • https://realpython.com/flask-project/
This post is licensed under CC BY 4.0 by the author.