Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Circular import in marshmallow

I have two objects that are related to one another. I'd like to be able to access one object through the other by going through the related attribute.

E.g. A.b_relationship.obj.some_property

How can I do this without creating a circular import?

# lib.py
class Relationship(object):

    def __init__(self, obj):
        self.obj = obj


# a.py
class A(object):
    b_relationship = Relationship(B)

# b.py
class B(object):
    a_relationship = Relationship(A)

For clarity, I've added this additional example. Obviously SQLAlchemy has solved this issue with the backref attribute. I'm not sure how feasible it would be for me to implement this sort of thing into marshmallow without breaking the way it works. Perhaps I need to change my mindset?

from marshmallow import Schema
from marshmallow.fields import String

from project.database import db


class PersonModel(db.Model):
    name = db.Column(db.String)


class PetModel(db.Model):
    name = db.Column(db.String)
    owner = db.relationship('PersonModel', backref='pets')


class PersonSchema(Schema):
    name = fields.String(init_arg='some value')
    pets = fields.Relationship(related_schema=PetSchema)


class PetSchema(Schema):
    name = fields.String()
    owner = fields.Relationship(related_schema=PersonSchema)
like image 410
Colton Allen Avatar asked Dec 25 '22 09:12

Colton Allen


2 Answers

From here: http://marshmallow.readthedocs.org/en/latest/nesting.html#two-way-nesting

See how a string is used for the class; AuthorSchema refers to 'BookSchema':

class AuthorSchema(Schema):
    # Make sure to use the 'only' or 'exclude' params
    # to avoid infinite recursion
    books = fields.Nested('BookSchema', many=True, exclude=('author', ))
    class Meta:
        fields = ('id', 'name', 'books')

class BookSchema(Schema):
    author = fields.Nested(AuthorSchema, only=('id', 'name'))
    class Meta:
        fields = ('id', 'title', 'author')

I assume in your case, you want to do the same thing with many=False. I've never used marshmallow but in Django, it's similar, we use class path like "my_app.MyClass" instead of MyClass to avoid circular import.

like image 191
François Constant Avatar answered Jan 10 '23 14:01

François Constant


You could implement a RelationshipManager (aka a registry), which all classes that can be part of a relationship must be registered with.

The Relationship initialiser could take then the name of the class it's related to, rather than the actual class object.

Finally, the relationship class itself can lazy-load the real class it's related to from the name it's given at initialisation (via the manager).

like image 26
Tom Dalton Avatar answered Jan 10 '23 14:01

Tom Dalton