无法正确更新 SQLAlchemy 行

Cannot properly update SQLAlchemy row

我正在使用 sqlalchemy 范围会话,无法正确更新现有行。

这是我的模型:

db = scoped_session(sessionmaker())
Base = declarative_base()

class Song(Base):
    __tablename__ = 'song'
    id = Column(Integer, primary_key=True)
    artist_id = Column(Integer, ForeignKey('artist.id'))
    artist_title = Column(Text)
    title = Column(Text)
    artist = relationship('Artist', backref='songs')
    preview_url = Column(Text, default=None)


class Artist(Base):
    __tablename__ = 'artist'
    id = Column(Integer, primary_key=True)
    title = Column(Text)

    similar_artists = relationship('Artist',
                                   secondary=followers,
                                   primaryjoin=(followers.c.follower_id == id),
                                   secondaryjoin=(followers.c.followed_id == id),
                                   backref=backref('followers', lazy='dynamic'),
                                   lazy='dynamic')

Index('my_index', Artist.id, unique=True, mysql_length=255)

我已经填充了数据库,可以看到歌曲行的所有列都在调试器和 pgweb 界面中填充了数据。在视图中,我查询以获取歌曲列表并希望按如下方式更新它们:

# song.artist_id has an integer value
song.preview_url = preview_url # Just a string
# db.query(Song).filter(Song.id == song.id).update({'preview_url':preview_url}) #Produces same result as above

db.commit()
# song.artist_id becomes None

这会将 preview_url 添加到一行中,但是,一旦我这样做并提交,即使我正在更新完全不同的字段,歌曲实例中的 artist.id 也会变成 None。我可以在调试器和 pgweb 界面中观察到这一点。

更新: 我在对行进行任何更改之前尝试了 db.commit(),它仍然用 None 替换了 song.artist_id。这意味着行更新与此无关,因此它必须是我在 song 上进行的预处理。有什么办法可以在更新和提交行之前摆脱会话中的所有更改?

有人遇到过这种情况吗?我是否必须再次显式设置 artist.id 因为它是外键?

我设法追踪到这种可疑行为。问题确实出在预处理上。

不久,我将 Artist.songs 来自列表中的许多艺术家聚集在一起,后来我从这个列表中弹出,但是我忘记了这个列表是特殊的,即它是 SQLAlchemy 中的 Instrumented Collection,根据文档:

"Instrumentation means that normal operations on the collection are tracked and result in changes being written to the database at flush time. "

通过从这个集合中弹出,我实际上是在删除 Artist-Song 关系,因此外键变成了 None

我的示例代码看起来像这样:

artists = db.query(Artist).all()
all_songs = []
for artist in artists:
    all_songs.append(artist.songs)

all_songs.pop(0) # This would delete the Artist-Song relation after the commit