gpt4 book ai didi

python - 为什么关系中的重复项不违反 UniqueConstraint?

转载 作者:太空宇宙 更新时间:2023-11-04 04:42:34 28 4
gpt4 key购买 nike

使用以下模型,为什么以下交互在同一事务中成功地向关系添加重复关联?我预计(并且需要)它会因关联表上放置的 UniqueConstraint 而失败。

模型:

from app import db # this is the access to SQLAlchemy
class User(db.Model):

id = db.Column(db.Integer, primary_key=True)

sz_shirt_dress_sleeve = db.relationship(
'SizeKeyShirtDressSleeve',
secondary=LinkUserSizeShirtDressSleeve,
backref=db.backref('users', lazy='dynamic'),
order_by="asc(SizeKeyShirtDressSleeve.id)")

class SizeKeyShirtDressSleeve(db.Model):
id = db.Column(db.Integer, primary_key=True)
size = db.Column(db.Integer)

def __repr__(self):
return 'Dress shirt sleeve size: %r' % self.size

LinkUserSizeShirtDressSleeve = db.Table(
'link_user_size_shirt_dress_sleeve',
db.Column(
'size_id',
db.Integer,
db.ForeignKey('size_key_shirt_dress_sleeve.id'), primary_key=True),
db.Column(
'user_id',
db.Integer,
db.ForeignKey('user.id'), primary_key=True),
db.UniqueConstraint('size_id', 'user_id', name='uq_association')
)

由于关联表上的 UniqueConstraint,我预计此交互式 session 会导致 IntegrityError。它不会,并且允许我将相同的尺寸关联两次:

>>> from app.models import User, SizeKeyShirtDressSleeve
>>> db.session.add(User(id=8))
>>> db.session.commit()
>>> u = User.query.filter(User.id==8).one()
>>> u
<User id: 8, email: None, password_hash: None>
>>> u.sz_shirt_dress_sleeve
[]
>>> should_cause_error = SizeKeyShirtDressSleeve.query.first()
>>> should_cause_error
Dress shirt sleeve size: 3000
>>> u.sz_shirt_dress_sleeve.append(should_cause_error)
>>> u.sz_shirt_dress_sleeve.append(should_cause_error)
>>> u.sz_shirt_dress_sleeve
[Dress shirt sleeve size: 3000, Dress shirt sleeve size: 3000]
>>> db.session.commit()
>>>

等等,什么?该关系不代表我的关联表中的内容吗?我想我应该验证一下:

(紧接着,同一 session )

>>> from app.models import LinkUserSizeShirtDressSleeve as Sleeve
>>> db.session.query(Sleeve).filter(Sleeve.c.user_id==8).all()
[(1, 8)]
>>>

所以 u.sz_shirt_dress_sleeve 没有准确地表示关联表的状态。 ...好的。但我需要它。事实上,我确实知道如果我尝试将另一个 should_cause_error 对象添加到关系中,它将失败:

>>> u.sz_shirt_dress_sleeve.append(should_cause_error)
>>> db.session.commit()
# huge stack trace
sqlalchemy.exc.IntegrityError: (sqlite3.IntegrityError) UNIQUE constraint failed: link_user_size_shirt_dress_sleeve.size_id, link_user_size_shirt_dress_sleeve.user_id [SQL: 'INSERT INTO link_user_size_shirt_dress_sleeve (size_id, user_id) VALUES (?, ?)'] [parameters: (1, 8)] (Background on this error at: http://sqlalche.me/e/gkpj)
>>>

太棒了!好的,所以我推断的是:1) 关系列表中可能有重复项。2) 关系表有可能不能准确反射(reflect)它所负责的关联表的状态。3) UniqueConstraint 工作......只要我继续与单独事务中的关系交互(由 session.commit() 打断)。

问题:1)、2) 或 3) 不正确吗?以及如何防止在我的关系列表中出现重复项目在同一交易中?

最佳答案

这三件事都是正确的。 3) 应该是合格的:UniqueConstraint 始终在您的数据库永远不会不一致的意义上起作用;它不会给你一个错误,除非你添加的关系已经被刷新。

发生这种情况的根本原因是 SQL 中的关联表与其在 SQLAlchemy 中的表示形式之间的阻抗不匹配。 SQL 中的表是元组的多重集,因此使用UNIQUE 约束,您的LinkUserSizeShirtDressSleeve 表是一个 (size_id, user_id) 元组。另一方面,SQLAlchemy 中关系的默认表示是对象的有序列表,但它对维护此列表的方式以及它希望您与此列表交互的方式施加了一些限制,所以它在某些方面更像一个 set。特别是,它会默默地忽略关联表中的重复条目(如果您碰巧没有 UNIQUE 约束),and it assumes that you never add duplicate objects to this list in the first place.

如果这对您来说是个问题,只需在您的关系中使用 collection_class=set 使行为更符合 SQL。如果您希望在将重复条目添加到关系中时引发错误,请创建一个基于 set 的自定义集合类,该类在重复添加时失败。在我的一些项目中,我使用猴子修补 relationship 构造函数来为我的所有关系设置collection_class=set这不那么冗长。

下面是我如何创建这样一个自定义集合类:

class UniqueSet(set):
def add(self, el):
if el in self:
raise ValueError("Value already exists")
super().add(el)

关于python - 为什么关系中的重复项不违反 UniqueConstraint?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50335507/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com