Package: anyblok_marshmallow
AnyBlok MarshmallowImproveAnyBlokto add validator, serializer and
deserializer schema withmarshmallow.This module is a wrapper ofmarshmallow-sqlalchemy,
the goal is to give the SQLAlchemy Model build by AnyBlok to this librairyAnyBlok Marshmallow is released under the terms of theMozilla Public License.See thelatest documentationContentsAnyBlok MarshmallowFront MatterProject HomepageProject StatusInstallationUnit TestDependenciesContributing (hackers needed!)AuthorContributorsBugsMementoDeclare yourAnyBlok modelDeclare your schema(De)serialize your data and validate itGive the registryAdd theregistryby the class attributeAdd theregistryduring initAdd theregistryby the contextAdd theregistrywhen the de(serialization or validator is calledmodeloptiononly_primary_keyoptionrequired_fieldsoptionUse the field JsonCollectionCHANGELOG2.3.0 (2019-10-31)2.2.3 (2019-05-06)2.2.2 (2018-12-06)2.2.1 (2018-10-26)2.2.0 (2018-10-17)2.1.0 (2018-09-26)2.0.1 (2018-06-07)2.0.0 (2018-05-30)1.4.0 (2018-04-07)1.3.0 (2017-12-23)1.2.0 (2017-11-30)1.1.0 (2017-11-02)1.0.2 (2017-10-25)1.0.0 (2017-10-24)Front MatterInformation about the AnyBlok / Marshmallow project.Project HomepageAnyBlok is hosted ongithub- the main project
page is athttps://github.com/AnyBlok/AnyBlok_Marshmallow. Source code is
tracked here usingGIT.Releases and project status are available on Pypi athttp://pypi.python.org/pypi/anyblok_marshmallow.The most recent published version of this documentation should be athttp://doc.anyblok-marshmallow.anyblok.org.Project StatusAnyBlok with Marshmallow is currently in beta status and is expected to be fairly
stable.   Users should take care to report bugs and missing features on an as-needed
basis.  It should be expected that the development version may be required
for proper implementation of recently repaired issues in between releases;InstallationInstall released versions of AnyBlok from the Python package index withpipor a similar tool:pip install anyblok_marshmallowInstallation via source distribution is via thesetup.pyscript:python setup.py installInstallation will add theanyblokcommands to the environment.Unit TestRun the test withpytest:pip install pytest pytest-cov
ANYBLOK_DRIVER_NAME=postgres ANYBLOK_DATABASE_NAME=mybase pytest anyblok_marshmallow/testsDependenciesAnyBlok works withPython 3.6and later. The install process will
ensure thatAnyBlok,marshmallow >= 3.2.0andmarshmallow-sqlalchemyare installed, in addition to other dependencies.
The latest version of them is strongly recommended.Contributing (hackers needed!)Anyblok / Marshmallow is at a very early stage, feel free to fork, talk with core
dev, and spread the word!AuthorJean-Sébastien SuzanneContributorsAnyboxteam:Jean-Sébastien SuzanneSenseeteam:Franck BretBugsBugs and feature enhancements to AnyBlok should be reported on theIssue
tracker.ContentsAnyBlok MarshmallowFront MatterProject HomepageProject StatusInstallationUnit TestDependenciesContributing (hackers needed!)AuthorContributorsBugsMementoDeclare yourAnyBlok modelDeclare your schema(De)serialize your data and validate itGive the registryAdd theregistryby the class attributeAdd theregistryduring initAdd theregistryby the contextAdd theregistrywhen the de(serialization or validator is calledmodeloptiononly_primary_keyoptionrequired_fieldsoptionUse the field JsonCollectionCHANGELOG2.3.0 (2019-10-31)2.2.3 (2019-05-06)2.2.2 (2018-12-06)2.2.1 (2018-10-26)2.2.0 (2018-10-17)2.1.0 (2018-09-26)2.0.1 (2018-06-07)2.0.0 (2018-05-30)1.4.0 (2018-04-07)1.3.0 (2017-12-23)1.2.0 (2017-11-30)1.1.0 (2017-11-02)1.0.2 (2017-10-25)1.0.0 (2017-10-24)MementoDeclare yourAnyBlok modelfrom anyblok.column import Integer, String
from anyblok.relationship import Many2One, Many2Many
from anyblok import Declarations


@Declarations.register(Declarations.Model)
class City:

    id = Integer(primary_key=True)
    name = String(nullable=False)
    zipcode = String(nullable=False)

    def __repr__(self):
        return '<City(name={self.name!r})>'.format(self=self)


@Declarations.register(Declarations.Model)
class Tag:

    id = Integer(primary_key=True)
    name = String(nullable=False)

    def __repr__(self):
        return '<Tag(name={self.name!r})>'.format(self=self)


@Declarations.register(Declarations.Model)
class Customer:
    id = Integer(primary_key=True)
    name = String(nullable=False)
    tags = Many2Many(model=Declarations.Model.Tag)

    def __repr__(self):
        return '<Customer(name={self.name!r}, '
               'tags={self.tags!r})>'.format(self=self)


@Declarations.register(Declarations.Model)
class Address:

    id = Integer(primary_key=True)
    street = String(nullable=False)
    city = Many2One(model=Declarations.Model.City, nullable=False)
    customer = Many2One(
        model=Declarations.Model.Customer, nullable=False,
        one2many="addresses")WarningTheAnyBlok modelmust be declared in a blokDeclare your schemafrom anyblok_marshmallow import SchemaWrapper, PostLoadSchema, Nested

class CitySchema(SchemaWrapper):
    model = 'Model.City'


class TagSchema(SchemaWrapper):
    model = 'Model.Tag'


class AddressSchema(SchemaWrapper):
    model = 'Model.Address'

    class Schema:
        # Add some marshmallow fields or behaviours

        # follow the relationship Many2One and One2One
        city = Nested(CitySchema)


class CustomerSchema(SchemaWrapper):
    model = 'Model.Customer'
    # optionally attach an AnyBlok registry
    # to use for serialization, desarialization and validation
    registry = registry

    class Schema(PostLoadSchema):
        # follow the relationship One2Many and Many2Many
        # - the many=True is required because it is *2Many
        # - exclude is used to forbid the recurse loop
        addresses = Nested(AddressSchema, many=True, exclude=('customer', ))
        tags = Nested(TagSchema, many=True)


customer_schema = CustomerSchema()NoteNewin version1.1.0the Nested field must come fromanyblok_marshmallow,
becausemarshmallowcache the Nested field with the context. And the context is not propagated
again if it changedNoteRefin version1.4.0,post_load_return_instancewas replaced by the mixin classPostLoadSchemaNoteRefin version2.1.0,ModelSchemawas replaced bySchemaWrapper. This action
break the compatibility with the previous version, but allow to follow the upgrade ofmarshmallow(De)serialize your data and validate itcustomer = registry.Customer.insert(name="JS Suzanne")
tag1 = registry.Tag.insert(name="tag 1")
customer.tags.append(tag1)
tag2 = registry.Tag.insert(name="tag 2")
customer.tags.append(tag2)
rouen = registry.City.insert(name="Rouen", zipcode="76000")
paris = registry.City.insert(name="Paris", zipcode="75000")
registry.Address.insert(customer=customer, street="Somewhere", city=rouen)
registry.Address.insert(customer=customer, street="Another place", city=paris)

dump_data = customer_schema.dump(customer).data
# {
#     'id': 1,
#     'name': 'JS Suzanne',
#     'tags': [
#         {
#             'id': 1,
#             'name': 'tag 1',
#         },
#         {
#             'id': 2,
#             'name': 'tag 2',
#         },
#     ],
#     'addresses': [
#         {
#             'id': 1
#             'street': 'Somewhere'
#             'city': {
#                 'id': 1,
#                 'name': 'Rouen',
#                 'zipcode': '76000',
#             },
#         },
#         {
#             'id': 2
#             'street': 'Another place'
#             'city': {
#                 'id': 2,
#                 'name': 'Paris',
#                 'zipcode': '75000',
#             },
#         },
#     ],
# }

customer_schema.load(dump_data).data
# <Customer(name='JS Suzanne' tags=[<Tag(name='tag 1')>, <Tag (name='tag 2')>])>

errors = customer_schema.validate(dump_data)
# dict with all the validating errorsNoteWe have an instance of the model cause of the mixinPostLoadSchemaGive the registryThe schema need to have the registry.If no registry found when the de(serialization) or validation then theRegistryNotFoundexception will be raised.Add theregistryby the class attributeThis is the solution given in the main exemple:class CustomerSchema(SchemaWrapper):
    model = 'Model.Customer'
    registry = registryAdd theregistryduring initThis solution is use during the instanciationcustomer_schema = CustomerSchema(registry=registry)Add theregistryby the contextThis solution is use during the instanciation or aftercustomer_schema = CustomerSchema(context={'registry': registry})orcustomer_schema = CustomerSchema()
customer_schema.context['registry'] = registryAdd theregistrywhen the de(serialization or validator is calledcustomer_schema.dumps(customer, registry=registry)
customer_schema.dump(customer, registry=registry)
customer_schema.loads(dump_data, registry=registry)
customer_schema.load(dump_data, registry=registry)
customer_schema.validate(dump_data, registry=registry)modeloptionThis option add in the model name. As the registry, this option
can be passed by definition, initialization, context or during the call of the (de)serialization / validationclass AnySchema(SchemaWrapper):
    model = "Model.Customer"orany_schema = AnySchema(model="Model.customer")orany_schema.context['model'] = "Model.Customer"orany_schema.dumps(instance, model="Model.Customer")
any_schema.dump(instance, model="Model.Customer")
any_schema.loads(dump_data, model="Model.Customer")
any_schema.load(dump_data, model="Model.Customer")
any_schema.validate(dump_data, model="Model.Customer")only_primary_keyoptionThis option add in the only argument the primary keys of the model. As the registry, this option
can be passed by definition, initialization, context or during the call of the (de)serialization / validationclass CustomerSchema(SchemaWrapper):
    model = "Model.Customer"
    only_primary_key = Trueorcustomer_schema = CustomerSchema(only_primary_key=True)orcustomer_schema.context['only_primary_key'] = Trueorcustomer_schema.dumps(instance, only_primary_key=True)
customer_schema.dump(instance, only_primary_key=True)
customer_schema.loads(dump_data, only_primary_key=True)
customer_schema.load(dump_data, only_primary_key=True)
customer_schema.validate(dump_data, only_primary_key=True)required_fieldsoptionThis option force the generated fields, and only them to be requried.class CustomerSchema(SchemaWrapper):
    model = "Model.Customer"
    required_fields = True
    # or required_fields = [ list of fieldname ]orcustomer_schema = CustomerSchema(required_fields=True)orcustomer_schema.context['required_fields'] = Trueorcustomer_schema.loads(dump_data, required_fields=True)
customer_schema.load(dump_data, required_fields=True)
customer_schema.validate(dump_data, required_fields=True)NoteAll the attributes can takeTrueor the list of the fieldname to be requiredUse the field JsonCollectionThis field allow the schema to inspect an AnyBlok.fields.Json in an any specific instance to
validate the value.AnyBlok models:@register(Model)
class Template:
    name = anyblok.column.String(primary_key=True)
    properties = anyblok.column.Json(defaumt={})

@register(Model)
class SaleOrder:
    id = anyblok.column.Integer(primary_key=True)
    number = anyblok.column.Integer(nullable=False)
    discount = anyblok.column.Integer()AnyBlok / Marchmallow schema:class SaleOrderSchema(SchemaWrapper):
    model = 'Model.SaleOrder'

    class Schema:
        discount = JsonCollection(
            fieldname='properties',
            keys=['allowed_discount'],
            cls_or_instance_type=marshmallow.fields.Integer(required=True)
        )Use:goodcustomer = registry.Template.insert(
    name='Good customer',
    properties={'allowed_discount': [10, 15, 30]
)
customer = registry.Template.insert(
    name='Customer',
    properties={'allowed_discount': [0, 5, 10]
)
badcustomer = registry.Template.insert(
    name='Bad customer',
    properties={'allowed_discount': [0]
)

schema = SaleOrderSchema(registry=registry)

--------------------------

data = schema.load(
    {
        number='SO0001',
        discount=10,
    },
    instances={'default': goodcustomer}
)

--------------------------

data = schema.load(
    {
        number='SO0001',
        discount=10,
    },
    instances={'default': customer}
)
==> error = {}

--------------------------

data = schema.load(
    {
        number='SO0001',
        discount=10,
    },
    instances={'default': badcustomer}
)
==> error = {'discount': ['Not a valid choice']}ContentsAnyBlok MarshmallowFront MatterProject HomepageProject StatusInstallationUnit TestDependenciesContributing (hackers needed!)AuthorContributorsBugsMementoDeclare yourAnyBlok modelDeclare your schema(De)serialize your data and validate itGive the registryAdd theregistryby the class attributeAdd theregistryduring initAdd theregistryby the contextAdd theregistrywhen the de(serialization or validator is calledmodeloptiononly_primary_keyoptionrequired_fieldsoptionUse the field JsonCollectionCHANGELOG2.3.0 (2019-10-31)2.2.3 (2019-05-06)2.2.2 (2018-12-06)2.2.1 (2018-10-26)2.2.0 (2018-10-17)2.1.0 (2018-09-26)2.0.1 (2018-06-07)2.0.0 (2018-05-30)1.4.0 (2018-04-07)1.3.0 (2017-12-23)1.2.0 (2017-11-30)1.1.0 (2017-11-02)1.0.2 (2017-10-25)1.0.0 (2017-10-24)CHANGELOG2.3.0 (2019-10-31)Fixed du of Marshmallow 3.2.1 release2.2.3 (2019-05-06)Fixed du of Marshmallow 3.0.0RC5 releaseRefactored unittest from nosetest to pytest2.2.2 (2018-12-06)Fixed du of Marshmallow 3.0.0RC1 release2.2.1 (2018-10-26)Fixed du of Marshmallow 3.0.0b19 release2.2.0 (2018-10-17)Fixed the convertion of type betweenAnyBlok.Columnandmarshmallow.Field2.1.0 (2018-09-26)Fixed the compatibility withMarshmallow > 3.0.0b8RemovedModelSchemaclassAddedSchemaWrapper, this is the best way to defined a generated
schema with themarshmallow_sqlalchemylibraryWarningThis version break the compatibility with previous version, in the only
goal to be adapted with the latest version ofmarshmallow2.0.1 (2018-06-07)Fix required_field put allow_none to False2.0.0 (2018-05-30)Add JsonCollection field, Allow to add a check in function of an collection
stored in a AnyBlok.fields.JsonAdd Text field, to represent ananyblok.column.TextMigration of the code and unit test to marshmallow 3.0.0Add Email matching foranyblok.column.EmailAdd URL matching foranyblok.column.URLAdd PhoneNumber matching foranyblok.column.PhoneNumberAdd Country matching foranyblok.column.CountryAdd required_fields optionAdd InstanceField1.4.0 (2018-04-07)Replacepost_load_return_instancemethod byPostLoadSchemaclassIn the case of the fieldSelection, the validatorOneOfis
applied with the available values come from the AnyBlok columnsReplacemarshmallow_sqlalchemy.fields.Relatedbyanyblok_marshmallow.fields.Nested. The goal is to improve the consistent
between all field in the schema1.3.0 (2017-12-23)[ADD] unittest on some case[FIX] AnyBlok field.Function is return as MarshMallow fields.Raw[ADD] fields.File, type to encode and decode to/from base 641.2.0 (2017-11-30)[REF] decrease complexity[IMP] Addvalidates_schemaon ModelSchema to automaticly check
if the field exist on the model1.1.0 (2017-11-02)Add option put only the primary keysFix the Front pageREF model option, can be given by another way than MetaPut RegistryNotFound in exceptionsAdd Nested field, this field is not and have not to be cached1.0.2 (2017-10-25)Fix pypi documentation1.0.0 (2017-10-24)Add marshmallow schema for AnyBlok for:SerializationDeserializationValidation