Django Unique Together (With Foreign Keys)

Django Unique Together (with foreign keys)

You can't.

The unique_together clause is directly translated to the SQL unique index. And you can only set those on columns of a single table, not a combination of several tables.

You can add validation for it yourself though, simply overwrite the validate_unique method and add this validation to it.

Docs: http://docs.djangoproject.com/en/dev/ref/models/instances/#django.db.models.Model.validate_unique

Django: Is there a way to add a foreign key field to unique_together?

you cannot. database constraints cannot contain those kind of data. you will have to check it programmatically before creating instances or write some validator

Unique together involving multiple foreign keys & a many to many field

Since only few d entities had to have a corresponding price (rest will continue to have a generic common price), I ended up with the following structure.

class PricingTable(models.Model):
a = models.ForeignKey(A, on_delete=models.CASCADE)
price = MoneyField()
b = ArrayField(models.CharField(choices=CHOICES))
c = models.ForeignKey(C, on_delete=models.CASCADE)
d = models.ForeignKey("x.D", on_delete=models.CASCADE, blank=True, null=True)

class Meta:
ordering = ("a",)
unique_together = ("a", "b", "c", "d")

def validate_b(self):
# b can't be empty
if not len(self.b) >= 1:
raise ValueError
# each element in b needs to be unique
if not len(self.b) == len(set(self.b)):
raise ValueError
# each element in b needs to be unique together with a, c & d
query = PricingTable.objects.filter(
a=self.a, c=self.c, d=self.d, b__overlap=self.b
).exclude(pk=self.pk)
if query.count() > 0:
raise ValueError

def save(self, *args, **kwargs):
self.validate_b()
return super().save(*args, **kwargs)

class DBasedPricing(models.Model):
"""
Lookup table that tells (row exists) if we have D based pricing coupled with param A
If we do, query PricingTable.d=d, else PricingTable.d=None for correct pricing
"""
d = models.ForeignKey("x.D", on_delete=models.CASCADE)
a = models.ForeignKey(A, on_delete=models.CASCADE)

class Meta:
unique_together = ("d", "a")

This forces me to first do a lookup based on d parameter, to check if pricing would be D based or not

d_id = None
if DBasedPricing.objects.filter(d_id=input_param.d, a_id=a.id).exists():
d_id = input_param.d

Which then adds another parameter to my usual queries

price_obj = PricingTable.objects.filter(...usual query..., d_id=d_id)

Overall, at the cost of a single simple indexed lookup, I save on rows & of course complex DB structuring. Also, I ended up not having to re-enter all the existing pricing!

Django unique combination of foreign keys

You have to set in the Meta class in the KeyList:

class KeyList(models.Model):
key_one = models.ForeignKey(KeyOne)
key_two = models.ForeignKey(KeyTwo)
key_three = models.ForeignKey(KeyThree)
list = models.CharField()
class Meta:
unique_together = (("key_one", "key_two", "key_three"),)

unique constraint on the basis of foreign model field value django

You can not put unique constraints across the relations. You can only put columns belonging to the current Model only in unique constraints or unique_together. However, you can try the following approach

class C(models.Model):
field1 = models.ForeignKey(A, ..)
field2 = models.ForeginKey(B, ..)

def validate_unique(self, exclude=None):
qs = C.objects.filter(field1=self.field1)
if qs.filter(B__field2=self.field2).exists():
raise ValidationError('Fields must be unique')

models.Model.validate_unique(self, exclude=exclude)

Also, note that this function will not be called automatically while saving the objects of model C, so you will have to call it explicitly.

How to make two foreign keys to same model unique together?

I suggest you to use model.clean method:

class Friendship(models.Model):
person1 = models.ForeignKey(Person, related_name='person1')
person2 = models.ForeignKey(Person, related_name='person2')

def clean(self):
direct = FriendShip.objects.filter(person1 = self.person1, person2 = self.person2)
reverse = FriendShip.objects.filter(person1 = self.person2, person2 = self.person1)

if direct.exists() or reverse.exists():
raise ValidationError({'key':'Message')})


Related Topics



Leave a reply



Submit