Flask样板和你的2023年Flask指南。带有SQLAlchemy。

作者: Dmitry

更新于

pythonflaskboilerplateguidejinjapostgresqlsqlalchemyflask-backbone
Flask-Backbone

前言

我喜欢使用 Flask 并使用这个框架制作了很多网站。我可以构建我想要的任何东西,我非常感激它作为开发者给我的自由。 Flask-Backbone 是我创建初始结构和一组规则的方式,因此大多数我的 Flask 项目易于支持。我已经在开发新项目时使用它。

我开始 Flask-Backbone 的另一个原因是,我理解对于新开发人员来说有时候深入新技术可能会很困难。 Flask 对于新手来说是一把双刃剑。它很容易开始,但当您需要扩大规模时,您最终会得到一堆臭代码。

在 Flask-Backbone 中,我使用了官方文档中的模式,因此您不会在进行自定义和从此处扩展时遇到任何问题。

您可以将 Flask-Backbone 视为宜家手册。它是一个模板。您可以学习 Flask 中的工作原理,也可以创建没有任何限制的大型项目。我每天都在我的工作中使用它。

注意:虽然 Flask 是一个很棒的工具,但对于 API 服务,我建议使用 Falcon、FastApi 或其他面向 API 的框架。请记住,作为一名软件工程师,选择正确的工具很重要。不用担心,如果您决定使用 Flask 来构建 API 服务,您也会做得很好。

Flask-Backbone 是一个模板,但我添加了一些交互工具。我还决定不发布大部分实用程序,以保持简单。我可能会以后将它们作为单独的库发布。

I18N。我删除了对本地化的支持。这是一个更复杂的主题,将在单独的文章中进行讨论。

虽然本文解释了 Flask 的一些术语,但它无法替代 Flask 文档。如果您是新手,您需要在路上阅读一些 Flask 知识,因此请保留 Flask 文档。

Flask应用程序样板。特性。

  • 预定义基本结构,使您最终获得干净的架构。
  • 通过SQLAlchemy 2+支持数据库。但是,您可以跳过数据库设置并使用Flask-Backbone而无需数据库。个人而言,我不使用Flask-SQLAlchemy,但您可以使用。
  • 使用instance_relative_config开发/生产/您自己的配置。
  • 通过flask_caching支持缓存。通过配置轻松设置。
  • Flask-Debug
  • 支持Sentry。只需添加您的DSN,就可以开始使用了。
  • Jinja过滤器和自定义变量。
  • 旨在成为蓝图优先。使用蓝图保持结构清晰稳定。一切都是蓝图。您未来的自己会感谢您的。
  • 交互式命令以创建您的下一个蓝图。定义您的蓝图骨架以加快开发速度。要创建下一个蓝图,只需运行flask app create-blueprint。这取决于您,您可以完全忽略或删除此部分,一切都将完美地运行。
  • 使用配置脚本进行初始设置。
  • uWSGI配置

Flask文件夹结构

├── 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
├── configure.py
├── requirements.txt
├── webservice.ini
└── wsgi.py

app/blueprints。这是你的蓝图所在的地方。关于此,请参阅本文的蓝图部分。

app/commands。应用程序范围的命令。例如,在这里,我放置了用于操作蓝图的脚本。

app/enums。在这里放置你的应用程序范围的 Python 枚举。本文不涉及此内容,你可以自由决定如何使用枚举

app/ext。应用程序扩展。你将在这里找到 SQLAlchemy、Sentry 和 Flask-Caching。你可以将其视为某种抽象层。在这里配置外部库,并从任何地方调用该处的库。

app/errors。在此注册你的应用程序错误处理程序。例如 404。你将在此找到一个名为register_error_handlers的函数。

app/jinja。Jinja 配置。

app/jinja/__init__.py。Jinja 映射器定义。

app/jinja/context_processor.py。Jinja上下文处理器。在这里设置任何要传递给模板的键。

