假设我有一组用户,每个用户都可以访问一组工具。同一个工具可能有许多具有访问权限的用户,因此这是一个多对多关系:
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, unique=True)
tools = db.relationship("Tool", secondary=user_tool_assoc_table,
back_populates='users')
class Tool(db.Model):
__tablename__ = 'tool'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, unique=False)
user_tool_assoc_table = db.Table('user_tool', db.Model.metadata,
db.Column('user', db.Integer, db.ForeignKey('user.id')),
db.Column('tool', db.Integer, db.ForeignKey('tool.id')))
请注意,用户名是唯一的,但工具名不是。所以
User.name:Mike1
和 User.name:Mike2
可能可以访问 Tool.name:Hammer
,并且分别地, User.name:John1
和 User.name:John2
可能可以访问同名的 Tool.name:Hammer
,但每个都有不同的 Tool.ids
。我想做一个约束,在
User.tools
集合中永远不会有一个与另一个 同名的工具,即Tool
,则用户无法创建新的 Mike1
作为其集合的一部分。 Hammer
无法创建名为 tools
的新工具,该工具构成其 Tool
集合的一部分。 tools
不能附加到用户的 Hammer
集合中,如果集合中已经存在同名用户,即 John1 的 Hammer
不能与 Mike1 共享,因为 Mike1 已经有他自己的 James
。 Hammer
可以创建一个新的 Hammer
,因为他还没有锤子。然后,数据库中将有 3 个名为 Users
的工具,每个工具都有一组不同的 Tool
。 User
才会存在 ojit_code ,但我也不知道如何在我的数据库中确保这一点。 这是否可以通过 SQLalchemy 本地自动配置我的数据库以保持完整性?我不想编写自己的验证器规则,因为我可能会遗漏一些东西并最终得到一个违反我规则的数据库。
最佳答案
问题是如何表达谓词“由 ID 标识的用户只有一个名为 NAME 的工具”。这当然很容易用一个简单的表格来表达,例如:
db.Table('user_toolname',
db.Column('user', db.Integer, db.ForeignKey('user.id'), primary_key=True),
db.Column('toolname', db.String, primary_key=True))
很明显,仅凭这一点还不足以维护完整性,因为用户的工具名称与实际工具之间没有联系。您的数据库可能会声明用户既有锤子又有没有锤子。
最好在您的
user_tool_assoc_table
或等效内容中强制执行此操作,但由于 Tool.name
不是 Tool
的主键的一部分,因此您无法引用它。另一方面,由于您确实希望允许多个具有相同名称的工具共存,因此子集 { id, name } 实际上是 Tool
的正确键:class Tool(db.Model):
__tablename__ = 'tool'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String, primary_key=True)
id
现在充当具有相同名称的工具之间的各种“鉴别器”。请注意, id
在此模型中不必全局唯一,而是在 name
本地。让它仍然自动递增很方便,但是 autoincrement='auto'
的默认设置仅将单列整数主键视为默认具有自动递增行为,因此必须明确设置。现在也可以根据
user_tool_assoc_table
定义 tool_name
,附加约束是用户只能拥有一个具有给定名称的工具:user_tool_assoc_table = db.Table(
'user_tool',
db.Column('user', db.Integer, db.ForeignKey('user.id')),
db.Column('tool', db.Integer),
db.Column('name', db.String),
db.ForeignKeyConstraint(['tool', 'name'],
['tool.id', 'tool.name']),
db.UniqueConstraint('user', 'name'))
使用此模型和以下设置:
john = User(name='John')
mark = User(name='Mark')
db.session.add_all([john, mark])
hammer1 = Tool(name='Hammer')
hammer2 = Tool(name='Hammer')
db.session.add_all([hammer1, hammer2])
db.session.commit()
这将成功:
john.tools.append(hammer1)
hammer2.users.append(mark)
db.session.commit()
这将在上述之后失败,因为它违反了唯一约束:
john.tools.append(hammer2)
db.session.commit()