abstractkitchen.com logo

Flask Boilerplate and Your Guide to Flask in 2022. With SQLAlchemy.

Posted by Dmitry

Updated on

python flask boilerplate guide jinja postgresql sqlalchemy flask-backbone
Flask Boilerplate and Your Guide to Flask in 2022. With SQLAlchemy.

Preface

I enjoy working with Flask and made lots of websites with this framework. I can build anything I want, and I'm grateful for the freedom it gives me as a developer. Flask-Backbone is my take to create an initial structure and a set of rules, so most of my flask projects are easy to support. I already use it when developing new projects.

The other reason why I started Flask-Backbone is that I understand how it can be difficult sometimes for new developers to dive into new technology. Flask for a newbie is a kind of double sword. It's easy to start, but when you need to grow bigger you end up with a bunch of smelly code.

In Flask-Backbone I used patterns from the official documentation, so you won't have any troubles making your customizations and growing from this point.

You can think of Flask-Backbone as an IKEA manual. It's a boilerplate. You can learn how things work in Flask and you can also create big projects without any limitations. I use it every day at my work.

Note: Although Flask is an awesome tool, for API services I would suggest Falcon, FastApi or other API-centric frameworks. Remember that being a software engineer means choosing the right tools. Don't worry, if you decided to use Flask to build API-service you'll be fine as well.

Flask-Backbone is boilerplate, but I've added a few tools for interactivity. I also decided not to publish most of my utils to keep things simple. I will probably publish them later as separate libs.

I18N. I removed support for localization. It's a more complex topic and will be covered in a separate article.

While this article explains some terminology about Flask, it can't replace Flask documentation. If you're a newbie you'll need to read about some Flask ideas along the way, so keep Flask documentation around.

Flask App Boilerplate. Features

  • Predefined basic structure, so you'll end up with a clean architecture.
  • Database support via SQLAlchemy. However, you can skip database setup and use Flask-Backbone without the database. Personally, I do not use Flask-SQLAlchemy, but you can.
  • Alembic for your database migrations.
  • Development/Production/You own configs with instance_relative_config.
  • Cache support via flask_caching. Setup easily with configuration.
  • Flask-Debug
  • Sentry support. Just add your DSN, and you're good to go.
  • Jinja filters and custom variables.
  • Designed to be blueprint-first. Keep your structure clean and steady with blueprints. Everything is a blueprint. Your future self will thank you.
  • Interactive commands to create your next blueprint. Define your blueprint skeletons to speed up your development. To create your next blueprint simply run flask app create-blueprint. It's up to you and you can completely ignore or remove this part and everything will work perfectly fine.
  • Initial setup with a configuration script.
  • uWSGI config

Flask Folder Structure

├── app/
│   ├── blueprints/
│   ├── commands/
│   ├── enums/
│   ├── errors/
│   ├── ext/
│   ├── models/
│   ├── static/
│   ├── templates/
│   ├── utils/
│   ├── jinja
│   │   ├── __init__.py
│   │   ├── context_processor.py
│   │   └── filters.py
│   ├── app.py
├── config/
│   ├── __init__.py
│   ├── default.py
│   ├── development.py
│   ├── production.py
├── instance/
│   └── config.py
├── migrations/
├── configure.py
├── requirements.txt
├── webservice.ini
├── alembic.ini
└── wsgi.py

app/blueprints. This is where your blueprints live. More about this in the blueprints section of this article.

app/commands. Application wide commands. For example, here I placed scripts to manipulate blueprints.

app/enums. Place here your application wide python enums. It won't be covered in this article, and it's totally up to you how to use enums.

app/ext. Application extensions. You'll find here SQLAlchemy, Sentry, Flask-Caching. You can think of it as a some sort of abstraction. Configure here external libraries and invoke them from this place anywhere in your app.

app/errors. Register here your application error-handlers. For example 404. Inside you'll find a function register_error_handlers.

app/jinja. Jinja configuration.

app/jinja/__init__.py. Jinja mapper definition.

app/jinja/context_processor.py. Jinja context processor. Set here any keys that you want to pass to templates.

app/jinja/filters.py. Jinja custom filters.

app/models. Application-wide models. Other models must be stored in the blueprints.

app/static. Shared static files.

app/templates. Shared templates. Usually I place here main layout and error pages.

app/app.py. This is main flask file. Your application central point.

config. Place here your configurations. Do not store here any sensitive information, since this files will be in git. By default, Flask-Backbone ships with default.py, development.py, and production.py.

instance/config.py. This is where you store your sensitive configurations, like API-keys, database URI, etc. This file must be added in .gitignore.

migrations. Alembic migrations. If you don’t have a database you can remove this folder.

configure.py. Just a small utility for your initial setup. This script will ask your some questions and will create config files.

requirements.txt. Your app requirements.

webservice.ini. uWSGI config for your deploy.

alembic.ini. Alembic configuration file. This file is created if

wsgi.py. This is your entry point to deploy application.

Getting Started. Installation

First. Clone flask-backbone.

git clone https://github.com/abstractkitchen/flask-backbone.git .

Next. As a rule of thumb, make sure that you use a virtual environment. For example python3 -m venv pythonenv. This will create an environment in the folder pythonenv. I prefer to name my python environment as a pythonenv, because it's more descriptive.

python3 -m venv pythonenv
. pythonenv/bin/activate
pip install -r requirements.txt

Launch configure.py. It will ask you some question about your future setup.

python configure.py

This utility will create: .env, alembic.ini and instance/config.py.

Note: if you're using port other than 5000, then don't forget to update your SERVER_NAME in the config/development.py.

Flask-Backbone initial configuration

And finally, launch your application.

flask run

How to Import CSV Files to PostgreSQL with Pgfutter

csv postgresql

Sometimes I need to upload large CSV files to PostgreSQL. CSV file might have hundreds of columns, that's why i want a tool that can do some magic for me.

Configuration Basics

Flask-Backbone configuration files

Configuration is based on python files. I used to have configuration based on ini files aka "configuring from data files", but it's not sustainable long-term and introduces a lot of limitations. Using python configuration files gives you more freedom, and I feel like it's more pythonic.

In the config folder, you will find three files: default.py, development.py, and production.py. These files are made to be in the repository. They contain general configuration and must not contain any sensitive information about your project. You can create as many configuration files as you want. Environment variable APP_CONFIG value is the name of the config file. For example, if you set APP_CONFIG=test, then config/test.py will be used. By default, it's a development.

Where to store your secrets? instance/config.py is made specifically for this. Don't forget that this file must not be shared in git. This is where you put SECRET_KEY and SQLALCHEMY_DATABASE_URI.

default.py. Your standard config. Any values from this file can be redefined in the development.py or your custom config file.

How does configuration implemented?

This is code from app.py. Go ahead and customize for your needs.

def init_configuration(app):
    # Load the default configuration
    app.config.from_object('config.default')

    # Load the configuration from the instance folder
    app.config.from_pyfile('config.py')

    # Variables defined here will override those in the default configuration
    app.config.from_object(f"config.{os.environ.get('APP_CONFIG')}")

Access config in the application

Use Flask application context for this purpose which is accessed via current_app proxy.

from flask import current_app


@blueprint.route("/", methods=["get"])
def index_route():
    print(current_app.config.get("IMAGES_CDN_URL"))

Default URL Redirect

By default, the application is set to redirect from example.com/url/ to example.com/url. If you want to change or remove this behavior, then go to app.py and change this code.

@app.before_request
def app_before_request():
    request_path = request.path

    # redirect: example.com/url/ -> example.com/url
    if request_path != '/' and request_path.endswith('/'):
        return redirect(request_path[:-1], 301)

Blueprints

A Blueprint is a way to organize a group of related views and other code. Rather than registering views and other code directly with an application, they are registered with a blueprint. Then the blueprint is registered with the application when it is available in the factory function.
from Flask documentation

Let's take a look at our app/blueprints folder structure. Home for your blueprints.

app/blueprints/
├── __boilerplate__/
├── index_page/
│   ├── __init__.py
│   ├── routes.py
│   ├── commands/
│   ├── enums/
│   ├── models/
│   ├── templates/
│   ├── utils/
│   └── views/
├── __init__.py
└── utils.py

__boilerplate__. I will cover this part later, but this is the place where blueprint templates are stored. Command flask app create-blueprint use this folder as a boilerplate. You can also create your own set of blueprints and put them here.

__init__.py. Blueprints central point. All blueprints are connected here to your app instance. Check the source code for the explanation. You'll find the function register_blueprints(app) which is used to automatically connect your blueprints. You can customize register_blueprints to do this manually.

utils.py. Blueprints technical utilities.

index_page. Blueprint folder. In this case, it's a blueprint for the index page.

index_page/routes.py. Blueprint routes like @blueprint.route("/", methods=["get"])

index_page/views. Define your route functions from routes.py here. If you have simple routes you can place them in the routes.py and skip views.

index_page/commands. Blueprint-related commands. In most cases, you won't need them. This part will be explained later in this article.

index_page/enums. Blueprint-related python enums.

index_page/models. Blueprint-related models.

index_page/templates. Blueprint-related templates.

index_page/utils. Blueprint-related utils. Any small functions that is used across your blueprint.

Create Blueprint

Flask-Backbone. Create Blueprint

To create your next blueprint simply use flask custom command:

flask app create-blueprint

The script will ask you some questions about your new boilerplate and as a result, you'll have a new blueprint structure. From this point you'll just need to open files and edit according to your needs.

What is a view style?

View style is based on Flask patterns:

Standard (url rule decorator). This is referred to Basic Reusable View. In your routes.py you'll have standard blueprint route decorator.

flask.views.View. This option will create two files: routes.py and views/{blueprint_name}.py. If you're familiar with Flask you understand that this is related to View Class pattern. Your route view will be placed to the views folder.

flask.views.MethodView. Similar to flask.views.View, but MethodView.

Don't want to use script? No problem. Do it on your own, but there are a few rules that you must follow:

  • Your blueprint must have a routes.py file.
  • Inside of your routes.py, you must expose the blueprint variable.

That's it! You're good to go. However, for consistency I suggest you keep the blueprint structure as defined in app/blueprints/__boilerplate__/skeletons/standard.

⚠️ By default, all blueprints are initialized automatically, based on the app/blueprints folder structure. It's easy to change. Check app/blueprints/__init__.py for the detailed explanation.

Working With The Database

Flask-Backbone uses SQLAlchemy as ORM and Alembic for migrations. To use a database, don't forget to set your SQLALCHEMY_DATABASE_URI in instance/config.py. If you remember, you've been asked about this in the first step.

SQLALCHEMY_DATABASE_URI = "postgresql://user:[email protected]:5432/test_db"

SQLAlchemy adapter is influenced and slightly based on Flask-SQLAlchemy. Don't forget to explore app/ext/sqlalchemy. If you want to switch to Flask-SQLAlchemy it will be easy for you.

app/ext/sqlalchemy/
├── __init__.py
├── base_query.py
├── database.py
├── model.py
├── pagination.py
└── utils.py

base_query.py. Extends default SQLAlchemy Query. Not much to see, but if you want to enhance your Query — do it here.

database.py. Central point for your database. Function init_database is used in the app.py and db_session is your session pointer.

model.py. Base model for your future models.

Don't forget to check source code in the folder — you'll find more information that is not covered here.

Your First Model

No need to set table name explicitly. It will be converted from class name. ContinentModel -> continent. ContinentCountryModel -> continent_country.

from sqlalchemy import Column, String

from app.ext.sqlalchemy.model import BaseModel, id_column


class ContinentModel(BaseModel):
    id = id_column()
    slug = Column(String)
    code = Column(String(2))

Then, somewhere in your blueprints:

from flask import views

from app.blueprints.continent.models.continent import ContinentModel
from app.ext.sqlalchemy.database import db_session


class ContinentView(views.View):
    def dispatch_request(self, continent_slug):
        continent = db_session.query(ContinentModel) \
            .filter_by(slug=continent_slug).first()

As you can see, to use database connection import this line:

from app.ext.sqlalchemy.database import db_session

Database Migrations

migrations/
├── README
├── alembic.ini.template
├── env.py
├── script.py.mako
└── versions/

alembic.ini.template. Template file for alembic config. This file is used in the initial step.

Everything else is just the default Alembic structure. Flask-Backbone uses pure Alembic, so just check their documentation.

Jinja Customization

app/jinja
├── __init__.py
├── context_processor.py
└── filters.py

