2013-03-05 6 views
6

Ho un utente condivide modello simile di seguito:Django unico personalizzato vincolo insieme

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")) 

voglio salvare un singolo oggetto per sharer (S) e ricevitore (R) (ordine non fa questioni RS o SR). ma sopra unique_together non adempierà questo; Supponiamo che R-S sia nel database e quindi se salvi S-R non otterrò la convalida per questo. Per questo ho scritto convalida unica personalizzata per il modello Share.

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) 

Questo metodo funziona correttamente nell'uso normale.

Problema: Ho un algoritmo di matching che ottiene le richieste di un ricevitore una quota di e e salva Share oggetto (sia S-R o R-S) poi inviare loro risposta (oggetto di condivisione) quasi contemporaneamente. Siccome sto controllando la duplicazione con la query (nessun livello di database) ci vuole tempo, quindi alla fine ho 2 oggetti S-R e R-S.

Voglio una soluzione per questo che per un condivisore S e un ricevitore R Posso solo salvare un singolo oggetto di condivisione, S-R o R-S altrimenti ottenere qualche errore di convalida (come IntegrityError di databse).

Django = 1.4, Database = Postgresql

risposta

4

Probabilmente potrebbe risolvere questo con PostgreSQL di indexes on expressions ma qui è un altro modo:

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) 

Ma ovviamente non funzionerebbe se si modificano i valori con QuerySet.update metodo . Puoi anche guardare django-denorm, risolve questo con i trigger.

+0

Grazie della risposta. Ho avuto 2 soluzioni. Uno per gestirlo con un numero di richiesta univoco (durante la corrispondenza nell'ID incrementato della cache Redis) e salvare il primo obj di condivisione della richiesta inferiore #, quindi attendere e ottenere share_obj per la richiesta più elevata #. ma l'ho risolto con la seconda soluzione (descritta da voi) per ordinare la lista di nomi utente di entrambi condivisori e ricevitori, in questo modo otterrò sempre lo stesso patter di condivisioni e ricevitori. Salverò la richiesta e se ottieni IntegrityError di postgresql, eseguo il rollback della transazione e Safley ottiene già l'oggetto S-R salvato. :) –