Sqlalchemy模型对象序列化

最近看了Python Flask高级编程之RESTFul API前后端分离精讲的课程受益匪浅,在一起的知识层面上进一步构建更加完美的项目结构,这里主要记录Sqlalchemy模型对象序列化问题。

0x01 以前方法

以前处理模型对象序列化的问题的时候,都是手动将模型对象构造成需要返回数据的字典,非常的繁琐,写在视图函数中显得代码结构非常的臃肿,举个栗子

1
user_list = []
2
users = User.query.all()
3
for _ in users:
4
    user = {}
5
    user['id'] = _.id
6
    user['name'] = _.name
7
    user_list.append(user)
8
return jsonify(user_list)

这种方法是最好实现的,但是写在视图函数中非常的臃肿,早想改掉这种写法,知道看到这个课程,发现了更加方便的写法。

0x02 现在方法(更好)

现在的写法是对整个项目结构的改善,主要讲序列化的代码写在了Model中,同时对JSONEncoder类中的default方法进行重写,使得能够解析模型对象

serialize.py

1
from flask.json import JSONEncoder as _JSONEncoder
2
3
from app.libs.error_code import ServerError
4
from datetime import date
5
6
class JSONEncoder(_JSONEncoder):
7
    def default(self, o):
8
        if hasattr(o, 'keys') and hasattr(o, '__getitem__'): #判断模型对象是否有keys 和 __getitem__属性,如果有使用dict()返回模型对象类属性和实例对象
9
            return dict(o)
10
        if isinstance(o, date): #对date类型进行格式化
11
            return o.strftime('%Y-%m-%d')
12
        raise ServerError()

如果仅仅这样写的话,通过调试发现,重写的default函数运行,还是运行原来的函数,所以需要指定Flask中的json_encoder对象

1
from flask import Flask as _Flask
2
class Flask(_Flask):
3
    json_encoder = JSONEncoder

dbs.py

继承db.Model类,所有的模型对象继承Base类,主要实现以下几个功能:

  • 模型对象增加create_timestatus字段
  • 删除数据时,实现软删除
  • 设置keys方法,自动执行获取,模型对象中的self.fields的值
  • self.fields进行增加删除,方便自定义返回数据
1
class Base(db.Model):
2
    __abstract__ = True
3
    create_time = Column(DateTime, default=datetime.now(), comment='创建时间')
4
    status = Column(SmallInteger, default=1, comment='用于软删除')
5
6
    def __getitem__(self, item):
7
        return getattr(self, item)
8
9
    def set_attrs(self, attrs_dict):
10
        for key, value in attrs_dict.items():
11
            if hasattr(self, key) and key != 'id':
12
                setattr(self, key, value)
13
14
    def delete(self):
15
        self.status = 0
16
17
    def keys(self):
18
        return self.fields
19
20
    def hide(self, *keys):
21
        for key in keys:
22
            self.fields.remove(key)
23
        return self
24
25
    def append(self, *keys):
26
        for key in keys:
27
            self.fields.append(key)
28
        return self

models.py

在构造函数中设置self.fields实现返回默认字段数据

1
class User(Base):
2
    '''
3
    用户:用户id、用户名、分数、授权登录时间(第一次使用本平台)、上次登录时间、本地登录时间
4
    第一次在前端使用本平台,会检查下sso中的id是否存在此表,若不存在则新增,代表用户授权使用本项目
5
    '''
6
    __tablename__ = 't_user'
7
    id = db.Column(db.String(50), primary_key=True, default=shortuuid.uuid)
8
    username = db.Column(db.String(20), comment='用户名', unique=True, nullable=False)
9
    email = db.Column(db.String(255), comment='邮箱', unique=True, nullable=False)
10
    _password = db.Column('password', db.String(100))
11
    score = db.Column(db.Integer, default=100, comment='用户积分')
12
    join_time = db.Column(db.DateTime, default=datetime.datetime.now, comment='加入时间')
13
    before_last_login_time = db.Column(db.DateTime, default=datetime.datetime.now, comment='上次登录时间')
14
    last_login_time = db.Column(db.DateTime, default=datetime.datetime.now, comment='本次登录时间')
15
    auth = db.Column(db.SmallInteger, default=1, comment='用于权限认证,1:普通用户;2:管理员;3:超级管理员')
16
17
    @orm.reconstructor  #增加这个装饰器,使得运行构造函数,如果不加,则不会运行。
18
    def __init__(self):
19
        self.fields = ['id', 'name', 'score', 'json_time', 'before_last_login_time', 'last_login_time', 'auth']
20
21
    # 或者重写keys方法
22
    def keys(self):
23
        return ['id', 'name', 'score', 'json_time', 'before_last_login_time', 'last_login_time', 'auth']

view.py

视图函数中只需要传入模型对象就可以了

1
@api.route('', methods=['GET'])
2
def get_user():
3
    uid = g.user.id
4
    user = User.query.filter_by(id=uid).first_or_404()
5
    return jsonify(user)

0x03 总结

虽然现在的方法,看起来比较复杂,但是调用起来非常的方便,可以忍受封装的复杂,不可以忍受调用的复杂,奥利给!!!