场景:我们有时候可能需要返回模型对象中的某些字段,或者全部字段,平时的做法就是将对象中的各个字段转为字典在返回jsonnify(data), 但是这样的写法可能在每个需要返回数据的试图函数中都写一个对应的字典。。对象转字典在返回。json默认是不能序列化对象的,一般我们的做法是 json.dumps(obj, default=lambda o: o.__dict__)但是 __dict__中只保存实例属性,我们的模型类基本定义的类属性。解决这个问题就要看jsonify中是如何做序列化的,然后怎么重写。
重写JSONEncoder
from datetime import date from flask import Flask as _Flask from flask.json import JSONEncoder as _JSONEncoder class JSONEncoder(_JSONEncoder): """ 重写json序列化,使得模型类的可序列化 """ def default(self, o): if hasattr(o, 'keys') and hasattr(o, '__getitem__'): return dict(o) if isinstance(o, date): return o.strftime('%Y-%m-%d') super(JSONEncoder, self).default(o) # 需要将重写的类绑定到应用程序中 class Flask(_Flask): json_encoder = JSONEncoder模型类的定义
class User(Base): id = Column(Integer, primary_key=True) email = Column(String(24), unique=True, nullable=False) nickname = Column(String(24), unique=True) auth = Column(SmallInteger, default=1) _password = Column('password', String(100)) def keys(self): return ['id', 'email', 'nickname', 'auth'] def __getitem__(self, item): return getattr(self, item)注意: 修改了json_encode方法后,只要调用到flask.json 模块的都会走这个方法
为什么要写keys和__getitem__方法
当我们使用dict(object) 操作一个对象的时候,dict首先会到实例中找keys的方法,将其返回列表的值作为key, 然后会根据object[key] 获取对应的值,所以实例要实现__getitem__方法才可以使用中括号的方式调用属性
进阶写法 - 控制返回的字段
场景:当我们有一个Book的模型类,我们的api接口可能需要返回book的详情页所以就要返回所有字典,但另外一个接口可能只需要返回某几个字段。
class Book(Base): id = Column(Integer, primary_key=True, autoincrement=True) title = Column(String(50), nullable=False) author = Column(String(30), default='未名') binding = Column(String(20)) publisher = Column(String(50)) price = Column(String(20)) pages = Column(Integer) pubdate = Column(String(20)) isbn = Column(String(15), nullable=False, unique=True) summary = Column(String(1000)) image = Column(String(50)) # orm实例化对象, 字段需要写在构造函数中,这样每个实例对象都会有自己的一份,删除增加都不会互相影响 @orm.reconstructor def __init__(self): self.fields = ['id', 'title', 'author', 'binding', 'publisher', 'price', 'pages', 'pubdate', 'isbn', 'summary', 'image'] def keys(self): return self.fields if hasattr(self, 'fields') else [] def hide(self, *keys): for key in keys: self.fields.remove(key) return self def append(self, *keys): for key in keys: self.fields.append(key) return self @api.route('/search') def search(): books = Book.query.filter().all() # 根据某些条件搜索的 books = [book.hide('summary') for book in books] return jsonify(books) @api,route('/<isbn>/detail') def detail(isbn): book = Book.query.filter_by(isbn=isbn).first_or_404() return jsonify(book) 请求钩子函数before_first_request:在处理第一个请求前运行。
before_request:在每次请求前运行。
after_request:如果没有未处理的异常抛出,在每次请求后运行。
teardown_request:在每次请求后运行,即使有未处理的异常抛出。
全局扫描器模仿flask exceptions 预加载各个异常类的方式,将用户组自动加载进内存中,这样获取的话就更方便
str2obj = {} level2str = {} def iteritems(d, *args, **kwargs): return iter(d.items(*args, **kwargs)) def _find_scope_group(): for _name, obj in iteritems(globals()): try: is_scope_obj = issubclass(obj, BaseScope) except TypeError: is_scope_obj = False if not is_scope_obj or obj.level < 1: continue old_obj = str2obj.get(_name, None) if old_obj is not None and issubclass(obj, old_obj): continue str2obj[_name] = obj level2str[obj.level] = _name # 模仿flask exceptions 预加载各个异常类的方式,将用户组自动加载进内存 _find_scope_group() del _find_scope_group 常见bugform正则校验注意事项
r'{6, 25}$'
带空格和不带空格是两码事, 正则里面{,} 连续不带空格
r'{6,25}$'
参考Python Flask高级编程之RESTFul API前后端分离精讲
七月老师的课程挺好的,不是纯写代码,而是从问题入手,怎么把复杂问题简单化,从0到1。