我正在使用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)
我填充了数据库,可以看到Song行的所有列都填充了调试器中的数据以及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()
,但仍然会将song.artist_id
替换为None
。这意味着行更新与此无关,因此它必须是我在song
上进行的预处理。在更新和提交行之前,有什么方法可以摆脱会话中的所有更改?
有没有人遇到过这种行为?我是否必须再次明确设置artist.id,因为它是外键?
答案 0 :(得分:0)
我设法追查这种可疑的行为。问题确实在于预处理。
很快,我在一个列表中聚集了许多艺术家的Artist.songs
,后来我从这个列表中弹出,但是我忘记的是这个列表很特别,即它是SQLAlchemy中的一个Instrumented Collection,根据到文档:
“检测意味着跟踪对集合的正常操作,并导致在刷新时将更改写入数据库。”
通过从此集合中弹出,我实际上正在删除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