Reputation: 2086
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
Upvotes: 6
Views: 1446
Reputation: 5344
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.
Upvotes: 4