app/jinja/filters.py。Jinja 自定义过滤器

app/models。应用程序范围的模型。其他模型必须存储在蓝图中。

app/static。共享静态文件。

app/templates。共享模板。通常,我在这里放置主要布局和错误页面。

app/app.py。这是 Flask 应用程序的主要文件。这是你的应用程序中心点。

开始。安装

注意:您需要 Python 3.11+。

首先,克隆 flask-backbone 代码库。

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

接下来。一般来说,确保你使用虚拟环境。For example python3 -m venv pythonenv. 这将在文件夹pythonenv中创建一个虚拟环境。我更喜欢将我的Python环境命名为pythonenv,因为这更具描述性。

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

运行 configure.py。它会询问您有关未来设置的一些问题。

python configure.py

这个工具将会创建: .envinstance/config.py

Flask的初始配置

最后,启动你的应用程序。

flask run

配置基础

Flask-Backbone的配置文件

配置基于Python文件。 我曾经基于ini文件,也就是"从数据文件配置",但是这不具有可持续性,并且会引入很多限制。使用Python配置文件可以给你更多的自由,我感觉更符合Python风格。

在config文件夹中,您将找到三个文件:default.pydevelopment.pyproduction.py。这些文件是为了在仓库中使用而创建的。它们包含一般配置信息,不能包含关于您项目的任何敏感信息。您可以根据需要创建任意多的配置文件。环境变量APP_CONFIG的值是配置文件的名称。例如,如果您设置APP_CONFIG=test,那么将使用config/test.py。默认情况下,它是一个开发环境。

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: Flask) -> None:

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

    # load configuration from .env
    app.config.from_prefixed_env()

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

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

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 toView 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 2 as ORM. 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:password@localhost:5432/test_db"

If you want to switch to Flask-SQLAlchemy it will be easy for you.

app/ext/sqlalchemy/
├── __init__.py
├── database.py
├── model.py

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

Please check Alembic and install it if you need migrations. I removed Alembic to reduce gap for a newbie devs.

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

Commands

Commands are implemented using Click, and it's a clear way to define both your application commands and blueprint commands. Read the officialFlask 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.

The Latest

Quick Python Intro to OpenAI Chat Completion Functions

Quick Python Intro to OpenAI Chat Completion Functions
pythonopenaichatgpt

In this brief article, I will underline the key points and present an illustrative code snippet demonstrating how to make an API call to Wikipedia using user input.

recv() failed (104: Connection reset by peer) while reading response header from upstream

uWSGI. recv() failed (104: Connection reset by peer) while reading response header from upstream
pythonuwsgierrorsnippet

Geolocate the Location of an IP Address With Cloudflare Workers and JavaScript

Geolocate the Location of an IP Address With Cloudflare Workers and JavaScript
javascriptcloudflaregeolocationworkers

Detect a visitor's country and city by IP address with Cloudflare Workers and JavaScript.

JavaScript Document Onload + TypeScript version

JavaScript Document Onload + TypeScript version
javascriptDOMsnippetbasicstypescript
🗃JavaScript Basics

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

JavaScript addEventListener

JavaScript addEventListener
javascriptDOMsnippetbasics
🗃JavaScript Basics

How to Create Jinja2 Filters in Flask

How to Create Jinja2 Filters in Flask.
pythonflaskjinja2snippet

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

How to Upload Files To Bunnynet Storage

How to Upload Files To Bunnynet Storage
pythonbunny_netstoragesnippet

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 Copy Text to Clipboard With Javascript

How to Copy Text to Clipboard With Javascript.
简体中文javascriptDOMbrowser

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.

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

Flask Boilerplate and Your Guide to Flask in 2023. With SQLAlchemy.
boilerplateopen sourceflask

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.

How to Import CSV Files to PostgreSQL with Pgfutter

How to Import CSV Files to PostgreSQL with Pgfutter.
csvpostgresql

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.