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

2024-02-26

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

Models:

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,我预计此交互式会话会导致 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()
>>> 

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

(紧接着,同一会话)

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

So u.sz_shirt_dress_sleeve wasn't准确地表示关联表的状态。 ...好的。但我需要它。事实上,我确实知道如果我尝试添加另一个就会失败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) 关系列表可能无法准确反映其所负责的关联表的状态。 3) 的UniqueConstraint只要我继续在单独的事务中与关系进行交互(由 session.commit() 打断),就可以工作。

问题:1)、2) 或 3) 是否错误?如何防止我的关系列表中出现重复的项目里面同一个交易?


这三点都是正确的。 3)应具备的资格:UniqueConstraint始终有效,因为您的数据库永远不会不一致;它只是没有给你一个错误除非您要添加的关系已被刷新。

发生这种情况的根本原因是 SQL 中的关联表与其在 SQLAlchemy 中的表示形式之间的阻抗不匹配。 SQL 中的表是multiset元组,所以这样UNIQUE约束,你的LinkUserSizeShirtDressSleeve表是一个set of (size_id, user_id)元组。另一方面,SQLAlchemy 中关系的默认表示是有序的list对象,但它对维护此列表的方式以及它期望您与此列表交互的方式施加了一些限制,因此它的行为更像是set在某些方面。特别是,它会默默地忽略关联表中的重复条目(如果您碰巧没有UNIQUE约束),并且它假设您从一开始就没有将重复的对象添加到此列表中。 https://groups.google.com/d/msg/sqlalchemy/2myGWqEg8LY/RnZgJxeABWEJ

如果这对您来说是个问题,只需使用以下命令使行为更符合 SQLcollection_class=set关于你们的关系。如果您希望在向关系中添加重复条目时引发错误,请基于以下内容创建自定义集合类:set重复添加失败。在我的一些项目中,我采用了猴子修补的方法relationship要设置的构造函数collection_class=set on all我的人际关系,以使其不那么冗长。

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

class UniqueSet(set):
    def add(self, el):
        if el in self:
            raise ValueError("Value already exists")
        super().add(el)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么关系中的重复项不会违反 UniqueConstraint? 的相关文章

随机推荐