How to use marshmallows to serialize custom sqlalchemy field?

I am just starting a simple project called flask_wiki these days and I am using some jar extensions as shown below:

  • Flask-SQLAlchemy
  • Flask Restful
  • Marshmallows

Well, I just discovered that the MarshMallow project provides a class called "ModelSchema" that reads all the fields from my SQLAlchemy model and provides a fully automated serializer (de).

In my case, I created the "GUID" field, which is an RDBM agnostic, and inserted it into my Sqlalchemy model as follows:

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 is implemented as follows:

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)

Code works to create test objects (through a mixer); All commands are generated and verified correctly.

, 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'>.

, : sqlalchemy?

+3
2

. - :

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")

. ,

+6

Source: https://habr.com/ru/post/1625080/


All Articles