【问题标题】:Two one-to-many relationships between the same two models相同的两个模型之间的两个一对多关系
【发布时间】:2017-06-10 12:34:01
【问题描述】:

我有一个Player 类,每个玩家拥有X 个Character 实例:

class Player(Model):
    characters = relationship('Character', back_populates='owner')

class Character(Model):
    owner = relationship('Player', back_populates='characters')
    owner_id = Column('player_id', Integer, ForeignKey('player.id'))

但现在我想将这些字符分成两个单独的列表,分别位于graveyard 和普通的characters 列表中:

class Player(Model):
    characters = relationship('Character', back_populates='owner')
    graveyard = relationship('Character', back_populates='owner')

class Character(Model):
    owner = relationship('Player', back_populates='characters')
    owner_id = Column('player_id', Integer, ForeignKey('player.id'))

但正如您所见,SQLAlchemy 无法区分这两个列表。如何启用此类行为?

【问题讨论】:

    标签: python python-3.x sqlalchemy relationship


    【解决方案1】:

    我大胆假设有一种方法可以区分角色是否在坟墓场中。否则这将毫无意义。如果“字符”表包含有关字符位置的信息,那么您需要的是“交替连接”,如下所述:http://docs.sqlalchemy.org/en/latest/orm/join_conditions.html#specifying-alternate-join-conditions

    如果我正确理解您的问题,这是一个愚蠢但有效的示例(忽略带有会话等的 gobbledygook,我只是在一段旧代码上复制粘贴了一个新模型)。它将创建一个模型,插入一个玩家,一个活着的和一个死去的角色,然后显示它们。这是你要找的吗?

    from sqlalchemy import Integer, ForeignKey, String, Column, Boolean, create_engine
    from sqlalchemy.orm import sessionmaker, scoped_session
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy.exc import IntegrityError
    from sqlalchemy.orm import relationship
    
    Base = declarative_base()
    
    engine = create_engine("postgresql://test:test@localhost/test", echo=False)
    session_factory = sessionmaker(bind=engine)
    Session = scoped_session(session_factory)
    
    
    class Player(Base):
        __tablename__ = 'player'
        id = Column(Integer, primary_key=True)
        name = Column(String, unique=True)
        characters = relationship('Character', primaryjoin="and_(Character.player_id == Player.id, \
                                  Character.alive == True)")
        graveyard = relationship('Character', primaryjoin="and_(Character.player_id == Player.id, \
                                  Character.alive == False)")
    
    
    class Character(Base):
        __tablename__ = 'character'
        id = Column(Integer, primary_key=True)
        name = Column(String, unique=True)
        alive = Column(Boolean)
        player_id = Column(Integer, ForeignKey('player.id'))
    
    
    Base.metadata.create_all(engine)
    
    try:
        p = Player(name="foo")
        Session.add(p)
        Session.commit()
    except IntegrityError:
        Session.rollback()
    
    try:
        p = Session.query(Player).filter(Player.name == "foo").one()
        c1 = Character(name="c1", alive=True, player_id=p.id)
        c2 = Character(name="c2", alive=False, player_id=p.id)
        Session.add(c1)
        Session.add(c2)
        Session.commit()
    except IntegrityError:
        Session.rollback()
    
    player = Session.query(Player).filter(Player.name == "foo").one()
    
    print player
    for c in player.characters:
        print c.name
    
    for c in player.graveyard:
        print c.name
    

    【讨论】:

    • “我大胆假设有一种方法可以区分角色是否在坟墓场。”不是真的,除了他在哪个列表中...:s 我想我应该添加一个 bool 标志,除了告诉它在哪个列表中之外,它并没有真正做任何事情,对吧?但是是的,除此之外,这似乎已经足够了
    • 但是如果没有任何标志,那么数据库就无法区分墓地和角色。要记住的一件非常重要的事情是 SQLAlchemy 不会将关系规则传输到数据库。无论有没有关系子句,您的数据库看起来都完全相同。它们只是 sqlalchemy 助手,用于提供对代码中类的访问并简化查询。影响数据库的唯一行是 player_id = .. .ForeignKey 约束。它改变了数据库。
    • 如果您不喜欢任何关系陈述,则不需要它们。您始终可以在单个查询中执行相同的操作。它们只是为了帮助您组织代码并使其更具可读性。数据库不关心它们。所以是的,你的字符表中需要一个标志,你可以通过这种方式或者只是使用 Session.query 方法的“标准”方式来查询它。
    • 是的,这一切现在都说得通了!您的解决方案不是我将使用的 exact 解决方案,但它帮助我更好地了解 SQLAlchemy 的工作原理。我最终创建了两个单独的类(简化了墓地示例)和表格,谢谢和 kiitos :)
    猜你喜欢
    • 2017-06-10
    • 2018-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多