最近看了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_time
、status
字段 - 删除数据时,实现软删除
- 设置
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 |
|
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 |
|
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 总结
虽然现在的方法,看起来比较复杂,但是调用起来非常的方便,可以忍受封装的复杂,不可以忍受调用的复杂,奥利给!!!