DEV Community

loading...
Cover image for Flask - A list of useful “HOW TO’s”

Flask - A list of useful “HOW TO’s”

sm0ke profile image Sm0ke Originally published at blog.appseed.us ・13 min read

Hello Coders,

This article presents a shortlist with useful hints and features that might help developers code faster a Flask project. For newcomers, Flask is a lightweight WSGI web application framework designed to get started quickly and easily, with the ability to scale up to complex applications.


Thanks for reading! Content provided by AppSeed - a platform used by many developers across the globe. TL;DR;


  • 1# - How to code a super simple App in Flask
  • 2# - How to structure your project
  • 3# - How to enable DEBUG
  • 4# - How to set up SQLAlchemy
  • 5# - How to use the Flask CLI
  • 6# - How to migrate a Database
  • 7# - How to define and use a form
  • 8# - How to implement authentication - Flask-Login
  • 9# - How to read POST data
  • 10# - How to redirect in Flask
  • 11# - Logging in Flask
  • 12# - How to return JSON in Flask
  • 13# - How to enable CORS

Ok, let's get back to the real content - the first one is pretty basic and explains how to code a minimal Flask app in less than 1 minute (you need to type fast and have Python3 installed).


1# - Flask, a minimal app

Open a terminal and install Flask using PIP:

$ pip install Flask
Enter fullscreen mode Exit fullscreen mode

Use your preferred editor to create a file called hello.py:

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello():
    return f'Hello from Flask!'
Enter fullscreen mode Exit fullscreen mode

Once the file is saved, let's start the app:

$ env FLASK_APP=hello.py flask run
 * Serving Flask app "hello"
 * Running on http://127.0.0.1:5000/
Enter fullscreen mode Exit fullscreen mode

By visiting the app in the browser we should see "Hello from Flask" message.


2# - How to structure your project

Being such a lightweight framework, Flask comes with great flexibility regarding the codebase structure of a project. The one suggested in this article has been used in many Flask starters generated by the AppSeed platform but we are pretty sure there are many other good configurations published on Github or private blogs.

< PROJECT ROOT >
   |
   |-- app/__init__.py
   |-- app/
   |    |-- static/
   |    |    |-- <css, JS, images>         # CSS files, Javascripts files
   |    |
   |    |-- templates/
   |    |    |
   |    |    |-- includes/                 # Page chunks, components
   |    |    |    |
   |    |    |    |-- navigation.html      # Top bar
   |    |    |    |-- sidebar.html         # Left sidebar
   |    |    |    |-- scripts.html         # JS scripts common to all pages
   |    |    |    |-- footer.html          # The common footer
   |    |    |
   |    |    |-- layouts/                  # App Layouts (the master pages)
   |    |    |    |
   |    |    |    |-- base.html            # Used by common pages like index, UI
   |    |    |    |-- base-fullscreen.html # Used by auth pages (login, register)
   |    |    |
   |    |    |-- accounts/                 # Auth Pages (login, register)
   |    |    |    |
   |    |    |    |-- login.html           # Use layout `base-fullscreen.html`
   |    |    |    |-- register.html        # Use layout `base-fullscreen.html`  
   |    |    |
   |    |  index.html                      # The default page
   |    |  page-404.html                   # Error 404 page (page not found)
   |    |  page-500.html                   # Error 500 page (server error)
   |    |    *.html                        # All other pages provided by the UI Kit
   |
   |-- requirements.txt
   |
   |-- run.py
   |
   |-- ************************************************************************
Enter fullscreen mode Exit fullscreen mode

The relevant files:

  • run.py - the entry point used to start the application requirements.txt - a file that specifies all dependencies (for now is just Flask)
  • app - the application folder where we will add our code
  • app/init.py - This file is required to let us use the app as a Python Package
  • app/static - this folder will contain design assets: JS, CSS, and images.
  • templates - the directory with pages, layouts, and components used by Flask to generate some nice pages for us

