保存前需要检查 ForeignKey 引用特定对象

Posted

技术标签:

【中文标题】保存前需要检查 ForeignKey 引用特定对象【英文标题】:Need to check ForeignKey references a specific object before saving 【发布时间】:2014-11-05 14:56:45 【问题描述】:

我是 Django(和一般的 Python)新手,并且有一个模型验证问题。

我有 2 个模型,团队和游戏。 2支球队参加比赛,获胜者字段可以为空白/空:

class Team(models.Model):
    abbreviation = models.CharField(max_length=5, unique=True)
    location = models.CharField(max_length=100)
    nickname = models.CharField(max_length=100)

class Game(models.Model):
    away_team = models.ForeignKey(Team, related_name='away_games')
    away_score = models.PositiveSmallIntegerField(blank=True, null=True)
    home_team = models.ForeignKey(Team, related_name='home_games')
    home_score = models.PositiveSmallIntegerField(blank=True, null=True)
    week = models.PositiveSmallIntegerField(validators=[MaxValueValidator(21)])
    winner = models.ForeignKey(Team, blank=True, null=True)

    def check_winner_played(self):
        if not (winner and (winner.id == away_team.id or winner.id == home_team.id)):
            # raise error("Winner did not compete")

我需要确保获胜者是 away_teamhome_team,而不仅仅是任何团队。我创建了check_winner_played 方法,但我不知道如何在.save() 上调用它。这只会在管理部分,所以我认为我不应该使用表单验证。在我看来,验证似乎应该在模型级别完成。

另外,如果这里有任何事情让你停下来说“这家伙不知道他在做什么。”让我知道。寻找我能得到的任何指示。具体来说,我是否正确验证了week IntegerField 最大值?

【问题讨论】:

【参考方案1】:

试试这个,它应该会引发错误 - 如果获胜者不是空白并且它不是 away_team 或 home_team。 注意它覆盖了 save 方法,所以这个验证将在 Game.save() 上完成

class Team(models.Model):
        abbreviation = models.CharField(max_length=5, unique=True)
        location = models.CharField(max_length=100)
        nickname = models.CharField(max_length=100)

class Game(models.Model):
    away_team = models.ForeignKey(Team, related_name='away_games')
    away_score = models.PositiveSmallIntegerField(blank=True, null=True)
    home_team = models.ForeignKey(Team, related_name='home_games')
    home_score = models.PositiveSmallIntegerField(blank=True, null=True)
    week = models.PositiveSmallIntegerField(validators=[MaxValueValidator(21)])
    winner = models.ForeignKey(Team, blank=True, null=True)

    def save(self, *args, **kwargs):
        if not (self.winner and (self.winner == self.away_team or self.winner == self.home_team)):
              raise ValidationError('Winner Error')
        super(Post, self).save(*args, **kwargs)

由于您要引发 ValidationError,因此您需要在您的 models.py(在顶部)中导入 ValidationError:

from django.core.exceptions import ValidationError

【讨论】:

所以这个解决方案有效,django 抛出一个验证错误,代码为“获胜者错误”。但是当我尝试通过管理站点提交有效的获胜者时,我收到一个未定义帖子的名称错误。我在models.py 中尝试了from django.http import HttpRequest, HttpResponse,但仍然抛出错误NameError。我需要导入什么以及在哪里导入? 在您的代码中。替换:super(Post, self).save(*args, **kwargs)super(Game, self).save(*args, **kwargs) 太棒了,谢谢。现在它允许其中一支参赛球队作为获胜者提交。如果我提交了一个没有参加比赛的球队,那么我会抛出获胜者错误。现在有一个文档部分介绍如何在表单上将该错误作为错误条目抛出(只是闪烁红色错误消息)而不是显示带有堆栈跟踪的 Django 错误页面? 尝试返回一个简单的布尔表达式,而不是引发验证错误。所以,你可以写 return False 而不是 raise ValidationError('Winner Error'),然后在 super(Game, self).save(*args, **kwargs) 行之后添加 return True。这样,当使用 Game.save() 它应该返回一个布尔变量 - 你可以做类似if Game.save():【参考方案2】:

我建议选择另一种方式来确定团队的获胜者, 因为你有不止一个状态但赢得了比赛。也可以是相等的分数。

赢得比赛的信息实际上是由球队的得分间接决定的,因此请尝试使用使用得分返回获胜者的属性来模拟获胜游戏。

class Game(models.Model):
    away_team = models.ForeignKey(Team, related_name='away_games')
    away_score = models.PositiveSmallIntegerField(blank=True, null=True)
    home_team = models.ForeignKey(Team, related_name='home_games')
    home_score = models.PositiveSmallIntegerField(blank=True, null=True)
    week = models.PositiveSmallIntegerField(validators=[MaxValueValidator(21)])

    @property
    def winner():
        if self.away_score > self.home_score:
            return self.away_team
        elif self.away_score < self.home_score:
            return self.home_team
        else:
            return None

这将消除您进行额外验证的需要。如果你真的想使用验证器,你应该先阅读关于验证器的 django 文档:https://docs.djangoproject.com/en/1.11/ref/validators/

【讨论】:

以上是关于保存前需要检查 ForeignKey 引用特定对象的主要内容,如果未能解决你的问题,请参考以下文章

如何检查是否从django admin调用了save()

有没有办法只引用 Django ForeignKey 中的某些字段?

Django - 保存前 VS 保存后 VS 查看保存

用保存前已存在的对象交换对象

保存前创建对象的iOS模式?

Django orm总结