将两个相同的实体添加到会话两次时,如何防止IntegrityError?

时间:2017-01-26 13:11:11

标签: python sqlalchemy

下面的(可执行)代码会导致IntegrityError,因为具有相同PK的实体会被添加两次(隐式)到会话。会话不知道实体表示相同的对象(相同的PK)并触发两个INSERT语句。我的印象是会话应该自动检测到。我意识到这两个实体都是瞬态/分离的,我需要执行merge来获取托管实例。但这是不可避免的吗?

关于示例代码的一些注释:

  • 展示手头的问题非常简单。我的真实代码要复杂得多。
  • 需要注意的一点是,主要实体是使用“build_data”函数“构建”的,该函数没有任何对会话的引用。会话是在应用程序的更高级别创建的。
  • UserArticle的概念仅供参考。实际上,它是不同的商业实体。我在这里用“article”和“user”替换它们,因为它是一个众所周知的概念。
  • 在创建articlearticle2之间发生了很多其他事情,创建了一个相当复杂的数据结构。我还不知道哪一行首先发生,因为涉及非确定性循环(通过字典键)。

我可以通过以下方式解决这个问题:

  • 根据需要将会话传递到build_data功能和merging,或
  • 保留对实例的手动引用,仅创建一次

我想知道的是:我可以避免上述两种方法来保持代码简单吗?

以下是相关代码:

from sqlalchemy import (
    create_engine,
    Column,
    ForeignKeyConstraint,
    Unicode,
)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import (
    relationship,
    sessionmaker)

Base = declarative_base()


class User(Base):
    __tablename__ = 'user'
    name = Column(Unicode, nullable=False, primary_key=True)


class Article(Base):
    __tablename__ = 'article'
    __table_args__ = (
        ForeignKeyConstraint(
            ['user_'],
            ['user.name'],
            ondelete='CASCADE',
            onupdate='CASCADE'),
    )
    user_ = Column(Unicode, nullable=False, primary_key=True,
                   )
    title = Column(Unicode, nullable=False, primary_key=True)
    content = Column(Unicode)

    user = relationship(User, backref='articles')


# Prepare the session
Session = sessionmaker()
engine = create_engine('sqlite:///:memory:', echo=True)
Session.configure(bind=engine)
Base.metadata.create_all(engine)


# --- The main code -----------------------------------------------------------
def build_data():
    user = User(name='JDoe')
    article = Article(user=user, title='Hello World', content='Foobar')
    print(article)

    # More stuff is happening here.

    article2 = Article(user=user, title='Hello World', content='Foobar')
    print(article2)
    return user

session = Session()
entity = build_data()
session.add(entity)
session.flush()
# -----------------------------------------------------------------------------

0 个答案:

没有答案