Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django custom unique together constraint

I have a users share model something like below:

class Share( models.Model ):
    sharer = models.ForeignKey(User, verbose_name=_("Sharer"), related_name='sharer')
    receiver = models.ForeignKey(User, verbose_name=_("Receiver"), related_name='receiver')

    class Meta:
        unique_together = ( ("sharer", "receiver"), ("receiver", "sharer") )

I want to save a single object for sharer(S) and receiver(R) (order doesn't matters R-S or S-R). but above unique_together will not fulfil this; Suppose R-S is in database and then if I save S-R I will not get validation for this. For this I have written custom unique validation for Share model.

    def validate_unique(
        self, *args, **kwargs):
            super(Share, self).validate_unique(*args, **kwargs)
            if self.__class__.objects.filter( Q(sharer=self.receiver, receiver=self.sharer) ).exists():
                raise ValidationError(
                    {
                        NON_FIELD_ERRORS:
                        ('Share with same sharer and receiver already exists.',)
                    }
                )

    def save(self, *args, **kwargs):
        # custom unique validate
        self.validate_unique()
        super(Share, self).save(*args, **kwargs)

This method works fine in normal use.

Problem: I have an matching algorithm which gets a share's and a receiver's requests and saves Share object(either S-R or R-S) then send them response(share object) at almost same time. As I am checking duplication with query(no database level) it takes time, so at the end I have 2 Objects S-R and R-S.

I want some solution for this that for a sharer S and a receiver R I can only save single share object, either S-R or R-S else get some validation error(like IntegrityError of databse).

Django=1.4, Database=Postgresql

like image 463
Zubair Afzal Avatar asked Mar 05 '13 09:03

Zubair Afzal


People also ask

What is unique_together in Django?

Sets of field names that, taken together, must be unique: unique_together = (("driver", "restaurant"),) This is a tuple of tuples that must be unique when considered together. It's used in the Django admin and is enforced at the database level (i.e., the appropriate UNIQUE statements are included in the CREATE TABLE statement).

How many examples are there of uniqueconstraint in Django?

The following are 14 code examples of django.db.models.UniqueConstraint () . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example.

What is a unique tuple in Django?

This is a tuple of tuples that must be unique when considered together. It's used in the Django admin and is enforced at the database level (i.e., the appropriate UNIQUE statements are included in the CREATE TABLE statement). Show activity on this post.

How can a registered user subscribe to a game in Django?

So, an registered user can subscribe to a game by adding a name, team, game, and his/her user. However, the user should only be able to add his account once to an game, which would be a second unique constrain Is it possible to give in Django two unique constraints to the model?


1 Answers

You probably could solve this with postgresql's indexes on expressions but here is another way:

class Share( models.Model ):
    sharer = models.ForeignKey(User)
    receiver = models.ForeignKey(User), related_name='receiver')
    key = models.CharField(max_length=64, unique=True)

    def save(self, *args, **kwargs):
        self.key = "{}.{}".format(*sorted([self.sharer_id, self.receiver_id]))
        super(Share, self).save(*args, **kwargs)

But it obviously wouldn't work if you change values with QuerySet.update method. You also could look at django-denorm, it solves this with triggers.

like image 55
alex vasi Avatar answered Nov 15 '22 04:11

alex vasi