如何使用棉花糖序列化自定义 sqlalchemy 字段?

How to use marshmallow to serialize a custom sqlalchemy field?

我这几天刚开始一个名为 flask_wiki 的简单项目,我正在使用一些 Flask 扩展,如下所示:

好吧,我刚刚发现 MarshMallow 项目提供了一个名为 'ModelSchema' 的 class,它从我的 SQLAlchemy 模型中读取所有字段并提供一个完全自动化的(反)序列化程序。

就我而言,我创建了一个与 RDBM 无关的 'GUID' 字段,并将其插入到我的 Sqlalchemy 模型中,如下所示:

from flask.ext.sqlalchemy import SQLAlchemy
from flask_wiki.backend.custom_fields import GUIDField

class Page(db.Model):
    """
    Implements the Page Model.
    """
    guid = db.Column(GUIDField, primary_key=True, default=uuid.uuid4)
    name = db.Column(db.String, nullable=False)
    raw_content = db.Column(db.Text)
    rendered_content = db.Column(db.Text)

    def __repr__(self):
        return self.__str__()

    def __str__(self):
        return self.name

GUIDField是这样实现的:

from sqlalchemy.types import TypeDecorator, CHAR
import uuid


class GUIDField(TypeDecorator):
    # Platform independent GUID Implementation that uses little endianess.
    impl = CHAR

    def load_dialect_impl(self, dialect):
        return dialect.type_descriptor(CHAR(32))

    def process_bind_param(self, value, dialect):
        if value is None:
            return value
        else:
            if isinstance(value, uuid.UUID):
                return value.bytes_le

    def process_result_value(self, value, dialect):
        if value is None:
            return value
        else:
            return uuid.UUID(bytes_le=value)

创建测试对象的代码(通过混合器)正在运行;所有 guid 均已正确生成和验证。

考虑到这一点,A 刚刚创建了以下 MarshMallow 序列化程序字段:

from marshmallow import fields
import uuid


class GUIDSerializationField(fields.Field):
    def _serialize(self, value, attr, obj):
        if value is None:
            return value
        else:
            if isinstance(value, uuid.UUID):
                return str(value)
            else:
                return None

最后,我创建了 SerializerClass:

from flask_wiki.backend.backend import marsh
from flask_wiki.backend.custom_serialization_fields import GUIDSerializationField
from flask_wiki.backend.models import Page
from marshmallow import fields


class PageSchema(marsh.ModelSchema):
    class Meta:
        model = Page

    guid = GUIDSerializationField()


page_schema = PageSchema()
pages_schema = PageSchema(many=True)

我尝试在插入和不插入 guid 字段的情况下使用最后一个代码,但在所有情况下都会出现以下错误:

Traceback (most recent call last):
  File "/home/arthas/dev/flask-wiki/flask_wiki/wiki.py", line 3, in <module>
    from flask_wiki.backend import backend
  File "/home/arthas/dev/flask-wiki/flask_wiki/backend/__init__.py", line 1, in <module>
    import flask_wiki.backend.routes
  File "/home/arthas/dev/flask-wiki/flask_wiki/backend/routes.py", line 2, in <module>
    from flask_wiki.backend.views import PageView
  File "/home/arthas/dev/flask-wiki/flask_wiki/backend/views.py", line 3, in <module>
    from flask_wiki.backend.serializers import pages_schema, page_schema
  File "/home/arthas/dev/flask-wiki/flask_wiki/backend/serializers.py", line 7, in <module>
    class PageSchema(marsh.ModelSchema):
  File "/home/arthas/env/wiki/lib/python3.5/site-packages/marshmallow/schema.py", line 116, in __new__
    dict_cls=dict_cls
  File "/home/arthas/env/wiki/lib/python3.5/site-packages/marshmallow_sqlalchemy/schema.py", line 53, in get_declared_fields
    declared_fields = mcs.get_fields(converter, opts)
  File "/home/arthas/env/wiki/lib/python3.5/site-packages/marshmallow_sqlalchemy/schema.py", line 77, in get_fields
    return converter.fields_for_model(opts.model, fields=opts.fields, exclude=opts.exclude)
  File "/home/arthas/env/wiki/lib/python3.5/site-packages/marshmallow_sqlalchemy/convert.py", line 75, in fields_for_model
    field = self.property2field(prop)
  File "/home/arthas/env/wiki/lib/python3.5/site-packages/marshmallow_sqlalchemy/convert.py", line 93, in property2field
    field_class = self._get_field_class_for_property(prop)
  File "/home/arthas/env/wiki/lib/python3.5/site-packages/marshmallow_sqlalchemy/convert.py", line 151, in _get_field_class_for_property
    field_cls = self._get_field_class_for_column(column)
  File "/home/arthas/env/wiki/lib/python3.5/site-packages/marshmallow_sqlalchemy/convert.py", line 121, in _get_field_class_for_column
    return self._get_field_class_for_data_type(column.type)
  File "/home/arthas/env/wiki/lib/python3.5/site-packages/marshmallow_sqlalchemy/convert.py", line 143, in _get_field_class_for_data_type
    'Could not find field column of type {0}.'.format(types[0]))
marshmallow_sqlalchemy.exceptions.ModelConversionError: Could not find field column of type <class 'flask_wiki.backend.custom_fields.GUIDField'>.

所以,我最后问:如何使用marshmallow序列化一个自定义的sqlalchemy字段?

根据我在文档中阅读的内容,您可以在 ModelSchema Meta class. The ModelConverter class 中指定一个 model_converter 属性,您可以在子类中覆盖一个 SQLA_TYPE_MAPPING 属性以添加您的自定义 GUID 字段到自动架构生成器检测到的类型。

也就是说,我从未使用过它,所以我不知道这是否有效。

您需要创建自己的转换器。 尝试这样的事情:

import uuid
from flask_wiki.backend.models import Page
from flask_wiki.backend.backend import marsh
from marshmallow_sqlalchemy.convert import ModelConverter
from flask_wiki.backend.custom_fields import GUIDField
from marshmallow import fields


#Here you are overwriting the list of types of the SQLAlchemy, adding your custom type 
class GUIDConverter(ModelConverter):
    SQLA_TYPE_MAPPING = dict(
        list(ModelConverter.SQLA_TYPE_MAPPING.items()) + 
        [(GUIDField, fields.Str)] 
    )

class GUIDSerializationField(fields.Field):
    def _serialize(self, value, attr, obj):
        if value is None:
            return value
        else:
            if isinstance(value, uuid.UUID):
                return str(value)
            else:
                return None

class PageSchema(marsh.ModelSchema):
    class Meta:
        model = Page        
        model_converter = GUIDConverter #Tell to Marshmallow to use your custom converter for this model

    guid = GUIDSerializationField(attribute="guid")

您还可以查看this link寻求帮助。 我希望它对我的英语不好感到抱歉