代码之家  ›  专栏  ›  技术社区  ›  Nevermore

SQLAlchemy模型不可预测地分配关系

  •  1
  • Nevermore  · 技术社区  · 6 年前

    我正在创建数据库 User 可以属于多个的模型 Trait s、 下面的脚本复制了一个我不理解的奇怪行为——在提交新行之后,关系会附加 特质 s到 使用者 以一种不可预测的方式。

    我希望能够为用户附加1个以上的特征,并且我希望用户能够共享特征。

    但是在这个例子中,当一个特征在用户之间共享时 有时 已连接到用户1,并且 其他时间 用户2。我怎样才能让用户分享自己的特点呢?

    import enum
    from sqlalchemy import Column, Integer, String
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import Enum, UniqueConstraint, ForeignKey
    from sqlalchemy.orm import backref, relationship, validates
    Base = declarative_base()
    
    class TraitName(enum.Enum):
        happy = 0
        mad = 1
        full = 2
    
    class User(Base):
        __tablename__ = 'users'
    
        id = Column(Integer, primary_key=True)
        username = Column(String(80), unique=True, nullable=False)
        traits = relationship("Trait")
        def __repr__(self):
            return self.username + str(traits)
    
    class Trait(Base):
        __tablename__ = 'traits'
    
        id = Column(Integer, primary_key=True)
        name = Column(Enum(TraitName), nullable=False)
        user_id = Column(Integer, ForeignKey('users.id'))
        __table_args__ = (UniqueConstraint('name', 'user_id', name='_user_trait'),)
        def __repr__(self):
            return str(self.name)
    

    当我多次运行此脚本时,

    from sqlalchemy import create_engine
    from sqlalchemy.exc import IntegrityError
    from sqlalchemy.orm import Session
    
    engine = create_engine('postgresql://postgres@localhost:5420/test_db')
    Base.metadata.drop_all(engine)
    Base.metadata.create_all(engine)
    session = Session(engine)
    
    user1 = User(username="bob")
    user2 = User(username="ann")
    happy_trait = Trait(name=TraitName.happy)
    full_trait = Trait(name=TraitName.full)
    user1.traits.extend([full_trait, happy_trait])
    user2.traits.append(happy_trait)
    session.add_all([user1, user2])
    session.commit()
    print([t.traits for t in session.query(User).all()])
    print(session.query(Trait.user_id).all())
    print(session.query(Trait).filter(Trait.user_id.in_((1,))).all())
    session.close()
    

    不同的结果:

    要么:

    [[TraitName.full, TraitName.happy], []] # Users' traits
    [(1,), (1,)]                            # Traits' user_ids
    [TraitName.full, TraitName.happy]       # Traits with uers_id 1
    

    [[TraitName.full], [TraitName.happy]] # Users' traits
    [(1,), (2,)]                          # Traits' user_ids
    [TraitName.full]                      # Traits with uers_id 1
    

    所以我想知道为什么 特质 将不可预测地分配。以及如何对我的用户和特征进行建模以避免这种情况--也许是这样 不是的 将特性附加到用户的合适模式?

    另外,如果可能的话(我想一切都有可能),我希望有两个字段 使用者 : inactive_traits active_traits 字段。但无论如何,我首先必须弄清楚如何让一系列特质发挥作用。

    1 回复  |  直到 6 年前
        1
  •  1
  •   Nevermore    6 年前

    您的类命名错误,这导致您使用错误。

    你的TraitName实际上是你的Trait,它应该被共享,而你的Trait实际上是一个UserTrait,它不能在用户之间共享,但是你尝试在他们之间共享实例。

    我会做以下事情:

    import enum
    from sqlalchemy import Column, Integer, String
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import orm, Enum, ForeignKey, create_engine
    
    Base = declarative_base()
    
    class Trait(enum.Enum):
        happy = 0
        mad = 1
        full = 2
    
    class User(Base):
        __tablename__ = 'users'
    
        user_id = Column(Integer, primary_key=True)
        username = Column(String(80), unique=True, nullable=False)
        traits = orm.relationship("UserTrait")
        def __repr__(self):
            return self.username + str(self.traits)
    
    class UserTrait(Base):
        __tablename__ = 'usertraits'
    
        user_id = Column(Integer, ForeignKey('users.user_id'), primary_key=True)
        trait = Column(Enum(Trait), primary_key=True)
    
        def __repr__(self):
            return str([self.user_id, self.trait])
    
    def __main__():
    
        engine = create_engine('postgresql:///?host=/var/run/postgresql/', echo=False)
        Base.metadata.drop_all(engine)
        Base.metadata.create_all(engine)
        session = orm.Session(engine)
    
        user1 = User(username="bob")
        user2 = User(username="ann")
        user1.traits.extend([UserTrait(trait=Trait.full), UserTrait(trait=Trait.happy)])
        user2.traits.append(UserTrait(trait=Trait.happy))
        session.add_all([user1, user2])
        session.commit()
        print([t.traits for t in session.query(User).all()])
        print(session.query(UserTrait.user_id).all())
        print(session.query(UserTrait).filter(UserTrait.user_id.in_((1,))).all())
        session.close()
    
    if __name__ == "__main__":
        __main__()