Regarding this codebase structure, here is a short list with open-source projects built with a similar files structure:


Flask - Datta Able template provided by AppSeed.


3# - How to enable DEBUG

Sometimes during our development we need to investigate errors in deep by checking the variables values, freeze the application execution and inspect the context or visualize a filtered products list in the user shopping cart. We can achieve this in two ways:

  • using logs and inject print() statements all over the place
  • using a Debugger and gain full control over the app without writing non-functional code in all controllers and helpers.

Flask comes with a Built-in Debugger that allows us to inspect in deep application runtime execution. Flask automatically detects and enable the debugger by scanning the application environment:

$ export FLASK_ENV=development
$ flask run
Enter fullscreen mode Exit fullscreen mode

When running from Python code, the app object accepts the debug variable as an argument:

app.run(debug=True)
Enter fullscreen mode Exit fullscreen mode

At this point, app should run in DEBUG more. Another useful option is to use the debugger toolbar plugin.

Adding an external Debugger Toolbar

This plugin provides useful runtime information like HTTP Headers, configuration, rendered templates, and SqlAlchemy queries executed during requests.

To enable these features is pretty easy. First, we need to install the plugin (via PIP).

$ pip install flask-debugtoolbar
Enter fullscreen mode Exit fullscreen mode

The next step is to use the extension over our Flask app:

from flask import Flask
from flask_debugtoolbar import DebugToolbarExtension

app = Flask(__name__)

# the toolbar is only enabled in debug mode:
app.debug = True

# set a 'SECRET_KEY' to enable the Flask session cookies
app.config['SECRET_KEY'] = '<replace with a secret key>'

toolbar = DebugToolbarExtension(app)
Enter fullscreen mode Exit fullscreen mode

If your product uses app-factory pattern to construct the Flask application, please use this code snippet:

...
# Initialize the Debug Toolbar
toolbar = DebugToolbarExtension()

# Make sure the app has DEBUG enable 
app = create_app()

# Inject the toolbar object in the Flask app object
toolbar.init_app(app)
...
Enter fullscreen mode Exit fullscreen mode

For more information related to Debugging in Flask, please access


4# - How to set up Flask-SQLAlchemy

Flask-SQLAlchemy is a wrapper over the popular SQLAlchemy Python toolkit that empowers developers to manage a database using an object-oriented approach.

The setup required by Flask-SqlAlchemy is minimal:

$ pip3 install flask_sqlalchemy
Enter fullscreen mode Exit fullscreen mode

Once the package is installed, we can use it in the source code:

from flask import Flask

# Import SQLAlchemy
from flask_sqlalchemy import SQLAlchemy 

app = Flask(__name__)

# Define the database
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'

# Inject SQLAlchemy magic 
db = SQLAlchemy(app)

# Sample model handled by SqlAlchemy  
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)

    def __repr__(self):
        return '<User %r>' % self.username
Enter fullscreen mode Exit fullscreen mode

To effectively create User table in the database, we need to use the Flask CLI (see the next section)


More SQLAlchemy resources


5# - How to use the Flask CLI

To use the Flask CLI, FLASK_APP environment variable should point to the application bootstrap script. Once we have this variable present in the working environment, we can launch the Flask console and interact with our application in a different way:

$ export FLASK_APP=app # adjust to match your entry point
$ flask shell
Enter fullscreen mode Exit fullscreen mode

Using the CLI we can create the table defined in the previous section:

$ flask shell
>>> from app import db # 
>>> db.create_all()    # All tables are created
Enter fullscreen mode Exit fullscreen mode

Using the CLI we can select and update users (or any other defined tables) as well:

$ flask shell
>>> from app import User
>>> User.query.all()
[<User u'admin'>, <User u'guest'>]
Enter fullscreen mode Exit fullscreen mode

For more resources, please access:


6# - How to migrate a Database

A migration means our database has changed:

  • New table added
  • The existing table is updated with a new column
  • The type of an existing column has changed. For instance, we have a column that stores 100 characters and we need to extend it to 500.

To safely update the database, we need to "migrate" to the new structure. The Flask plugin that saves us from doing the dirty work is Flask Migrate.

$ pip install Flask-Migrate
Enter fullscreen mode Exit fullscreen mode

To effectively work, Flask-Migrate must be injected in the Flask object:

...
app = Flask(__name__)
...
migrate = Migrate(app, db) # <- The magic line
Enter fullscreen mode Exit fullscreen mode

If we execute the migration for the first time, we should run:

$ # This will create the migrations folder
$ flask db init
Enter fullscreen mode Exit fullscreen mode

Let's update the User model with a new field: role

# Sample model handled by SqlAlchemy  
class User(db.Model):
    id       = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email    = db.Column(db.String(120), unique=True, nullable=False)

    # The new field 
    role     = db.Column(db.Integer, primary_key=False)
Enter fullscreen mode Exit fullscreen mode

To update the database, two steps are required: generate the SQL according to our change and apply it on the database.

Generate the SQL

$ flask db migrate
Enter fullscreen mode Exit fullscreen mode

Update the Database - using the SQL generated in the previous step

$ flask db upgrade
Enter fullscreen mode Exit fullscreen mode

At this point, we can inspect the table to visualize the presence of the role column and use it in our code.


Resources


7# - How to define and use a form

Flask assists us a lot regarding forms via a dedicated library: flask_wtf/FlaskForm. We can easily define, secure, and validate forms using this package.

Here is the definition of a Login form:

from flask_wtf          import FlaskForm
from wtforms            import StringField, PasswordField

class LoginForm(FlaskForm):
    username    = StringField  (u'Username'  , validators=[DataRequired()])
    password    = PasswordField(u'Password'  , validators=[DataRequired()])
Enter fullscreen mode Exit fullscreen mode

As mentioned, FlaskForm comes with re-defined types and validators for the password, mandatory fields check, email, and much more.

The view (aka the page) that use it:

<form role="form" method="post" action="">
    {{ form.username(placeholder="Username") }}
    {{ form.password(placeholder="Password",type="password") }}
</form>
Enter fullscreen mode Exit fullscreen mode

For newcomers, a simplified CSRF mechanism is presented below:

  • Server generates the CRSF Token
  • Form is built and sent to the client browser along with the token
  • The user fills and submits the form
  • The server receives a new POST request and check the CSRF token authenticity
  • If the token is not validated request is rejected with a 403 (not authorized) status

More resources


8# - How to authenticate using Flask-Login

Flask-Login provides user session management for Flask. It handles the common tasks of logging in, logging out, and remembering users’ sessions.

Let's highlight the code to integrate Flask-Login in a real application.

Update your User model to inherit the UserMixin class. This is necessary because Flask expects a minimum set of handlers to be implemented by the user model: is_authenticated, is_active, get_id. To skip over this work, UserMixin class saves the day for us.

from app         import db
from flask_login import UserMixin

# User model uses UserMixin as parent class 
class User(UserMixin, db.Model):

    id       = db.Column(db.Integer,     primary_key=True)
    user     = db.Column(db.String(64),  unique = True)
    email    = db.Column(db.String(120), unique = True)
    password = db.Column(db.String(500))
Enter fullscreen mode Exit fullscreen mode

The next step is to define user_loader a method used by Login-Manager to identify the current authenticated user information: id, email and all other fields provided by the User class.

...
# provide login manager with load_user callback
@lm.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))
...
Enter fullscreen mode Exit fullscreen mode

Once we have the User table updated in the database, we can successfully login.

The login view (aka login page):

...
<form method="post" action="" class="mt-4">
    {{ form.hidden_tag() }}
    {{ form.username(class="form-control") }}
    {{ form.password(class="form-control", type="password") }}

    <button type="submit" name="login">Sign in</button>
