Python 在向会话添加同一实体两次时,如何防止IntegrityError?

Python 在向会话添加同一实体两次时,如何防止IntegrityError?,python,sqlalchemy,Python,Sqlalchemy,下面的(可执行)代码会导致IntegrityError,因为具有相同PK的实体会两次(隐式)添加到会话中。会话不知道实体代表相同的对象(相同的主键),并触发两条INSERT语句。我的印象是,会话应该自动检测到这一点。我意识到这两个实体都是暂时的/分离的,我需要执行合并,以获得托管实例。但这不是可以避免的吗 关于示例代码的一些注释: 为了演示手头的问题,它被大大简化了。我拥有的真正代码要复杂得多 需要注意的一点是,主实体是使用函数“build_data”构建的,该函数没有任何会话引用。会话是在应

下面的(可执行)代码会导致
IntegrityError
,因为具有相同PK的实体会两次(隐式)添加到会话中。会话不知道实体代表相同的对象(相同的主键),并触发两条
INSERT
语句。我的印象是,会话应该自动检测到这一点。我意识到这两个实体都是暂时的/分离的,我需要执行
合并
,以获得托管实例。但这不是可以避免的吗

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

  • 为了演示手头的问题,它被大大简化了。我拥有的真正代码要复杂得多
  • 需要注意的一点是,主实体是使用函数“build_data”构建的,该函数没有任何会话引用。会话是在应用程序的更高级别上创建的
  • User
    Article
    的概念仅用于说明。实际上,这是不同的商业实体。我在这里用“文章”和“用户”取代了它们,因为这是一个众所周知的概念
  • 在创建
    article
    article2
    之间,发生了许多其他事情,创建了一个相当复杂的数据结构。我也不知道哪一行首先发生,因为涉及到非确定性循环(通过字典键)
我可以通过以下两种方式解决这个问题:

  • 将会话传递给
    生成数据
    功能,并根据需要进行
    合并
    ,或
  • 保留对实例的手动引用,并且只创建一次
我想知道的是:我可以避免上述两种方法以保持代码简单吗?

下面是有问题的代码:

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()
# -----------------------------------------------------------------------------