Guys, you closed my issue yesterday mayb I was not explain my issue clearly.
I try again. I would like separate schemas in different modules according its function.
Here is 3 differ modules with schemas (I call it serializers):
βββ serializers
β βββ init.py
β βββ analytic.py
β βββ portfolio.py
β βββ user.py
β βββ ....
user.py
from serializers.base import BaseSerializer
from serializers.portfolio import PortfolioSerializer
class UserBaseSerializer(BaseSerializer):
portfolios = fields.Nested(PortfolioSerializer, many=True, only=('id', 'type', 'balance'))
...
portfolio.py
from serializers.base import BaseSerializer
from serializers.user import UserBaseSerializer
class PortfolioSerializer(BaseSerializer):
user = fields.Nested(UserBaseSerializer, only=('id', 'username', 'avatar_url'))
type = EnumField(PortfolioTypeEnum)
balance = fields.Decimal(as_string=True)
It was my first code iteration and obviously I got ImportError.
So, I read again documentation and change code (refer to a nested schema by its class name.
user.py
from serializers.base import BaseSerializer
from serializers.portfolio import PortfolioSerializer
class UserBaseSerializer(BaseSerializer):
portfolios = fields.Nested('PortfolioSerializer', many=True, only=('id', 'type', 'balance'))
portfolio.py
from serializers.base import BaseSerializer
from serializers.user import UserBaseSerializer
class PortfolioSerializer(BaseSerializer):
user = fields.Nested('UserBaseSerializer', only=('id', 'username', 'avatar_url'))
type = EnumField(PortfolioTypeEnum)
balance = fields.Decimal(as_string=True)
I got again ImportError.
I found only one working solution - keep all of schemas in one module, but this is awful solution when you a lot of schemas.
What wrong?
I'll appreciate any help.
Upadate:
If I pass full module-qualified path in the field portfolios into UserBaseSerializer, I got other error:
marshmallow.exceptions.RegistryError: Class with name 'serializers.portfolio.PortfolioSerializer' was not found. You may need to import the class
My BaseSerializer:
class BaseSerializer(Schema):
id = fields.UUID()
@post_load
...
@post_dump
...
A schema class gets added to the internal class registry once it is declared/imported, so you need to ensure that the PortfolioSerializer has been imported before you refer to it as a string in other schemas.
The issue you are running into is a python circular import not specific to marshmallow. Python will stop interpreting the script when it hits a circular import and the error you end up with is that the attribute you are trying to import is undefined, because python stopped executing the module before actually executing the class declarations.
The example in the documentation for 2-way nesting gives you all the info you need. Once you have it working in a single file, referencing nested schemas by name, you you can pull the schemas out into separate files without needing to import them into each other. As long as you import all the schemas into a script before you try to use one, the references will be resolved automatically.
# serializers/user.py
from .serializers.base import BaseSerializer
class UserBaseSerializer(BaseSerializer):
portfolios = fields.Nested('PortfolioSerializer', many=True, only=('id', 'type', 'balance'))
# serializers/portfolio.py
from .serializers.base import BaseSerializer
class PortfolioSerializer(BaseSerializer):
user = fields.Nested('UserBaseSerializer', only=('id', 'username', 'avatar_url'))
type = EnumField(PortfolioTypeEnum)
balance = fields.Decimal(as_string=True)
# main.py
from .serializers.portfolio import PortfolioSerializer
from .serializers.user import UserBaseSerializer
...
A good way to organize this is to put all of the schema imports into serializers/__init__.py and import them from there so that any time you import one of them, they all get registered.
# serializers/__init__.py
from .portfolio import PortfolioSerializer
from .user import UserBaseSerializer
# main.py
from serializers import PortfolioSerializer
...
Great, thank you, guys!
I've clean forgotten that I can import all schemas in __init__.py. I'm a little ashamed of my question with such an obvious solution.
Most helpful comment
The issue you are running into is a python circular import not specific to marshmallow. Python will stop interpreting the script when it hits a circular import and the error you end up with is that the attribute you are trying to import is undefined, because python stopped executing the module before actually executing the class declarations.
The example in the documentation for 2-way nesting gives you all the info you need. Once you have it working in a single file, referencing nested schemas by name, you you can pull the schemas out into separate files without needing to import them into each other. As long as you import all the schemas into a script before you try to use one, the references will be resolved automatically.
A good way to organize this is to put all of the schema imports into
serializers/__init__.pyand import them from there so that any time you import one of them, they all get registered.