</form> 
Enter fullscreen mode Exit fullscreen mode

Once the user fills username and the password, all information is sent to the server via a POST request.

Login - The controller code

# Authenticate user
@app.route('/login.html', methods=['GET', 'POST'])
def login():

    # Declare the login form
    form = LoginForm(request.form)

    # The information returned to the user
    msg = "Your cool!"

    # Apply all validations defined in the form
    if form.validate_on_submit():

        # recover form information
        username = request.form.get('username', '', type=str)
        password = request.form.get('password', '', type=str) 

        # recover the user from database using username (unique information)
        user = User.query.filter_by(user=username).first()

        # If the user is found, validate the password
        if user:

            # BC = BCrypt library 
            if bc.check_password_hash(user.password, password):
                # The user is now authenticated
                login_user(user)
            else:
                msg = "Wrong password. Please try again."
        else:
            msg = "Unknown user"

    return render_template( 'accounts/login.html', form=form, msg=msg )
Enter fullscreen mode Exit fullscreen mode

The controller flow explained:

  • Local variable form is initialized using the request form (sent by the user)
  • If the form is valid (user and password are present) the code extracts the values
  • Select the user from the database using the name
  • Check the password by comparing the database value with the one provided by the user and hashed by the BCrypt library.
  • If all is good, the user session is created via "login_user()" handler
  • Error cases are flagged with a message to inform the user what was wrong.

Flask authentication resources:


Flask - Pixel UI Template, provided by AppSeed.


9# - How to read POST data

For newcomers, POST and GET are different methods used to submit information to a server. During a GET request, the variables are appended to the page URL:

GET/login.html?user=Biden&pass=MAGA2020 
Enter fullscreen mode Exit fullscreen mode

POST requests uses the request body to bundle the information submitted by the user.

In both cases, we can easily read POST and GET parameters with Flask via the request object. Here is a list of request objects that we can refer to extract the information:

  • request.args: the key/value pairs in the URL query string
  • request.form: the key/value pairs in the body, from a HTML post form
  • request.json: parsed JSON data

A real sample - used to handle registration flow

@app.route('/register.html', methods=['GET', 'POST'])
def register():

    # declare the Registration Form
    form = RegisterForm(request.form)

    # request is GET - just display the form
    if request.method == 'GET': 

        return render_template( 'accounts/register.html', form=form, msg=msg )

    # Here we have a POST request
    if form.validate_on_submit():

        # READ information from a POST request
        username = request.form.get('username', '', type=str)
        password = request.form.get('password', '', type=str) 
        email    = request.form.get('email'   , '', type=str) 
Enter fullscreen mode Exit fullscreen mode

As mentioned above, the composite request.form provides the form fields in key/value pairs bundled in the request body.


More information on this topic:


10# - How to redirect in Flask

This is a simple one. Flask exposes a "redirect" handler to keep us lazy:


import os
from flask import Flask,redirect

app = Flask(__name__)

@app.route('/')
def not_me():

    # redirect to another page
    return redirect("/register")

    # redirect to external page
    return redirect("https://google.com")

    # redirect with HTTP code
    return redirect("https://google.com", code=301)
Enter fullscreen mode Exit fullscreen mode

Resources:


11# - Logging in Flask

Flask uses the native Python logging system (no configuration required). To log a message during a request, we need to call the logger interface and set the message we want to be visible in logs:

from flask import Flask
import logging

app = Flask(__name__)

logging.basicConfig(level=logging.DEBUG)

@app.route('/')
def hello():
    app.logger.info('Logging is up!')
    return 'Hello World!'

if __name__ == '__main__':
    app.run()
Enter fullscreen mode Exit fullscreen mode

By running the app, we should see the logging message in the console:

