austinheiman
austinheiman

Reputation: 1008

Need to check ForeignKey references a specific object before saving

I am new to Django (and Python in general) and have a Model validation question.

I have 2 models, team and game. 2 teams play in a game, and the winner field can be blank/null:

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")

I need to ensure that the winner is either the away_team or the home_team, not just any team. I created the check_winner_played method, but I have no idea how to call that on .save(). This will only be in the admin section, so I dont think I should be using Form validation. In my mind, it seems like the validation should be done at the Model level.

Also, if there are any things in here that make you stop and say 'this guy has no idea what he is doing.' let me know. Looking for any pointers I can get. Specifically, am I validating the week IntegerField max value correctly?

Upvotes: 0

Views: 95

Answers (2)

Johannes Reichard
Johannes Reichard

Reputation: 947

I would advise to choose another way in determining the winner of the team, since you have more than one state but winning the game. It could also be an equal score.

The information who won the game is actually indirectly determined by the score of the teams, so try to model the winning game with a property that uses the score to return the winner.

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

This will eliminate your need for additional validation. If you really want to use validators you should read the django docs about validators first: https://docs.djangoproject.com/en/1.11/ref/validators/

Upvotes: 1

Simar
Simar

Reputation: 2505

Try this, it should raise an error - if winner is not blank and its not either the away_team or home_team. Notice its overriding the save method so this validation will be done on 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)

Since you're rasing a ValidationError you will need to import ValidationError in your models.py (at the top):

from django.core.exceptions import ValidationError

Upvotes: 0

Related Questions