2017-07-18 5 views
4

J'utilise le modèle d'objet d'association SQLalchemy (http://docs.sqlalchemy.org/en/rel_1_1/orm/basic_relationships.html#association-object) pour trois classes de modèle.Comment utiliser factory boy pour tester des modèles d'objet d'association SQLalchemy?

La relation de base est sur le côté gauche. Un utilisateur peut appartenir à plusieurs organisations. Je stocke des données supplémentaires relatives à l'organisation utilisateur dans la classe d'objets association. Ensuite, la classe association-objet mappe un plusieurs-à-un à l'organisation. A partir du point SQLAlchemy, la relation fonctionne correctement. Le problème est de tester cela avec un garçon d'usine s'est avéré difficile et entraîne toujours une erreur RecursionError: maximum recursion depth exceeded.

Voici les trois modèles pour la relation d'objet de l'association, où l'utilisateur est parent et l'enfant est l'organisation:

class MemberOrgsAssoc(Model): 
     """The left side of the relationship maps a User as a one-to-many to 
     Organizations. User-Organization relevant data is stored in 
     this association-object table. Then, there is a one-to-many from 
     this association-object table to the Organization table. """ 

     __tablename__ = 'member_orgs' 

     member_id = Column(db.Integer, db.ForeignKey("users.id"), primary_key=True) 
     org_id = Column(db.Integer, db.ForeignKey("organizations.id"), primary_key=True) 
     manager_id = Column(db.Integer, db.ForeignKey("users.id")) 
     org_title = Column(db.Unicode(50)) 
     organization = relationship("Organization", back_populates="members") 
     member = relationship("User", back_populates="organizations", 
           foreign_keys=[member_id]) 
     manager = relationship("User", back_populates="subordinates", 
           foreign_keys=[manager_id]) 

class User(SurrogatePK, Model): 
    """A user of the app.""" 
    __tablename__ = 'users' 

    username = Column(db.Unicode(80), unique=True, nullable=False) 
    organizations = relationship("MemberOrgsAssoc", back_populates="member", 
           primaryjoin = "member_orgs.c.member_id == User.id", 
           lazy="dynamic") 
    subordinates = relationship("MemberOrgsAssoc", back_populates="manager", 
           primaryjoin = "member_orgs.c.manager_id == User.id", 
           lazy="dynamic") 

class Organization(SurrogatePK, Model): 
    """An organization that Users may belong to.""" 
    __tablename__ = 'organizations' 
    name = Column(db.Unicode(128), nullable=False) 
    members = relationship("MemberOrgsAssoc", back_populates="organization") 

Ainsi toutes les classes du modèle de sqlalchemy ci-dessus et les relations semblent fonctionner comme prévu pour l'instant.

Voici les trois classes d'usine-garçon que je tente de faire fonctionner.

MemberOrgs usine association objet:

class MemberOrgsAssocFactory(BaseFactory): 
    """Association-object table Factory""" 

    class Meta: 
     """Factory config""" 
     model = MemberOrgsAssoc 

    member_id = factory.SubFactory('tests.factories.UserFactory') 
    org_id = factory.SubFactory('tests.factories.OrganizationFactory') 
    manager_id = factory.SubFactory('tests.factories.UserFactory') 
    org_title = Sequence(lambda n: 'CEO{0}'.format(n)) 
    organization = factory.SubFactory('tests.factories.OrganizationFactory') 
    member = factory.SubFactory('tests.factories.UserFactory') 
    manager = factory.SubFactory('tests.factories.UserFactory') 

class UserFactory(BaseFactory): 
    """User factory.""" 

    class Meta: 
     """Factory configuration.""" 
     model = User 

    username = Sequence(lambda n: 'user{0}'.format(n)) 
    organizations = factory.List(
     [factory.SubFactory('tests.factories.MemberOrgsAssocFactory')]) 
    subordinates = factory.List(
     [factory.SubFactory('tests.factories.MemberOrgsAssocFactory')]) 

class OrganizationFactory(BaseFactory): 
    """Company factory""" 

    class Meta: 
     """Factory config""" 
     model = Organization 

    id = Sequence(lambda n: '{0}'.format(n)) 
    name = Sequence(lambda n: 'company{0}'.format(n)) 
    members = factory.List(
     [factory.SubFactory('tests.factories.MemberOrgsAssocFactory')]) 

Enfin, besoin de faire un utilisateur pour les tests et ainsi ci-dessous est un appareil de pytest pour faire un utilisateur. C'est là que les tests échouent en raison de `RecursionError: profondeur de récursivité maximale dépassée »

@pytest.fixture(scope='function') 
def user(db): 
    """An user for the unit tests. 
    setup reference: https://github.com/FactoryBoy/factory_boy/issues/101 
    # how to handle self referential foreign key relation in factory boy 
    # https://github.com/FactoryBoy/factory_boy/issues/173 
    """ 
    user = UserFactory(
     organizations__0=None, 
     subordinates__0=None, 
    ) 

    a = MemberOrgsAssocFactory(
     is_org_admin=True, 
     is_default_org=True, 
     is_active=True, 
    ) 

    a.organization=OrganizationFactory() 
    user.organizations.append(a) 

    db.session.commit() 
    return user 

Message d'erreur:.

E RecursionError: maximum recursion depth exceeded 
!!! Recursion detected (same locals & position) 

Répondre

1

plus ou moins résolu cela, mais un peu fragile d'ensemble doit suivre motif requis. soigneusement as laid out in the sqlalchemy docs:

""" EXAMPLE USE: 
# create User object, append an Organization object via association 
p = User() 
a = MemberOrgsAssoc(extra_data="some data") 
a.organization = Organization() 
p.organizations.append(a) 

# iterate through Organization objects via association, including association attributes: 
for assoc in p.organizations: 
    print(assoc.extra_data) 
    print(assoc.child) 
""" 

modifications ci-dessous pour la fixation de pytest tranché la question RecursionError et obtenu ce travail:

@pytest.fixture(scope='function') 
def user(db): 
    """An user for the tests.""" 

    user = UserFactory(
     organizations='', 
     subordinates='' 
    ) 

    a = MemberOrgsAssocFactory(
     member_id=None, 
     org_id=None, 
     manager_id=None, 
     is_org_admin=True, 
     is_default_org=True, 
     is_active=True, 
     organization=None, 
     member=None, 
     manager=None 
    ) 
    a.organization = OrganizationFactory(members=[]) 
    user.organizations.append(a) 
    db.session.commit() 

    # debugging 
    # thisuser = User.get_by_id(user.id) 
    # for assoc in thisuser.organizations: 
    # if assoc.is_default_org: 
    #  print('The default organization of thisuser is -> {}'.format(assoc.organization.name)) 

    return user