Django模型:如何避免在使用来自同一个表的2个外键时引用相同的记录

Posted

技术标签:

【中文标题】Django模型:如何避免在使用来自同一个表的2个外键时引用相同的记录【英文标题】:Django model: How to avoid referencing the same record when using with 2 foreign keys from the same table 【发布时间】:2020-10-21 05:26:54 【问题描述】:

我有两个看起来像这样的 Django 模型:

class Team(models.Model):
    name = models.CharField(max_length=30)
    ...


class Game(models.Model):
    teamA = models.ForeignKey(Team, on_delete=CASCADE, related_name='teamA')
    teamB = models.ForeignKey(Team, on_delete=CASCADE, related_name='teamB')
    ....

Game 模型有 2 个指向 Team 模型的外键。

当然,一个团队不可能与自己对战,所以我想防止teamA和teamB引用DB中的相同记录。

所以你不能创建这样的游戏:

Game.objects.create(teamA=ChicagoBulls, teamB=ChicagoBulls)

最好的方法是什么?

【问题讨论】:

【参考方案1】:

您可以在.clean() method [Django-doc] 中实现约束,也可以在数据库级别使用CheckConstraint [Django-doc] 实现约束(但并非所有数据库都会强制执行此操作):

from django.core.exceptions import ValidationError
from django.db.models import F, Q

class Game(models.Model):
    teamA = models.ForeignKey(Team, on_delete=CASCADE, related_name='teamA')
    teamB = models.ForeignKey(Team, on_delete=CASCADE, related_name='teamB')
    # …
    
    def clean(self, *args, **kwargs):
        if self.teamA_id == self.teamB_id:
            raise ValidationError('The two teams should be different')
        super().clean(*args, **kwargs)

    class Meta:
        constraints = [
            models.CheckConstraint(
                check=~Q(teamA=F('teamB')),
                name='different_teams'
            )
        ]

.clean() 方法在您使用模型层创建对象时检查。但是,当您通过ModelForm [Django-doc] 创建Game 时,它会得到验证。

您可能还想让Teamname 唯一,这样就不能有两个同名的团队:

class Team(models.Model):
    name = models.CharField(max_length=30, unique=True)
    # …

【讨论】:

以上是关于Django模型:如何避免在使用来自同一个表的2个外键时引用相同的记录的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Django 中构建模型?

Django - 使用表单集在不通过表的情况下建立 2 个模型之间的多对多关系

Django Python - 如何在一个 HTML 模板中显示来自不同表的信息

django模型

Django/Python DRY:使用 Q 对象和模型属性时如何避免重复代码

从 django admin list_display 访问,来自一对一表的值