如何防止SQLAlchemy一对多关系中子对象被重新创建?

1 投票
1 回答
887 浏览
提问于 2025-04-16 21:19

我正在使用 Python 2.6.6 和 SQLAlchemy 0.6.6 来处理我数据库中的一对多关系,但我不太确定怎么才能防止 SQLAlchemy 在已有相似数据的情况下添加新的子记录。

数据库代码:

from sqlalchemy import *
from sqlalchemy.orm import backref, relationship, sessionmaker, create_session
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

# Parent table, no foreign key.
class Author(Base):
    __tablename__ = 'authors'

    id = Column(Integer, primary_key=True)
    username = Column(String)
    author_metadata = relationship('AuthorMetadata', backref='author')

# Child table, many records with same author_id.
class AuthorMetadata(Base):
    __tablename__ = 'author_metadata'

    id = Column(Integer, primary_key=True)
    author_id = Column(Integer, ForeignKey('authors.id'))
    metakey = Column(String)
    metavalue = Column(Text)

示例脚本:

if __name__ == '__main__':
    engine = create_engine('database_details', pool_recycle=90)
    session = create_session(bind=engine)

    author = session.query(Author).filter_by(username='Godfrey').first()
    if not author:
        author = Author()
    author.username = 'Godfrey'
    author.author_metadata = [
        AuthorMetadata(metakey='location', metavalue='New York'),
        AuthorMetadata(metakey='posts', metavalue='5')]
    session.add(author)
    session.flush()

第一次运行示例脚本时,数据库中会出现以下内容(这是预期的结果):

dev=# select id from authors where username = 'Godfrey';
  id  
------
 5025
(1 row)

dev=# select id, author_id, metakey, metavalue from author_metadata order by id desc limit 2;
  id   | author_id | metakey  | metavalue 
-------+-----------+----------+-----------
 85090 |      5025 | posts    | 5
 85089 |      5025 | location | New York
(2 rows)

但是如果我再次运行这个示例脚本,你会发现已有的元数据记录的作者 ID 被设置为 null,并且插入了新的记录:

dev=# select id, author_id, metakey, metavalue from author_metadata order by id desc limit 4;
  id   | author_id | metakey  | metavalue 
-------+-----------+----------+-----------
 85092 |      5025 | posts    | 5
 85091 |      5025 | location | New York
 85090 |           | posts    | 5
 85089 |           | location | New York
(4 rows)

我对此并不感到惊讶,但我在想有没有什么好的方法可以告诉 SQLAlchemy,只有在新的元数据列表与现有列表不同的情况下,它才应该插入、修改或删除作者的元数据行。

1 个回答

2

你可以明确检查列表里的内容,只有在新添加的 AuthorMetadata 对象不存在时才添加它们,而不是删除整个集合然后重新创建一遍。这样至少可以避免丢失之前创建的记录。

你的使用场景很适合 attribute_mapped_collectionassociation_proxy,所以你可能想选择其中一个来使用。

撰写回答