• flask自定义序列化


    一、 flask返回响应的序列化流程

    1、问题溯源
    Flask
    Flask.__call__( )
    Flask.wsgi_app( )
    Flask.finalize_request( )
    Flask.make_response( )

    重点就是一个Flask.make_response,这里会做请求的响应的处理。

    里面行代码:

    elif isinstance(rv, dict):
         rv = jsonify(rv)
    
    • 1
    • 2

    rv是我需要返回的响应:

    {
    	'msg': [
            {'roleName': 'guest', 'access_list':[<accessName root>]},
            {'roleName': 'admin', 'access_list': [...]}
            ], 
        'error_no': 0
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    rv是一个字典,但是msg的里面有部分东西无法序列化,jsonify里面返回如下。

    return current_app.response_class(
            f"{dumps(data, indent=indent, separators=separators)}\n",
            mimetype=current_app.config["JSONIFY_MIMETYPE"],
        )
    
    • 1
    • 2
    • 3
    • 4

    里面的data就是上面交道的类似json的数据(不是json,实际是对象)。

    接下来:调用了flask下的自带的一个json库

    JSONEncoder.dumps( ) 【具体代码看M-1】
    python内置的json.dumps( ) 【具体代码看M-2】
    flask自带的JSONEncoder.default( )
    return super().default(o);这里的super是内置的json库 【具体代码看M-4】
    • 代码片段: M-1
    _dump_arg_defaults(kwargs, app=app)
    return _json.dumps(obj, **kwargs)
    
    • 1
    • 2
    • 代码片段: 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)
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

      这里的obj的:

      {'msg': [{...}, {...}], 'error_no': 0}
      
      • 1

      就是我们之前讲到的东西。

    • 代码片段: M-4,这个是内置的json库的default方法。内置的没有实现序列化,所以需要自己在某个步骤接入到这个序列化的过程。

      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')
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20

      o在这里就是AccessOrm是实例对象,所以他报错说这个:

      Object of type AccessOrm is not JSON serializable
      
      • 1
    • 归因:就是flask没有自带实现对类的序列化

    • 解决: 就是通过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.
        """
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在片段代码M-1中:

    _dump_arg_defaults(kwargs, app=app)
    
    • 1

    函数内容如下:

    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,蓝图的优先级高于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)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    3、解决方法

    使用自带的序列化类

    在入口函数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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    这时候,我的orm类需要一个toJson方法。

    class AccessOrm(Base):
        __tablename__ = 'access'
        id = Column(Integer, primary_key=True)
        accessName = Column(String(255), nullable=True)
    
        def __repr__(self) -> str:
            return "".format(
                self.accessName,
                )
    
        def toJson(self,o):
            return {
                'accessName': o.accessName,
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
  • 相关阅读:
    热门项目!知识付费小程序源码系统 带完整的安装代码包以及安装部署教程
    疫情后时代变了吗?
    前端网络请求性能优化之缓存
    达梦数据库——异常崩溃(core)分析处理
    TypeScript24:TS中的声明文件
    美日两国利差或继续增大 美元兑日元势创24年新高?
    C语言之字符串函数二
    【项目】实现一个mini的tcmalloc(高并发内存池)
    node.js 下载安装 配置环境变量
    git快速使用教程
  • 原文地址:https://blog.csdn.net/baidu_36831253/article/details/127652192