4

Structuring a Flask Project

 3 years ago
source link: http://www.patricksoftwareblog.com/structuring-a-flask-project/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Introduction

Flask provides such great flexibility when developing a web application, including the structure of the application. Flask applications can range from a single file to larger projects with multiple Blueprints. In this blog post, I’m going to describe how I like to structure a Flask application using Blueprints and an Application Factory function.

For reference, the project that I created as part of writing this blog post is available on GitLab: Flask User Management Example.

Blueprints

Blueprints allow you to cleanly organize the source code of your Flask application. Each Blueprint encapsulates a significant piece of functionality in your application. For example, I like to have a separate Blueprint for handling the user management of a Flask application.

I like to create a separate package (ie. a directory that includes the __init__.py file) for each Blueprint. For example, here is the structure of the Flask User Management example that contains two Blueprints (recipes and users):

(venv) $ tree -L 5
.
├── README.md
├── instance
├── main.py
├── project
│   ├── __init__.py
│   ├── models.py
│   ├── recipes
│   │   ├── __init__.py
│   │   ├── routes.py
│   │   └── templates
│   │       └── recipes
│   │           └── index.html
│   ├── templates
│   │   └── base.html
│   └── users
│       ├── __init__.py
│       ├── forms.py
│       ├── routes.py
│       └── templates
│           └── users
│               ├── login.html
│               ├── profile.html
│               └── register.html
├── requirements.txt
└── venv
├── bin
│   ├── activate
|   ├── ...

Taking a look at a single Blueprint (users), it encapsulates all of components to handle the user management aspects of the application (defining routes, handling forms, generating templates):

users
├── __init__.py
├── forms.py
├── routes.py
└── templates
└── users
├── login.html
├── profile.html
└── register.html

The subfolders for storing the template files may look strange, but this structure (…/blueprint_name/templates/blueprint_name/) is recommended in the Flask documentation. The template directory for each Blueprint gets added to the search path for template files within your Flask application. With this structure, you can define the templates for each Blueprint within the Blueprint, but still import a base template (in …/project/templates/) that gets shared by all the Blueprints. With this structure, the template file for a Blueprint can be accessed using ‘users/login.html’.

In order to create the Blueprint, an object of the Blueprint class gets instantiated in the __init__.py file of the Blueprint folder:

"""
The users Blueprint handles the user management for this application.
Specifically, this Blueprint allows for new users to register and for
users to log in and to log out of the application.
"""
from flask import Blueprint
users_blueprint = Blueprint('users', __name__, template_folder='templates')
from . import routes

This instantiation of the Blueprint specifies the name of the Blueprint (users) and it specifies the location of the template files within the Blueprint. Additionally, the last line imports the routes created in routes.py.

The users_blueprint that was created in __init__.py is then imported in the routes.py file:

#################
#### imports ####
#################
from . import users_blueprint

This import allows the routes that are being created in routes.py to be specified with ‘@users_blueprint’:

################
#### routes ####
################
@users_blueprint.route('/')
def index():
return render_template('index.html')

After creating a Blueprint (and registering it), you can check that the routes are being created successfully by checking the mapping of the URLs to specific functions. The best way to see this is to bring up the Python interpreter with the key Flask components loaded using flask shell:

(venv) $ flask shell
>>> print(app.url_map)
Map([<Rule '/register' (OPTIONS, HEAD, GET, POST) -> users.register>,
<Rule '/profile' (OPTIONS, HEAD, GET) -> users.profile>,
<Rule '/logout' (OPTIONS, HEAD, GET) -> users.logout>,
<Rule '/login' (OPTIONS, HEAD, GET, POST) -> users.login>,
<Rule '/' (OPTIONS, HEAD, GET) -> recipes.index>,
<Rule '/static/<filename>' (OPTIONS, HEAD, GET) -> static>])

This shows that the URLs are being mapped correctly to the functions in the Blueprints.

Application Factory

The initialization of a Flask project requires the following steps:

  • Creating the Flask application as an instance of the Flask class
  • Configuring the Flask application
  • Initializing the extensions to be used
  • Registering the Blueprints in the project

These steps can be put together in a single function called an Application Factory function (an implementation of the Factory method pattern). In addition to making it clear what steps need to be taken to initialize a Flask application, the Application Factory function allows different Flask applications to be created with different configurations. This flexibility becomes very useful during testing, where the configuration can often be different from development or production.

The Application Factory function is created in the …/project/__init__.py file. To start, import all of the modules (including the Flask extensions) that are needed for the Flask project:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from flask_bcrypt import Bcrypt

Next, create the instances of each of the Flask extensions without initializing them:

#######################
#### Configuration ####
#######################
# Create the instances of the Flask extensions (flask-sqlalchemy, flask-login, etc.) in
# the global scope, but without any arguments passed in.  These instances are not attached
# to the application at this point.
db = SQLAlchemy()
bcrypt = Bcrypt()
login = LoginManager()
login.login_view = "users.login"

Next, the actual Application Factory function (create_app) is defined:

######################################
#### Application Factory Function ####
######################################
def create_app(config_filename=None):
app = Flask(__name__, instance_relative_config=True)
app.config.from_pyfile(config_filename)
initialize_extensions(app)
register_blueprints(app)
return app

The function to initialize each Flask extension passes in the Flask application to each extension:

def initialize_extensions(app):
# Since the application instance is now created, pass it to each Flask
# extension instance to bind it to the Flask application instance (app)
db.init_app(app)
bcrypt.init_app(app)
login.init_app(app)
...

Finally, all of the Blueprints are registered with the Flask application:

def register_blueprints(app):
# Since the application instance is now created, register each Blueprint
# with the Flask application instance (app)
from project.recipes import recipes_blueprint
from project.users import users_blueprint
app.register_blueprint(recipes_blueprint)
app.register_blueprint(users_blueprint)

After creating the Application Factory function, it gets used in the …/main.py file:

from project import create_app
# Call the Application Factory function to construct a Flask application instance
# using the standard configuration defined in /instance/flask.cfg
app = create_app('flask.cfg')

Now the Flask application is ready to run using:

(venv) $ export FLASK_APP=main.py
(venv) $ flask run
* Serving Flask app "main"
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Overall Structure

The overall structure for this project is shown below:

(venv) $ tree -L 3
.
├── README.md
├── instance
├── main.py
├── project
│   ├── __init__.py
│   ├── models.py
│   ├── recipes
│   │   ├── __init__.py
│   │   ├── routes.py
│   │   └── templates
│   ├── templates
│   │   └── base.html
│   └── users
│       ├── __init__.py
│       ├── forms.py
│       ├── routes.py
│       └── templates
├── requirements.txt
└── venv
├── bin
├── ...

The Blueprints are defined in separate modules in the …/project folder. The Application Factory function is defined in the …/project/__init__.py file. The actual Flask application is then created in …/main.py.

Conclusion

This blog post described how to use Blueprints and an Application Factory function to help structure a Flask project. Blueprints provide a great tool to organize your project into major pieces of functionality. An Application Factory function provides a clean method for creating the Flask application. Put together, these are the two primary ideas that I use to structure a Flask project.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK