2017-07-18 67 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