Python Web开发:SQLAlchemy

上一篇博文简单介绍了Flask的使用,这次深入一下,研究如何将一个Web应用和数据库相结合。这就需要用到今天要提及的SQLAlchemy。

要了解SQLAlchemy,首先需要认识ORM。ORM在数据库中是指对象关系映射,通过某种映射,开发者可以通过面向对象的方式操作数据库。数据库的表被映射成类,行实例代表数据库中的记录,属性则对应于记录中的字段。具体而言,开发者不需要直接使用SQL语句就能编写DDL等数据库操作指令。

简单上手

首先我们简单用Flask蓝图,PostgresSQL来实现一个连接本地数据库,并插入数据的操作。

要做到这一点,我们需要实现几个内容:

  • 本地构建一个PG数据库,我使用了docker来构建。
  • 编写一个db文件,用来定义数据库模型,并且实现数据库写入的操作。
  • 编写一个Flask应用的配置文件,以达到灵活配置的作用。
  • 使用蓝图,并在蓝图中定义一个插入数据库的操作路由。
  • 编写一个Flask主程序,用来实例化Flask程序,并注册蓝图和初始化。

Postgres数据库的构建

Docker如何使用就不赘述了,执行以下命令行构建一个数据库:

1
docker run --name flask_db -e POSTGRES_PASSWORD=12345 -p 5432:5432 -d postgres

上述指令会构建一个名为flask_db的镜像,里面是一个PG数据库,默认名为postgres

配置文件的编写

配置文件用来保存一些隐私数据,例如数据库密码等。我们新建一个config.py文件,里面定义Config类,来保存配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import os
import dotenv

dotenv.load_dotenv()

DEFAULTS = {
'DB_USERNAME': 'postgres',
'DB_PASSWORD': '12345',
'DB_HOST': 'localhost',
'DB_PORT': '5432',
'DB_DATABASE': 'postgres',
'DB_CHARSET': '',
}

def get_env(key):
return os.environ.get(key, DEFAULTS.get(key))

class Config:
SQLALCHEMY_DATABASE_URI = f"postgresql://{get_env('DB_USERNAME')}:{get_env('DB_PASSWORD')}@{get_env('DB_HOST')}:{get_env('DB_PORT')}/{get_env('DB_DATABASE')}"
SQLALCHEMY_TRACK_MODIFICATIONS = False

配置文件写完,先暂时不用,我们去写数据库相关的操作。

数据库相关定义

我们需要用到SQLAlchemy来定义数据库模型。按照顺序,我们需要先对数据库实例化,随后定义模型,以及一个初始化方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from flask_sqlalchemy import SQLAlchemy

db = 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

def init_db():
if not User.query.filter_by(username='admin').first():
admin = User(username='admin', email='admin@example.com')
db.session.add(admin)

if not User.query.filter_by(username='guest').first():
guest = User(username='guest', email='guest@example.com')
db.session.add(guest)

db.session.commit()

User类继承自db.Model,实际上就是定义的数据库模型。而init_db用到了事务,来向数据库写入两个sample数据。

定义蓝图

随后,我们创建一个blueprint.py,在里面定义一个蓝图,这个蓝图专门负责用户相关的操作。这里我只定义了一个添加用户的操作,并且内容是写死的。

1
2
3
4
5
6
7
8
9
10
11
12
13
from flask import Blueprint, request, jsonify
from db import db, User

user_bp = Blueprint('user_bp', __name__)

@user_bp.route('/add_user', methods=['POST'])
def add_user():
username = 'user1'
email = 'zy1@gmail.com'
new_user = User(username=username, email=email)
db.session.add(new_user)
db.session.commit()
return jsonify(message='User added successfully')

定义主程序

目前为止我们构建的三个文件都只是独立且割裂的,我们需要在主程序应用中将它们串联起来。我们构建一个app.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from flask import Flask
from config import Config
from db import db, User, init_db
from blueprint import user_bp

def create_app():
app = Flask(__name__, instance_relative_config=True)
# 读取配置文件
app.config.from_object(Config)

# SQLAlchemy的Flask应用初始化方法
db.init_app(app)

# 数据库的初始化,后续用flask db migrate代替
with app.app_context():
db.create_all()
init_db()

# 注册蓝图
app.register_blueprint(user_bp)
return app

app = create_app()

这样,一个简单的应用就完成了。我们使用flask run --debug来执行。这时,直接向http://localhost:5000/add_user发送POST请求后,可以看到数据库里就创建并写入了一张新表。

上面包含了一段内容:

1
2
3
with app.app_context():
db.create_all()
init_db()

这段代码的作用是将定义好的模型同步到数据库。不过每次用上下文终归是太麻烦,因此可以使用Flask-Migrate代替。

Flask Migrate

Flask-Migrate是一个 Flask 扩展,用来处理 SQLAlchemy 数据库迁移。例如我们在前面定义的User类中新增一个字段,这时候数据库里并不会直接增加字段,我们就需要使用Flask-Migrate来进行数据库的同步。

执行pip install flask-migrate进行安装。

在主应用中导入Migrate模块并初始化,以下是示例代码:

1
2
3
4
5
6
7
8
9
10
11
from flask_migrate import Migrate

def create_app():
app = Flask(__name__, instance_relative_config=True)
app.config.from_object(Config)

db.init_app(app)
# 初始化 Flask-Migrate
migrate = Migrate(app, db)

return app

随后,执行flask db init来初始化迁移脚本的存储库。这个指令类似于Git仓库的初始化,只要执行一次就行了。

随后需要创建一个迁移的脚本,代码为flask db migrate -m "Initial migration."。这段代码的作用是检测数据库模型与当前数据库状态之间的差异,并自动生成一个迁移脚本。

最后执行flask db upgrade,作用是将应用迁移脚本到数据库,并修改数据库结构。

2024/6/2 于苏州