我有一个表,其中主键(id)不是我区分记录的键。为此,我对 3 列有一个唯一的约束。 为了能够合并记录,我添加了一个类方法,用于检索相关记录(如果存在),否则返回一条新记录。
class Foo(Base):
__table_args__ = (sa.UniqueConstraint('bar', 'baz', 'qux'),)
id = sa.Column(Identifier, sa.Sequence('%s_id_seq' % __tablename__), nullable=False, primary_key=True)
bar = sa.Column(sa.BigInteger)
baz = sa.Column(sa.BigInteger)
qux = sa.Column(sa.BigInteger)
a1 = sa.Column(sa.BigInteger)
a2 = sa.Column(sa.BigInteger)
@classmethod
def get(cls, bar=None, baz=None, qux=None, **kwargs):
item = session.query(cls).\
filter(cls.bar== bar).\
filter(cls.baz == baz).\
filter(cls.qux == qux).\
first()
if item:
for k, v in kwargs.iteritems():
if getattr(item, k) != v:
setattr(item, k, v)
else:
item = cls(bar=bar, baz=baz, qux=qux, **kwargs)
return item
这在大多数情况下都有效,但每隔一段时间,我在尝试合并项目时会收到完整性错误:
foo = Foo.get(**item)
session.merge(foo)
据我了解,发生这种情况是因为合并尝试插入一条记录,其中已存在具有唯一字段的记录。
get
函数有问题吗?我在这里缺少什么?
(顺便说一句:我意识到这可能看起来很尴尬,但我需要一个唯一的顺序ID,并且为了避免数据库不支持非主键列上的序列的问题,我这样做了)
编辑 1: 将 orm.db 更改为 session,这样示例会更清晰
编辑 2: 我有这个系统在多个平台上运行,似乎这只发生在 Ubuntu 之上的 mysql 中(其他平台是 RedHat 之上的 Oracle)。此外,以某种奇怪的方式,这种情况更多地发生在特定的最终用户身上。 关于mysql,我尝试了mysql和mysql+mysqldb作为连接字符串,但都产生了这个错误。 对于最终用户来说,这是没有意义的,我不知道该怎么做...... 关于mysql,
最佳答案
事实上,您的方法很容易出现完整性错误。发生的情况是,当您调用 Foo.get(1,2,3) 2 次时,您不会刷新其间的 session 。第二次调用它时,ORM 查询再次失败 - 因为数据库中还没有实际行 - 并且创建了一个具有不同标识的新对象。然后在提交/刷新时这两个冲突导致完整性错误。这可以通过在每次对象创建后刷新数据库来避免。
现在,如果在合并/ORM get 中已知主键,则 SQLAlchemy 中的工作方式会有所不同 - 如果发现匹配的主键已在 session 中加载,SQLAlchemy 意识到这两个必须是同一对象。但是,不会对唯一索引进行此类检查。或许也可以这样做。然而,这只会使竞争条件变得更加罕见,因为可能有 2 个 session 同时创建相同的(bar、baz、qux)三元组。
TL;博士:
else:
item = cls(bar=bar, baz=baz, qux=qux, **kwargs)
session.add(item)
session.flush()
关于python - sqlalchemy 完整性错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18030857/