简体   繁体   中英

Django undirected unique-together

I want to model pair-wise relations between all members of a set.

class Match(models.Model):

    foo_a = models.ForeignKey(Foo, related_name='foo_a')
    foo_b = models.ForeignKey(Foo, related_name='foo_b')

    relation_value = models.IntegerField(default=0)

    class Meta:
        unique_together = ('ingredient_a', 'ingredient_b')

When I add a pair AB, it successfully prevents me from adding AB again, but does not prevent me from adding BA.

I tried following, but to no avail.

unique_together = (('ingredient_a', 'ingredient_b'), ('ingredient_b', 'ingredient_a'))

Edit: I need the relationship_value to be unique for every pair of items

If you define a model like what you defined, its not just a ForeignKey, its called a ManyToMany Relation.

In the django docs, it is explicitly defined that unique together constraint cannot be included for a ManyToMany Relation.

From the docs,

A ManyToManyField cannot be included in unique_together. (It's not clear what that would even mean!) If you need to validate uniqueness related to a ManyToManyField, try using a signal or an explicit through model.

EDIT

After lot of search and some trial and errors and finally I think I have found a solution for your scenario. Yes, as you said, the present schema is not as trivial as we all think. In this context, Many to many relation is not the discussion we need to forward. The solution is, (or what I think the solution is) model clean method :

class Match(models.Model):
    foo_a = models.ForeignKey(Foo, related_name='foo_a')
    foo_b = models.ForeignKey(Foo, related_name='foo_b')

    def clean(self):
        a_to_b = Foo.objects.filter(foo_a = self.foo_a, foo_b = self.foo_b)
        b_to_a = Foo.objects.filter(foo_a = self.foo_b, foo_b = self.foo_a) 

        if a_to_b.exists() or b_to_a.exists():
            raise ValidationError({'Exception':'Error_Message')})

For more details about model clean method, refer the docs here...

I've overridden the save method of the object to save 2 pairs every time. If the user wants to add a pair AB, a record BA with the same parameters is automatically added.

Note: This solution affects the querying speed. For my project, it is not an issue, but it needs to be considered.

 def save(self, *args, **kwargs): if not Match.objects.filter(foo_a=self.foo_a, foo_b=self.foo_b).exists(): super(Match, self).save(*args, **kwargs) if not Match.objects.filter(foo_a=self.foo_b, foo_b=self.foo_a).exists(): Match.objects.create(foo_a=self.foo_b, foo_b=self.foo_a, bar=self.bar) 

EDIT: Update and remove methods need to be overridden too of course.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM