假设我有一组用户,每个用户都可以访问一组工具。同一个工具可能有许多具有访问权限的用户,因此这是一个多对多关系:

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:Mike1User.name:Mike2 可能可以访问 Tool.name:Hammer ,并且分别地, User.name:John1User.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()
    

    10-07 23:04