Flask自定義序列化超詳細講解
1、問題溯源
重點就是一個Flask.make_response,這里會做請求的響應的處理。
里面行代碼:
elif isinstance(rv, dict): rv = jsonify(rv)
rv是我需要返回的響應:
{ 'msg': [ {'roleName': 'guest', 'access_list':[<accessName root>]}, {'roleName': 'admin', 'access_list': [...]} ], 'error_no': 0 }
rv是一個字典,但是msg的里面有部分東西無法序列化,jsonify里面返回如下。
return current_app.response_class( f"{dumps(data, indent=indent, separators=separators)}\n", mimetype=current_app.config["JSONIFY_MIMETYPE"], )
里面的data就是上面交道的類似json的數(shù)據(jù)(不是json,實際是對象)。
接下來:調(diào)用了flask下的自帶的一個json庫。
代碼片段: M-1
_dump_arg_defaults(kwargs, app=app) return _json.dumps(obj, **kwargs)
代碼片段: M -2
if cls is None: cls = JSONEncoder return cls( skipkeys=skipkeys, ensure_ascii=ensure_ascii, check_circular=check_circular, allow_nan=allow_nan, indent=indent, separators=separators, default=default, sort_keys=sort_keys, **kw).encode(obj)
這里的obj的:
{'msg': [{...}, {...}], 'error_no': 0}
就是我們之前講到的東西。
代碼片段: M-4,這個是內(nèi)置的json庫的default方法。內(nèi)置的沒有實現(xiàn)序列化,所以需要自己在某個步驟接入到這個序列化的過程。
def default(self, o): """Implement this method in a subclass such that it returns a serializable object for ``o``, or calls the base implementation (to raise a ``TypeError``). For example, to support arbitrary iterators, you could implement default like this:: def default(self, o): try: iterable = iter(o) except TypeError: pass else: return list(iterable) # Let the base class default method raise the TypeError return JSONEncoder.default(self, o) """ raise TypeError(f'Object of type {o.__class__.__name__} ' f'is not JSON serializable')
o在這里就是AccessOrm是實例對象,所以他報錯說這個:
Object of type AccessOrm is not JSON serializable
歸因:就是flask沒有自帶實現(xiàn)對類的序列化
解決: 就是通過flask的機制,綁定一個序列化類。
2、flask序列化
flask代碼里面寫了:
class JSONEncoder(_json.JSONEncoder): """The default JSON encoder. Handles extra types compared to the built-in :class:`json.JSONEncoder`. - :class:`datetime.datetime` and :class:`datetime.date` are serialized to :rfc:`822` strings. This is the same as the HTTP date format. - :class:`uuid.UUID` is serialized to a string. - :class:`dataclasses.dataclass` is passed to :func:`dataclasses.asdict`. - :class:`~markupsafe.Markup` (or any object with a ``__html__`` method) will call the ``__html__`` method to get a string. Assign a subclass of this to :attr:`flask.Flask.json_encoder` or :attr:`flask.Blueprint.json_encoder` to override the default. """
在片段代碼M-1中:
_dump_arg_defaults(kwargs, app=app)
函數(shù)內(nèi)容如下:
def _dump_arg_defaults( kwargs: t.Dict[str, t.Any], app: t.Optional["Flask"] = None ) -> None: """Inject default arguments for dump functions.""" if app is None: app = current_app if app: cls = app.json_encoder #app的json_encoder bp = app.blueprints.get(request.blueprint) if request else None # type: ignore if bp is not None and bp.json_encoder is not None: cls = bp.json_encoder #這里設置藍圖的json_encoder,藍圖的優(yōu)先級高于app.json_encoder # Only set a custom encoder if it has custom behavior. This is # faster on PyPy. if cls is not _json.JSONEncoder: kwargs.setdefault("cls", cls) kwargs.setdefault("cls", cls) kwargs.setdefault("ensure_ascii", app.config["JSON_AS_ASCII"]) kwargs.setdefault("sort_keys", app.config["JSON_SORT_KEYS"]) else: kwargs.setdefault("sort_keys", True) kwargs.setdefault("cls", JSONEncoder)
3、解決方法
使用自帶的序列化類
在入口函數(shù)setup.py中寫入
from flask import Flask from flask.json import JSONEncoder from config import Config,LogConfig from db import init_db from scripts import user_cli from flask_bcrypt import Bcrypt from utils import RedisPool class ExtendJSONEncoder(JSONEncoder): def default(self, o): if getattr(o,'toJson'): return o.toJson(o) else: return super().default(o) flask_bcrypt = Bcrypt() def create_app(): Flask.json_encoder = ExtendJSONEncoder #之前在with app.app_context() #或者在app實例化之后,修改app的JSONEncoder 都沒成功,這里簡單粗暴一點,直接修改Flask的。 app = Flask(__name__) app.config.from_object(Config) LogConfig.openLog() from utils import initException,initBeforeRequestHandle with app.app_context(): init_db(app) RedisPool(app) initException(app) initBeforeRequestHandle(app) flask_bcrypt.init_app(app) app.cli.add_command(user_cli) import controller for bp in controller.__all__: app.register_blueprint(controller.__dict__[bp]) return app
這時候,我的orm類需要一個toJson方法。
class AccessOrm(Base): __tablename__ = 'access' id = Column(Integer, primary_key=True) accessName = Column(String(255), nullable=True) def __repr__(self) -> str: return "<accessName {}>".format( self.accessName, ) def toJson(self,o): return { 'accessName': o.accessName, }
到此這篇關于Flask自定義序列化超詳細講解的文章就介紹到這了,更多相關Flask序列化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
python實現(xiàn)的登錄與提交表單數(shù)據(jù)功能示例
這篇文章主要介紹了python實現(xiàn)的登錄與提交表單數(shù)據(jù)功能,結(jié)合實例形式分析了Python表單登錄相關的請求與響應操作實現(xiàn)技巧,需要的朋友可以參考下2019-09-09Windows中安裝使用Virtualenv來創(chuàng)建獨立Python環(huán)境
有時我們的程序中需要調(diào)用不同版本的Python包和模塊,那么借助Virtualenv的虛擬環(huán)境就可以幫助我們隔離使用,接下來我們就來看一下在Windows中安裝使用Virtualenv來創(chuàng)建獨立Python環(huán)境的方法2016-05-05