2011-07-16 3 views
0

J'ai ce modèle:Django comment créer une relation "super symétrique" sur une auto relation ManyToMany

class People(models.Model): 
    name = models.CharField(max_length=128, db_index=True) 
    friends = models.ManyToManyField('self') 

Alors friends relation est symétrique. Donc, si vous êtes mon ami, je suis votre ami.

Ce que j'aimerais aussi, c'est que tous les amis de mes amis soient automatiquement mes amis. Exemple:

Si A et B sont amis (AB, BA) et que nous ajoutons un nouvel ami C à B, C sera automatiquement ajouté à A (AB, BA, BC, CB, AC, CA). Si nous supprimons C de B, C sera automatiquement retiré de A.

J'ai besoin que cela fonctionne dans une page d'administration normale. Lors de la soumission d'un formulaire, pour un ManyToManyField, Django appelle d'abord clean(), en effaçant toutes les relations liées à l'instance en cours, puis add(), en ajoutant toutes les relations provenant du formulaire.

j'ai pu obtenir le bon comportement lors de l'ajout d'une nouvelle relation avec ce code (mais il ne fonctionne pas lors de la suppression relation):

def add_friends(sender, instance, action, reverse, model, pk_set, **kwargs): 
    if action == 'post_add': 
     if len(pk_set) > 1: 
      pk = pk_set.pop() 
      next = People.objects.get(pk=pk) 
      next.friends.add(*pk_set) 

m2m_changed.connect(add_friends, sender=People.friends.through) 

Lors de la recherche de solutions, j'ai du mal à ne pas créer une boucle infinie.

Répondre

1

J'ai finalement trouvé une solution. Le problème était que je devais effacer toutes les relations d'amis du groupe actuel mais je ne pouvais pas trouver un moyen de le faire sans entrer dans une boucle infinie de signaux. La solution consistait donc à contourner la méthode clear() et à utiliser directement la méthode delete() du gestionnaire. J'utilise finalement la même approche pour les 3 signaux. Voici le code modifié de la fonction add_friends:

def add_friends(sender, instance, action, reverse, model, pk_set, **kwargs): 
    # On clear, clear all indirect relations of this instance 
    if action == 'pre_clear': 
     instance.friends.through.objects.filter(from_people__in=instance.friends.all()).delete() 

    # Delete all relations of the objects in the removed set 
    # (not just the ones related to the instance) 
    elif action == 'post_remove': 
     instance.friends.through.objects.filter(
       Q(from_people__in=pk_set) | Q(to_people__in=pk_set) 
      ).delete() 

    # Clear all relations of People moved from one group to another 
    elif action == 'pre_add' and pk_set: 
     if instance.pk in pk_set: 
      raise ValueError(_(u"You can't add self as a friend.")) 
     instance.friends.through.objects.filter(
       (Q(from_people__in=pk_set) & ~Q(to_people=instance.pk)) | 
       (Q(to_people__in=pk_set) & ~Q(from_people=instance.pk)) 
      ).delete() 

    # Add all indirect relations of this instance 
    elif action == 'post_add' and pk_set: 
     manager = instance.friends.through.objects 
     # Get all the pk pairs 
     pk_set.add(instance.pk) 
     pk_set.update(instance.friends.all().values_list('pk', flat=True)) 
     pairs = set(permutations(pk_set, 2)) 
     # Get the pairs already in the DB 
     vals = manager.values_list('from_people', 'to_people') 
     vals = vals.filter(from_people__in=pk_set, to_people__in=pk_set) 
     # Keep only pairs that are not in DB 
     pairs = pairs - set(vals) 

     # Add them 
     for from_pk, to_pk in pairs: 
      manager.create(from_people_id=from_pk, to_people_id=to_pk) 

m2m_changed.connect(add_friends, sender=People.friends.through) 
Questions connexes