2012-07-27 17 views
6

necesito para detectar una señal post_remove, por lo que he escrito:Una señal m2m_changed y error con post_remove

def handler1(sender, instance, action, reverse, model, pk_set, **kwargs): 
if (action == 'post_remove'): 
    test1() # not declared but make a bug if it works, to detect :) 

m2m_changed.connect(handler1, sender=Course.subscribed.through) 

Si cambio 'post_remove' por 'post_add' que está bien .. ¿Es el insecto de un django sobre post_remove ??

uso ese modelo y me cambio beetween dos valores de 'suscrito' (por lo que uno y uno añade suprimido)

class Course(models.Model): 
    name = models.CharField(max_length=30) 
    subscribed = models.ManyToManyField(User, related_name='course_list', blank=True, null=True, limit_choices_to={'userprofile__status': 'student'}) 

he visto un post con un error de Django, tal vez no ha estado arreglado ... (o soy yo ^^)

Respuesta

5

Según tengo entendido, no es un error, es solo que Django no actualiza las relaciones m2m en la forma esperada. No elimina las relaciones que se eliminarán y luego agrega las nuevas. En cambio, borra todas las relaciones m2m y luego las agrega de nuevo.

Hay una pregunta relacionada Django signal m2m_changed not triggered que enlaza al ticket 13087.

Así se puede comprobar por la acción pre_clearpost_clear o con la señal m2m_changed, pero ya que esas acciones no proporcionan pk_set, no ayuda a encontrar las entradas relacionadas antes de la operación de guardar, ya que queríamos hacer en your other question.

+0

realmente no sé cómo hacer que necesito hacer ... No puedo usar señales, ya sea .save() sobrescribir ... Necesito eliminar un usuario de un campo de modelo (m2m) si se lo quita del campo de otro modelo (m2m) ... – nlassaux

+4

I ' No estoy seguro del mejor enfoque. Puede intentar almacenar los objetos relacionados en la instancia con la señal 'pre_save', p. 'instance._old_m2m = list (instance.subscribed.values_list ('pk', flat = True))'. Luego, en su controlador para la señal 'post_add', compare' pk_set' con 'instance._old_m2m'. ¡Buena suerte! – Alasdair

+0

Sí, lo he pensado, pero no está realmente optimizado:/Voy a buscar ... – nlassaux

6

Gracias a Alasdairscomment Encontré la solución y la publicaré aquí - tal vez alguien podría usarla.

models.py

class Team(models.Model): 
    name = models.CharField(max_length=100) 
    members = models.ManyToManyField(User) 

pre_save.connect(team_pre_save, sender=Team) 
m2m_changed.connect(team_members_changed, sender=Team.members.through) 

signals.py

def team_pre_save(sender, instance, **kwargs): 
    if instance.pk: 
     instance._old_m2m = set(list(instance.members.values_list('pk', flat=True))) 
    else: 
     instance._old_m2m = set(list()) 

def team_members_changed(sender, instance, **kwargs): 
    if kwargs['action'] == "post_clear": 
     # remove all users from group 
     group = Group.objects.get(name='some group') 
     for member in instance._old_m2m: 
      user = User.objects.get(pk=member) 
      user.groups.remove(group) 

    if kwargs['action'] == "post_add": 
     added_members = list(kwargs['pk_set'].difference(instance._old_m2m)) 
     deleted_members = list(instance._old_m2m.difference(kwargs['pk_set'])) 

     if added_members or deleted_members: 
      # we got a change - do something, for example add them to a group? 
      group = Group.objects.get(name='some group') 

      for member in added_members: 
       user = User.objects.get(pk=member) 
       user.groups.add(group) 

      for member in deleted_members: 
       user = User.objects.get(pk=member) 
       user.groups.remove(group) 
1

tuve llegar a una conclusión después de mucho, mucho tiempo o seaching En primer lugar mi problema: He tenido algunos cómo actualizar una atribuir de mi modelo para establecerlo False cuando mi m2m está vacía, y verdadero si tiene al menos 1 elemento, entonces, la verdad funciona, pero cuando intento "pre_remove" o "post_remove" nunca es desencadenado, así que después de algunos intentos con algunos ejemplos diferentes vi algo extraño en este "pre_clear", cada vez que cambio mi m2m esto siempre tiene los últimos valores, así que logro forzar eliminar estos valores de mi m2m y de esta manera activa el pre_remove y post_remove, por lo que este funciona para mí, ver debajo el código

así que ahora puedo configurar automaticamente ativo Verdadero o Falso basado en mi M2M

class Servico(BaseMixin): 
    descricao = models.CharField(max_length=50) 

#This inheritance from User of django that has is_active boolean field 
class UsuarioRM(Usuario): 
    servicos = models.ManyToManyField(Servico,related_name='servicos_usuario', blank=True) 

# SIGNALS 
from django.db.models import signals 
from django.db.models.signals import m2m_changed 

def usuariorm_servicos_changed(sender, **kwargs): 
    action = kwargs.pop('action', None) 
    pk_set = kwargs.pop('pk_set', None) 
    instance = kwargs.pop('instance', None) 

    if action == "pre_clear": 
     if instance.servicos.all(): 
     servicos = instance.servicos.all() 
      for servico in servicos: 
       instance.servicos.remove(servico) 
      instance.save() 
    else: 
     instance.is_active = False 
     instance.save() 
     if action == "post_add": 
      if pk_set: 
      instance.is_active = True 
     else: 
      instance.is_active = False 

     instance.save() 

m2m_changed.connect(usuariorm_servicos_changed, sender=UsuarioRM.servicos.through) 
Cuestiones relacionadas