【问题标题】:Django model constraint for related objects相关对象的 Django 模型约束
【发布时间】:2019-03-19 13:41:13
【问题描述】:

我有以下模型代码:

class Tag(models.Model):
    user = models.ForeignKey('auth.User', on_delete=models.CASCADE)

class Activity(models.Model):
    user = models.ForeignKey('auth.User', on_delete=models.CASCADE)
    tags = models.ManyToManyField(Tag, through='TagBinding')

class TagBinding(models.Model):
    tag = models.ForeignKey(Tag)
    activity = models.ForeignKey(Activity)

我想使用新的 Django 2.2 语法在 TagBinding 模型上编写数据库约束。此约束应检查 TagBinding 模型的 tagactivity 字段是否具有相同的用户。我试图做的事情:

class TagBinding(models.Model):
    tag = models.ForeignKey(Tag)
    activity = models.ForeignKey(Activity)

    class Meta:
        constraints = [
            models.CheckConstraint(
                name='user_equality',
                check=Q(tag__user=F('activity__user')),
            )
        ]

但这不起作用,因为 Django 不允许在 F 函数内使用连接。此外,SubqueryOuterRef 对我不起作用,因为查询中引用的模型未注册。

有没有什么方法可以使用没有原始 SQL 的新语法来实现这个约束?

更新

似乎有些 SQL 后端不支持约束定义中的连接,所以现在的问题是:是否有可能在关系数据库中实现这种行为?

【问题讨论】:

  • 为什么在 db 级别需要这个约束?
  • 因为应尽可能将约束卸载到数据库,而不是在代码中强制执行。但是这种约束是必要的吗?我怀疑它可以通过不同的建模来避免,但我不能完全确定它。
  • @dan-klasson 当然,我可以在代码级别包含此逻辑,但我更愿意将此行为限制在可能的最低级别(特别是如果此行为在逻辑上适合此级别)。 @EndreBoth 我也在寻找另一个模型,但没有找到。我想出的所有解决方案都只是删除模型Tag 并在Activity 模型中定义tags 字段。但它会删除所有可以使用标签存储的其他数据。
  • 建模确实有问题。您已经在 Activity 模型中有 ManyToMany 字段“标签”。这意味着给定的活动可以与许多标签相关联,因此可以与许多不同的用户相关联(假设标签有不同的用户)。在这种情况下,活动中的“用户”属性对我来说似乎没有多大意义。如果你想实现“绑定”,那么你不需要为此手动创建一个单独的表(Django 已经在幕后为你创建了 ManyToMany 字段)。
  • @arudzinska user 字段对于两个模型都是必需的,因为关系不是强制性的。可以有没有标签的活动和没有活动的标签,但它们都应该分配给特定用户。中间模型TagBinding 被添加到示例中,因为它稍后用于约束定义。如果没有任何东西可以添加到中间模型中,则可以省略(如您所说)。

标签: python django django-models


【解决方案1】:

在 Postgres 中,有两种类型的约束(除了唯一和外键约束),CHECK CONSTRAINTS 和 EXCLUDE 约束。

检查约束只能应用于单行。

排除约束只能应用于单个表。

您将无法使用其中任何一个来强制执行您想要的约束,该约束跨越表边界以确保一致性。

您可以改用基于触发器的约束,它可以执行其他查询以验证数据。

例如,您可以在检查用户匹配的各种表上设置 BEFORE INSERT OR UPDATE 触发器。我有一些类似的代码在相同的自关系树代码上运行,以确保父母和孩子都具有相同的“类别”。

在这种情况下,它会有点棘手,因为您需要一些机制来阻止检查,直到所有涉及的表都已更新。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-03-30
    • 2011-08-25
    • 2019-02-15
    • 2021-04-15
    • 1970-01-01
    • 2011-01-15
    • 2012-07-21
    • 1970-01-01
    相关资源
    最近更新 更多