无法正确更新SQLAlchemy行

时间:2017-08-01 19:26:15

标签: python sqlalchemy pyramid

我正在使用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,因为它是外键?

1 个答案:

答案 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