【问题标题】:Updating a field in an Association Object in SQLAlchemy在 SQLAlchemy 中更新关联对象中的字段
【发布时间】:2013-10-28 12:54:15
【问题描述】:

我在 SQLAlchemy 中有一个关联对象,其中包含其他 2 个对象的一些额外信息(实际上是一个字段)。

第一个对象是Photo 模型,第二个对象是PhotoSet,关联对象称为PhotoInSet,它包含position 属性,它告诉我们Photo 在当前PhotoSet

class Photo(Base):

    __tablename__ = 'photos'
    id = Column(Integer, primary_key=True)
    filename = Column(String(128), index=True)
    title = Column(String(256))
    description = Column(Text)
    pub_date = Column(SADateTime)

class PhotoInSet(Base):

    __tablename__ = 'set_order'
    photo_id = Column(Integer, ForeignKey('photos.id'), primary_key=True)
    photoset_id = Column(Integer, ForeignKey('photo_set.id'), primary_key=True)
    position = Column(Integer)
    photo = relationship('Photo', backref='sets')

    def __repr__(self):
        return '<PhotoInSet %r>' % self.position


class PhotoSet(Base):

    __tablename__ = 'photo_set'
    id = Column(Integer, primary_key=True)
    name = Column(String(256))
    description = Column(Text)
    timestamp = Column(SADateTime)
    user_id = Column(Integer, ForeignKey('users.id'))
    user = relationship('User', backref=backref('sets', lazy='dynamic'))
    photo_id = Column(Integer, ForeignKey('photos.id'))
    photos = relationship('PhotoInSet', backref=backref('set', lazy='select'))

创建一个新的PhotoSet 保存position 并创建关系没有问题,这(大致)是这样完成的:

    # Create the Set
    new_set = PhotoSet(name, user)

    # Add the photos with positions applied in the order they came
    new_set.photos.extend(
        [
            PhotoInSet(position=pos, photo=photo)
            for pos, photo in
            enumerate(photo_selection)
        ]
    )

但是我在尝试弄清楚如何在订单更改时更新position 时遇到了很多麻烦。

如果我有 3 个Photo 对象,ID 分别为 1、2 和 3,位置分别为 1、2 和 3,创建后将如下所示:

>>> _set = PhotoSet.get(1)
>>> _set.photos
[<PhotoInSet 1>, <PhotoInSet 2>, <PhotoInSet 3>]

如果顺序发生变化,(让我们颠倒这个例子的顺序),SQLAlchemy 是否可以帮助我更新position 值?到目前为止,我对我能想出的任何方法都不满意。

最简洁的方法是什么?

【问题讨论】:

    标签: python sqlalchemy many-to-many


    【解决方案1】:

    看看Ordering List扩展:

    orderinglist 是可变有序关系的助手。它会 对关系()管理的拦截列表操作 收集并自动将列表位置的更改同步到 目标标量属性。

    我相信您可以将架构更改为:

    from sqlalchemy.ext.orderinglist import ordering_list
    
    # Photo and PhotoInSet stay the same...
    
    class PhotoSet(Base):
        __tablename__ = 'photo_set'
        id = Column(Integer, primary_key=True)
        name = Column(String(256))
        description = Column(Text)
        photo_id = Column(Integer, ForeignKey('photos.id'))
        photos = relationship('PhotoInSet',
           order_by="PhotoInSet.position",
           collection_class=ordering_list('position'),
           backref=backref('set', lazy='select'))
    
    # Sample usage...
    session = Session()
    
    # Create two photos, add them to the set...
    p_set = PhotoSet(name=u'TestSet')
    
    p = Photo(title=u'Test')
    p2 = Photo(title='uTest2')
    
    p_set.photos.append(PhotoInSet(photo=p))
    p_set.photos.append(PhotoInSet(photo=p2))
    session.add(p_set)
    
    session.commit()
    print 'Original list of titles...'
    print [x.photo.title for x in p_set.photos]
    print ''
    
    # Change the order...
    p_set.photos.reverse()
    # Any time you change the order of the list in a way that the existing
    # items are in a different place, you need to call "reorder". It will not
    # automatically try change the position value for you unless you are appending
    # an object with a null position value.
    p_set.photos.reorder()    
    session.commit()
    
    p_set = session.query(PhotoSet).first()
    print 'List after reordering...'
    print [x.photo.title for x in p_set.photos]
    

    这个脚本的结果...

    Original list of titles...
    [u'Test', u'uTest2']
    
    List after reordering...
    [u'uTest2', u'Test']
    

    在您的评论中,您说...

    所以这意味着如果我为 _set.photos 分配一个新列表,我就可以免费获得定位?

    我怀疑是这样的。

    【讨论】:

    • 所以这意味着如果我为_set.photos 分配一个新列表,我就可以免费获得定位?
    • Mnn 看来我缺少一些东西。我在set 上收到KeyError。如果我删除 backref 它会失败并显示 AttributeError: 'Photo' object has no attribute 'position'
    • 我相信,片场上的 KeyError 在您的原始架构中也是一个问题。第二个错误可能是因为您将 Photo 对象附加到 photo_set.photos 而不是 PhotoInSet 对象。
    • 我已经更新了我的答案,包括它如何工作的快速示例。
    猜你喜欢
    • 1970-01-01
    • 2014-11-05
    • 1970-01-01
    • 1970-01-01
    • 2023-03-05
    • 2020-02-16
    • 2019-10-16
    • 2013-11-13
    • 1970-01-01
    相关资源
    最近更新 更多