Python: 2.7
Flask: 1.0.3
Flask-SQLAlchemy: 2.3.2SQLAlchemy: 1.3.12
Flask-SQLAlchemy是Flask中比较好用的ORM框架,说起ORM框架,熟悉SpringBoot开发的同学肯定对JPA比较了解,它们都是通过实体对象映射关系库中的表,操作实体对象,进行增删改查,减少了SQL的编写,非常方便。在JPA中,当我们修改了一个实体类之后(添加字段),对应的表结构可以自动发生更新,通常不需要人为干预。在Flask-SQLAlchemy中,并没有提供自动更新表结构的功能,网上也有不少方式去增强实现这个功能,比如表迁移新建、执行SQL等等。笔者经过一系列的搜索之后,没有找到一个自己比较满意的解决方案,所以总结了一个相对比较方便的方法,能够自动根据实体对象,自动更新表结构。
项目Github地址:https://github.com/shirukai/flask-examples-auto-alter-tables.git
思路非常的简单:
1. 拿到数据库所有表及字段信息
2. 拿到继承db.Model的所有子类
3. 分析实体类字段与表字段是否一致
4. 不一致,将进行更新操,目前只进行字段新增的判断
结合上面的实现思路,在进行实现之前,先简单列一下几个技术点:
如何拿到数据库所有表及字段信息?
通过db.engin.table_names()
可以拿到所有的表名
通过sqlalchemy.Table()
可以拿到具体的表元数据
如何拿到db.Model所有子类
直接通过db.Model.__subclasses__()
如何修改表
db.engine.execute()
可以执行对应的修改表结构的语句
PyCharm创建一个名为flask-examples-auto-alter-tables的项目,Python选2.7,其它版本也可以。
在项目下创建名为requirements.txt的文件,添加如下内容:
SQLAlchemy==1.3.12
Flask==1.0.3
Flask_SQLAlchemy==2.3.2
pip安装依赖
pip install -r requirements.txt
在项目下创建一个models.py的文件,内容如下:
# encoding: utf-8
"""
@author : shirukai
@date : 2021/7/22
"""
import uuid
import sqlalchemy
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm.attributes import InstrumentedAttribute
db = SQLAlchemy()
def auto_alter_tables(flask_app):
"""
自动修改表结构
:param flask_app:
:return:
"""
with flask_app.app_context():
metadata = sqlalchemy.MetaData()
tables = {table_name: {column.name: column for column in
sqlalchemy.Table(table_name, metadata, autoload=True, autoload_with=db.engine).c}
for table_name in db.engine.table_names()}
models = db.Model.__subclasses__()
for model_class in models:
table_name = model_class.__table__.name
if table_name in tables:
table = tables[table_name]
for attr_name in dir(model_class):
attr = getattr(model_class, attr_name)
if isinstance(attr, InstrumentedAttribute) \
and hasattr(attr, 'type') \
and hasattr(attr, 'compile'):
attr_name = attr.name
# 添加新字段
if attr_name not in table:
column_type = attr.type.compile(dialect=db.engine.dialect)
db.engine.execute(
'ALTER TABLE %s ADD COLUMN %s %s' % (table_name, attr_name, column_type))
class User(db.Model):
"""
用户表
"""
__tablename__ = 'user'
id = db.Column(db.String(32), primary_key=True, default=uuid.uuid1().hex, comment='用户ID')
name = db.Column(db.String(32), nullable=False, comment='用户名称')
def to_dict(self):
return {attr: getattr(self, attr) for attr in self.__mapper__.attrs.keys()}
在项目下创建一个名为main.py的文件,内容如下:
# encoding: utf-8
"""
@author : shirukai
@date : 2021/7/22
"""
import os
from flask import Flask, jsonify, request
from models import db, User, auto_alter_tables
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
app = Flask(__name__)
# 使用本地的sqlite数据库
app.config.from_mapping(**{
'SQLALCHEMY_DATABASE_URI': 'sqlite:///%s/application.db?check_same_thread=False' % BASE_DIR,
'SQLALCHEMY_TRACK_MODIFICATIONS': False
})
db.init_app(app)
with app.app_context():
db.create_all()
@app.route('/users', methods=['POST'])
def add_users():
"""
添加用户接口
:return:
"""
res = request.json
user = User()
for attr in User.__mapper__.attrs.keys():
if attr in res:
value = res[attr]
setattr(user, attr, value)
db.session.add(user)
db.session.commit()
return 'success'
@app.route('/users', methods=['GET'])
def get_users():
"""
查询用户接口
:return:
"""
users = User.query.all()
return jsonify([u.to_dict() for u in users])
# 更新表结构
auto_alter_tables(app)
app.run('localhost', 18111)
执行main.py启动Flask应用,右击直接运行即可:
调用创建用户的接口:
POST请求,请求参数为
{
"name":"小明"
}
调用查询用户的接口:
GET请求,请求结果为
[
{
"id": "3af2b9b5f1b011eb8315acde48001122",
"name": "小明"
}
]
修改models.py里的User实体类,添加一个age字段
class User(db.Model):
"""
用户表
"""
__tablename__ = 'user'
id = db.Column(db.String(32), primary_key=True, default=uuid.uuid1().hex, comment='用户ID')
name = db.Column(db.String(32), nullable=False, comment='用户名称')
# 新增字段
age = db.Column(db.Integer, nullable=True, comment='年龄')
def to_dict(self):
return {attr: getattr(self, attr) for attr in self.__mapper__.attrs.keys()}
完成上述修改之后重新启动应用
调用创建用户的接口
POST请求,请求参数为
{
"name":"小明",
"age":19
}
调用查询用户的接口
目前当前项目只实现了表的新增字段,对于其它需求,如字段类型变更、字段删除等操作,可以参考这个思路实现,但是要注意对表中原有数据产生的影响。目前代码已经上传至Github,地址:https://github.com/shirukai/flask-examples-auto-alter-tables.git。