I have a model Run
with two ForeignKey
s, signup
and report
,
class Run(models.Model):
signup = models.ForeignKey(Signup, on_delete=models.CASCADE, related_name="runs")
report = models.ForeignKey(Report, on_delete=models.CASCADE, related_name="runs")
kind = ...
pointing to models, which in turn are related to another model, Training
,
class Report(models.Model):
training = models.OneToOneField(
Training, on_delete=models.CASCADE, primary_key=True
)
cash_at_start = ....
class Signup(models.Model):
training = models.ForeignKey(
Training, on_delete=models.CASCADE, related_name="signups"
)
participant = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="signups"
)
When creating a Run
I would like to make sure, that it's signup
and report
are for the same Training
, i.e. that report.training == signup.training
.
What is this kind of validation called? And how would I achieve it?
Also, I am happy to learn other ways to implement this, if another database structure would be better.
Here are the docs that describe the validation process on models.
Please note that this validation usually only happens with ModelForms
(when calling form.is_valid()
). Manually creating an object and using save()
doesn't trigger this validation if you don't call model_instance.full_clean()
method. It's aimed at the user, not the developer.
According to the mentioned docs, my suggestion is to use the clean
method:
class Run(models.Model):
signup = models.ForeignKey(Signup, on_delete=models.CASCADE, related_name="runs")
report = models.ForeignKey(Report, on_delete=models.CASCADE, related_name="runs")
def clean(self):
if self.signup.training != self.report.training:
# Note: adding an error code is best practice :)
raise ValidationError('some message', code='some_error_code')
EDIT #1: Not using ModelForm
You don't have to use the ModelForm
to have validation on the model. Let's say you obtained Signup
and Report
instances from somewhere. Then you can instantiate and validate your Run
instance like this:
run = Run(signup=signup, report=report)
# Raises ValidationErrors
run.full_clean()
# If all is fine, call save
run.save()