Here is my model:
class GroupedModels(models.Model):
other_model_one = models.ForeignKey('app.other_model')
other_model_two = models.ForeignKey('app.other_model')
Essentially, what I want is is for other_model
to be unique in this table. That means that if there is a record where other_model_one
id is 123
, I should not allow another record to be created with other_model_two
id as 123
. I can override clean
I guess but I was wondering if django has something built in.
I am using version 2.2.5 with PSQL.
Edit: This is not a unqiue together situation. If I add a record with other_model_one_id=1
and other other_model_two_id=2
, I should not be able to add another record with other_model_one_id=2
and other other_model_two_id=1
I explain several options here, maybe one of them or a combination can be useful for you.
save
Your constraint is a business rule, you can override save
method to keep data consistent:
class GroupedModels(models.Model):
# ...
def clean(self):
if (self.other_model_one.pk == self.other_model_two.pk):
raise ValidationError({'other_model_one':'Some message'})
if (self.other_model_one.pk < self.other_model_two.pk):
#switching models
self.other_model_one, self.other_model_two = self.other_model_two, self.other_model_one
# ...
def save(self, *args, **kwargs):
self.clean()
super(GroupedModels, self).save(*args, **kwargs)
I put a sample easy to understand. Let's suppose this scenario:
class BasketballMatch(models.Model):
local = models.ForeignKey('app.team')
visitor = models.ForeignKey('app.team')
Now, you want to avoid a team playing a match with itself also team A only can play with team B for once (almost your rules). You can redesign your models as:
class BasketballMatch(models.Model):
HOME = 'H'
GUEST = 'G'
ROLES = [
(HOME, 'Home'),
(GUEST, 'Guest'),
]
match_id = models.IntegerField()
role = models.CharField(max_length=1, choices=ROLES)
player = models.ForeignKey('app.other_model')
class Meta:
unique_together = [ ( 'match_id', 'role', ) ,
( 'match_id', 'player',) , ]
ManyToManyField.symmetrical
This look like a symetrical issue, django can handle it for you. Instead of create GroupedModels
model, just make a ManyToManyField field with itself on OtherModel
:
from django.db import models
class OtherModel(models.Model):
...
grouped_models = models.ManyToManyField("self")
This is what django has as built in for these scenarios.
It's not a very satisfying answer, but unfortunately the truth is there is no way to do what you're describing with a simple built-in feature.
What you described with clean
would work, but you have to be careful to manually call it as I think it's only automatically called when using ModelForm. You might be able to create a complex database constraint but that would live outside of Django and you'd have to handle database exceptions (which can be difficult in Django when in the middle of a transaction).
Maybe there's a better way to structure the data?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With