Write custom Jinja filters and pass extra properties to your templates. All in one place and easy to support. Check source code for examples.

In the __init__.py you'll find function register_jinja_mapping(app). This function is invoked from app.py and your central point for Jinja tools.

Check out my post about Jinja Filters

JavaScript Document Onload + TypeScript version

javascript DOM snippet basics typescript
🗃 JavaScript Basics

Universal code-snippet to know when HTML is ready for modern browsers.

Commands

Commands are implemented using Click, and it's a clear way to define both your application commands and blueprint commands. Read the official Flask documentation.

Run flask in your shell to view available commands.

Application

Place your application commands in the app/commands. Go there and check available commands source code. It will pretty much explain everything. One thing you must understand is that application commands must be related to application logic. If you want to do something related to the blueprint then do it in your blueprint folder.

flask app create-blueprint
flask app list-blueprints

Blueprint

Let's create our first blueprint command. As a developer, I want to be able to run flask about me command.

Flask-Backbone. Blueprint command

1. Go to your about blueprint, open or create commands/__init__.py and add next code:

import click

from flask import current_app
from flask.cli import with_appcontext


@click.command('me')
@click.option('--name', prompt='Enter your name')
@with_appcontext
def command_about_me(name):
    click.echo(f"Your name: {name}")


def init_blueprint_cli(blueprint):
    blueprint.cli.add_command(command_about_me)

We created our first command with Flask and click. Don't forget to use @with_appcontext decorator for application context. This way you can use, for example current_app.config.get("MY_CONFIG").

2. Register commands in your blueprint. Open routes.py and add new code.

from app.blueprints.about.commands import init_blueprint_cli

# ... your blueprint routes ...

init_blueprint_cli(blueprint)

Finally, test your command.

flask about me
Enter your name: Dmitry
Your name: Dmitry

Extensions

Flask-Caching

To enable Flask-Caching, change CACHE_TYPE in your instance/config.py. By default it's "NullCache" which means that caching is disabled.

Explore Flask-Caching documentation for possible options and set them in your instance/config.py

To use cache decorator:

from app.ext.cache import cache

...

@blueprint.route("/", methods=["get"])
@cache.cached(timeout=0)
def w_index_route():
	...

Flask-Caching documentation

Sentry

To enable Sentry, add this lines in your instance/config.py:

SENTRY_ENABLED = True
SENTRY_DSN = "your sentry dsn"

Flask Sentry documentation

Deploy

I'll cover deploy in the next article, but it's pretty much explained in this digitalocean article.

Conclusion

Some aspect isn't covered here, but I feel like right now it would make this article more chaotic. If you're an experienced developer you probably won't need much reading, and you'll understand everything just by reading the code. Most of the code is based on the official Flask patterns. Don't be shy and contact me if your have anything to say.

A Short Guide To The Chinese Coordinate System

Have your ever searched google maps china offset? Most people who was in China yes. Here is my story behind this question.

The Definitive Guide To Sitemaps With Python

open source python

Sitemaps are important. Especially for big websites. It is always a good idea to develop your website with SEO in mind. Unfortunately, most developers ignore this part.

How to Import CSV Files to PostgreSQL with Pgfutter

csv postgresql

Sometimes I need to upload large CSV files to PostgreSQL. CSV file might have hundreds of columns, that's why i want a tool that can do some magic for me.

How to Upload Files to DigitalOcean Spaces with Python

digitalocean python

Snippet on how to upload files to DigitalOcean Spaces with boto3 and python.

How to Copy Text to Clipboard With Javascript

javascript DOM browser

Here is a short snippet on how to copy text to the clipboard with Javascript. Your visitors will thank you. Most likely not, but here we are.

How to Upload Files To Bunny.net Storage

python bunny_net storage snippet

Bunny.net is a well-known CDN and storage among developers. Here is my python snippet on how to upload a file to the storage.

How to Create Jinja2 Filters in Flask

python flask jinja2 snippet

In this post, I'll talk about filters. Jinja2 has a list of built-in filters, and Flask leverages them.

JavaScript Document Onload + TypeScript version

javascript DOM snippet basics typescript
🗃 JavaScript Basics

Universal code-snippet to know when HTML is ready for modern browsers.