为什么SQLAlchemy InstrumentedList的顺序不持久?

时间:2017-05-21 04:01:43

标签: python database list sqlalchemy

快速摘要:我想在SQLAlchemy中有一个有序的地址列表。 但是当我提交时,我的列表顺序会发生变化。 为什么会发生这种情况?如何更改呢?

冗长的解释:

  1. 我从附加到User对象的Address列表开始。
  2. 然后我用a替换“地址”列表的第一个元素 新地址。
  3. 然后我打印地址列表......到目前为止订单是我所期望的。
  4. 最后我承诺。我提交后,我做了一个查询,但我的顺序 地址列表已更改。
  5. 这只是关于数据库的一般情况,我不明白?或者SQLAlchemy InstrumentedList不像实际列表那样?我以为我可以改变关系中元素的顺序,但我不知道如何。

    from sqlalchemy import Column, Integer, String
    from sqlalchemy import create_engine
    from sqlalchemy import ForeignKey
    from sqlalchemy.orm import relationship
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy.orm import sessionmaker
    
    Base = declarative_base()
    Session = sessionmaker()
    
    class User(Base):
        __tablename__ = 'users'
        id = Column(Integer, primary_key=True)
        name = Column(String(50))
        fullname = Column(String(50))
        password = Column(String(12))
    
        addresses = relationship("Address", back_populates="user")
    
        def __repr__(self):
            return "<User(name='%s', fullname='%s', password='%s')>" % (
                                    self.name, self.fullname, self.password)
    
    class Address(Base):
        __tablename__ = 'addresses'
        id = Column(Integer, primary_key=True)
        email_address = Column(String, nullable=False)
        user_id = Column(Integer, ForeignKey('users.id'))
    
        user = relationship("User", back_populates="addresses")
    
        def __repr__(self):
            return "<Address(email_address='%s')>" % self.email_address
    
    
    if __name__ == "__main__":
        engine = create_engine('sqlite:///:memory:', echo=False)
        Session.configure(bind=engine)
        Base.metadata.create_all(engine)
        session = Session()
    
        user = User(name='ed', fullname='Ed Jones', password='edspassword')
        user.addresses = [Address(email_address='jack@google.com'), Address(email_address='j25@yahoo.com')]
        session.add(user)
        session.commit()
    
        user = session.query(User).filter_by(name='ed').first()
    
        print("Current order of addresses list at start.")
        print(user.addresses)
        print()
    
        new_primary_address = Address(email_address='primary@google.com')
        user.addresses[0] = new_primary_address
    
        print("Current order of addresses list before commit.")
        print("But after chaning addresses[0].")
        print(user.addresses)
        print()
    
        session.commit()
    
        user = session.query(User).filter_by(name='ed').first()
    
        print("Current order of addresses list after commit.")
        print(user.addresses)
        print()
    
        print("Why is the order of the InstrumentedList not persistent?")
        print("Isn't persistent order what makes a list a list?")
    

1 个答案:

答案 0 :(得分:2)

这是&#34;数据库&#34;一般来说。 InstrumentedList的确就像实际list一样,添加了ORM检测Python端,但当commit Session的默认行为是使所有数据库到期时加载状态的ORM管理的属性,因此列表必须在下次访问时刷新。这意味着SELECT等

2017-05-21 13:32:31,124 INFO sqlalchemy.engine.base.Engine SELECT addresses.id AS addresses_id, addresses.email_address AS addresses_email_address, addresses.user_id AS addresses_user_id 
FROM addresses 
WHERE ? = addresses.user_id
发出

以获取列表内容。在SQL the order of a SELECT is unspecified中,如果没有明确选择,那么您可能会也可能不会以与以前相同的顺序获取项目。还要注意ORM操作

user.addresses[0] = new_primary_address

转换为UPDATE,将旧地址元组的user_id设置为NULL并在表中插入新的地址,这样即使行返回,您也不会得到您认为的顺序按顺序排列。

如果地址顺序对您很重要,您必须选择订购。使用relationship的{​​{3}}参数:

class User(Base):
    ...
    addresses = relationship("Address", back_populates="user",
                             order_by="Address.email_address")
获取时,

将通过电子邮件地址对地址进行排序。 SQLAlchemy还提供了(感谢您挖掘它)一个可变有序关系的帮助器集合类:order_by,它有助于管理更改的索引/位置(如果用作排序)。

您似乎希望地址的顺序表示哪个是用户的主要地址。单独的标志列可以更好地发挥作用。