2017-07-18 101 views
4

我对三个模型类使用SQLalchemy关联对象模式(http://docs.sqlalchemy.org/en/rel_1_1/orm/basic_relationships.html#association-object)。如何使用工厂男孩测试SQLalchemy关联对象模型?

基本关系在左侧一个用户可以属于多个组织。我在关联对象类中存储了额外的用户 - 组织相关数据。然后,关联对象类将多对一映射到组织。

从SQLAlchemy的角度来看,这种关系可以正常工作。问题是测试这与工厂男孩已被证明是困难的,并总是导致错误RecursionError: maximum recursion depth exceeded

下面是三种车型为对象的关联关系,其中用户是家长和孩子组织:

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") 

因此,所有上述SQLAlchemy的模型类和关系似乎是用于现在的工作。

下面是我正在努力工作的三个工厂男生班。

MemberOrgs关联对象工厂:

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')]) 

最后,需要为测试一个用户,因此下面是一个pytest夹具以使用户。这是测试失败的原因`RecursionError:超过”最大递归深度

@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 

错误消息:

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

回答

1

更多或更少的解决了这个,虽然有点脆弱整体必须遵循所需的图案。仔细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) 
""" 

下面更改pytest夹具解决RecursionError问题,并得到它的工作:

@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