* Serving Flask app "app.py"
* Environment: development
INFO:werkzeug: * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
INFO:flask.app:Logging is up
INFO:werkzeug:127.0.0.1 - - [01/Jun/2019 17:03:09] "GET / HTTP/1.1" 200 -
Enter fullscreen mode Exit fullscreen mode

Logs might be useful when we want to trace runtime errors in production or save other relevant events in the logs for the application owner.

The above sample will push the log to the console but we can easily push messages to a file:

from flask import Flask
import logging

app = Flask(__name__)

logging.basicConfig(filename='app.log', level=logging.DEBUG)

@app.route('/')
def hello():
    app.logger.info('Logging is up!')
    return 'Hello World!'

if __name__ == '__main__':
    app.run()
Enter fullscreen mode Exit fullscreen mode

After a first request, a new file named "app.log" should be created with our log message as content.


Flask logging resources


12# - How to return JSON

Flask comes with native support for JSON management via jsonify interface. Let's take a look at this self-descriptive sample:

from flask import jsonify

@app.route('/_get_current_user')
def get_json():
    return jsonify(
        message="Hello",
        from="from JSON"
    )
Enter fullscreen mode Exit fullscreen mode

The output returned in the browser should be as below:

{
    "message": "Hello",
    "from": "from JSON"
}
Enter fullscreen mode Exit fullscreen mode

Something useful: let's return a registered user using JSON format:

from flask import jsonify

@app.route('/user_json')
def get_json():

    user = User.query.filter_by(username='testme').first()

    # Using the user object (without __dict__) will generate a runtime error 
    # TypeError: 'user' is not JSON serializable
    return jsonify( user.__dict__ )
Enter fullscreen mode Exit fullscreen mode

The response should contain all fields defined in the User table:

{
    "id": "1",
    "username": "testme",
    "email":"test@google.com"
}
Enter fullscreen mode Exit fullscreen mode

More resources:


13# - How to enable CORS in Flask

This feature is useful when our Flask backend is consumed by decoupled entities like a mobile UI interface, external systems that push or request the information provided by the Flask backend.

As usual, Flask has a module for this flask-cors.

$ Install the module via PIP
$ pip install -U flask-cors
Enter fullscreen mode Exit fullscreen mode

Let's take a look at the sample provided in the official docs:

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

@app.route("/")
def helloWorld():
  return "Hello, cross-origin-world!"
Enter fullscreen mode Exit fullscreen mode

We can also isolate CORS for specific paths like /api/*:

app = Flask(__name__)
cors = CORS(app, resources={r"/api/*": {"origins": "*"}})

@app.route("/api/v1/users")
def list_users():
  return "user example"
Enter fullscreen mode Exit fullscreen mode

CORS Resources


Thank you! For more resources, please access


Thank you! Btw, my (nick) name is Sm0ke and I'm pretty active also on Twitter.

Discussion

pic
Editor guide
Collapse
dabjazz profile image
Yash_Jaiswal

Thank you for providing an overall summary of basic functionality used in web app implemented by flask. It will be a lot helpful if you could make an indetail article for signup and login functionality

Collapse
sm0ke profile image
Sm0ke Author

Noted! Ty for reading!

Collapse
mreading2020 profile image
mreading2020

Flask is awesome.

Collapse
mreading2020 profile image
mreading2020

Especially for beginners.

Collapse
dendihandian profile image
Dendi Handian

Wow, what an article. I am on learning Flask for everything in web development. Thank you for sharing.

Collapse
sm0ke profile image
Sm0ke Author

Glad you like it! More will come.
Flask is such a great piece of software.

Collapse
crearesite profile image
Collapse
sm0ke profile image
Collapse
uithemes profile image
ui-themes

Thank you! Really nice content.

Collapse
sm0ke profile image
Collapse
stokry profile image
Stokry

This is awesome man! Thank you!

Collapse
sm0ke profile image
Collapse
arnabsen1729 profile image
Arnab Sen

Wow. Really helpful. Appreciate your efforts.