Flask: common problems

Common SQLAlchemy/marshmallow problems

Problem: Object initialization order issue.
In python and still more to in flask, the order of import and instantiation of python files and classes matters.
To make it consistent, we need to be very careful.
We can have exception at the startup of the application if we define schemas in the same file as models:

flask --app flask_example run --port 5001 --debug
Traceback (most recent call last):
  ......  
  File "/home/david/python-workspace/flask_example/venv/lib/python3.9/site-packages/flask/cli.py", line 897, in run_command
    app = info.load_app()
  File "/home/david/python-workspace/flask_example/venv/lib/python3.9/site-packages/flask/cli.py", line 308, in load_app
    app = locate_app(import_name, name)
  File "/home/david/python-workspace/flask_example/venv/lib/python3.9/site-packages/flask/cli.py", line 218, in locate_app
    __import__(module_name)
  File "/home/david/python-workspace/flask_example/flask_example.py", line 5, in <module>
    from PersonRepository import PersonRepository
  File "/home/david/python-workspace/flask_example/PersonRepository.py", line 1, in <module>
    from models.Person import Person
  File "/home/david/python-workspace/flask_example/models/__init__.py", line 1, in <module>
    import models.Address
  File "/home/david/python-workspace/flask_example/models/Address.py", line 30, in <module>
    class AddressSchema(SQLAlchemyAutoSchema):
  File "/home/david/python-workspace/flask_example/venv/lib/python3.9/site-packages/marshmallow/schema.py", line 121, in __new__
    klass._declared_fields = mcs.get_declared_fields(
  File "/home/david/python-workspace/flask_example/venv/lib/python3.9/site-packages/marshmallow_sqlalchemy/schema.py", line 91, in get_declared_fields
    fields.update(mcs.get_declared_sqla_fields(fields, converter, opts, dict_cls))
  File "/home/david/python-workspace/flask_example/venv/lib/python3.9/site-packages/marshmallow_sqlalchemy/schema.py", line 130, in get_declared_sqla_fields
    converter.fields_for_model(
  File "/home/david/python-workspace/flask_example/venv/lib/python3.9/site-packages/marshmallow_sqlalchemy/convert.py", line 136, in fields_for_model
    for prop in model.__mapper__.attrs:
  File "/home/david/python-workspace/flask_example/venv/lib/python3.9/site-packages/sqlalchemy/util/langhelpers.py", line 1254, in __get__
    obj.__dict__[self.__name__] = result = self.fget(obj)
  File "/home/david/python-workspace/flask_example/venv/lib/python3.9/site-packages/sqlalchemy/orm/mapper.py", line 3017, in attrs
    self._check_configure()
  File "/home/david/python-workspace/flask_example/venv/lib/python3.9/site-packages/sqlalchemy/orm/mapper.py", line 2374, in _check_configure
    _configure_registries({self.registry}, cascade=True)
  File "/home/david/python-workspace/flask_example/venv/lib/python3.9/site-packages/sqlalchemy/orm/mapper.py", line 4186, in _configure_registries
    _do_configure_registries(registries, cascade)
  File "/home/david/python-workspace/flask_example/venv/lib/python3.9/site-packages/sqlalchemy/orm/mapper.py", line 4228, in _do_configure_registries
    mapper._post_configure_properties()
  File "/home/david/python-workspace/flask_example/venv/lib/python3.9/site-packages/sqlalchemy/orm/mapper.py", line 2391, in _post_configure_properties
    prop.init()
  File "/home/david/python-workspace/flask_example/venv/lib/python3.9/site-packages/sqlalchemy/orm/interfaces.py", line 544, in init
    self.do_init()
  File "/home/david/python-workspace/flask_example/venv/lib/python3.9/site-packages/sqlalchemy/orm/relationships.py", line 1632, in do_init
    self._setup_entity()
  File "/home/david/python-workspace/flask_example/venv/lib/python3.9/site-packages/sqlalchemy/orm/relationships.py", line 1851, in _setup_entity
    self._clsregistry_resolve_name(argument)(),
  File "/home/david/python-workspace/flask_example/venv/lib/python3.9/site-packages/sqlalchemy/orm/clsregistry.py", line 519, in _resolve_name
    self._raise_for_name(name, err)
  File "/home/david/python-workspace/flask_example/venv/lib/python3.9/site-packages/sqlalchemy/orm/clsregistry.py", line 500, in _raise_for_name
    raise exc.InvalidRequestError(
sqlalchemy.exc.InvalidRequestError: When initializing mapper Mapper[Address(address)], expression 'Person' failed to locate a name ('Person'). If this is a class name, consider adding this relationship() to the <class 'models.Address.Address'> class after both dependent classes have been defined.

Solution:
Schemas have to be defined in their own files and these have to be loaded after all models and processing of SQLAlchemy.
Example:

from marshmallow_sqlalchemy import SQLAlchemyAutoSchema
 
from models.Address import Address
from models.Person import Person
 
 
class PersonSchema(SQLAlchemyAutoSchema):
    class Meta:
        model = Person
        include_relationships = True
        load_instance = True
 
 
class AddressSchema(SQLAlchemyAutoSchema):
    class Meta:
        model = Address
        include_fk = True
        load_instance = True

Problem: Relationships are not serialized with marshmallow schema
The result may look like, instead of having (object)s, we have digits :

curl -X GET localhost:5001/api/person/5?fetch_address
{
  "addresses": [
    1
  ],
  "firstname": "boyoDoe",
  "id": 1,
  "lastname": "David"
}

Solution: In the schema definition, we need to add the relationship as a Nested field (with the same name).
Example:

from marshmallow_sqlalchemy import SQLAlchemyAutoSchema
from marshmallow_sqlalchemy import fields
 
from models.Address import Address
from models.Person import Person
 
class PersonSchema(SQLAlchemyAutoSchema):
    class Meta:
        model = Person
        include_relationships = True
        load_instance = True
 
    # addresses = fields.Nested(AddressSchema, )
    addresses = fields.Nested(AddressSchema, many=True)

Important:
If the relationship has many instances(like in this example), we need to specify the many attribute to true.

Ce contenu a été publié dans Non classé. Vous pouvez le mettre en favoris avec ce permalien.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *