2015-04-16 5 views
1

Lorsque vous utilisez un signal Django comme post_save vous pouvez l'empêcher de tirer lorsqu'un objet est d'abord créé en faisant quelque chose comme:Empêcher m2m_changed de tir lors de la création d'un objet

@receiver(post_save,sender=MyModel) 
def my_signal(sender, instance, created,**kwargs): 
    if not created: 
     pass # Do nothing, as the item is new. 
    else: 
     logger.INFO("The item changed - %s"%(instance)) 

Cependant, les relations ManyToMany sont appliquées après un article est initialement créé, so no such argument is passed in, ce qui rend difficile à supprimer dans ces cas.

@receiver(m2m_changed,sender=MyModel.somerelation.though) 
def my_signal(sender, instance, created,**kwargs): 
    if __something__: # What goes here? 
     pass # Do nothing, as the item is new. 
    else: 
     logger.INFO("The item changed - %s"%(instance)) 

est-il un moyen facile de supprimer un signal m2m_changed lorsque son être fait sur un objet qui vient d'être créé?

Répondre

1

Je pense qu'il n'y a pas de moyen facile de le faire. Comme l'indique le Django doc, vous ne pouvez pas associer un élément à une relation tant qu'elle n'a pas été enregistrée. Exemple de la doc:

>>> a1 = Article(headline='...') 
>>> a1.publications.add(p1) 
Traceback (most recent call last): 
... 
ValueError: 'Article' instance needs to have a primary key value before a many-to-many relationship can be used. 

# should save Article first 
>>> a1.save() 
# the below statement never know it's just following a creation or not 
>>> a1.publications.add(p1) 

Il est logiquement impossible pour un enregistrement de relation de savoir s'il est ajouté à « un élément vient de créer » ou « un élément qui existe déjà depuis un certain temps », sans information externe.

Quelques solutions de contournement Je suis arrivé avec:

Solution 1. ajouter un DateTimeField dans MyModel pour indiquer le temps de création. Le gestionnaire m2m_changed utilise l'heure de création pour vérifier quand l'élément est créé. Cela fonctionne pratiquement dans certains cas, mais ne peut garantir l'exactitude

Solution 2. ajouter un attribut 'created' dans MyModel, soit dans un gestionnaire post_save ou dans d'autres codes. Exemple:

@receiver(post_save, sender=Pizza) 
def pizza_listener(sender, instance, created, **kwargs): 
    instance.created = created 

@receiver(m2m_changed, sender=Pizza.toppings.through) 
def topping_listener(sender, instance, action, **kwargs): 
    if action != 'post_add': 
     # as example, only handle post_add action here 
     return 
    if getattr(instance, 'created', False): 
     print 'toppings added to freshly created Pizza' 
    else: 
     print 'toppings added to modified Pizza' 
    instance.created = False 

Démo:

p1 = Pizza.objects.create(name='Pizza1') 
p1.toppings.add(Topping.objects.create()) 
>>> toppings added to freshly created Pizza 
p1.toppings.add(Topping.objects.create()) 
>>> toppings added to modified Pizza 

p2 = Pizza.objects.create(name='Pizza2') 
p2.name = 'Pizza2-1' 
p2.save() 
p2.toppings.add(Topping.objects.create()) 
>>> toppings added to modified Pizza 

Mais soyez prudent en utilisant cette solution. Depuis l'attribut « créé » a été attribué à l'instance Python, pas enregistré dans le DB, les choses peuvent mal tourner comme:

p3 = Pizza.objects.create(name='Pizza3') 
p3_1 = Pizza.objects.get(name='Pizza3') 
p3_1.toppings.add(Topping.objects.create()) 
>>> toppings added to modified Pizza 
p3.toppings.add(Topping.objects.create()) 
>>> toppings added to freshly created Pizza 

C'est tout au sujet de la réponse. Ensuite, vous a attrapé ici! Je suis zhang-z du groupe github django-notifications :)

+0

Hé mec, c'est à peu près ce que j'ai dû faire, donc je l'ai marqué correctement. J'ajouterai une réponse séparée à ce que j'ai réellement fait. –

0

La réponse de ZZY m'a fondamentalement aidé à réaliser que ce n'était pas possible sans stocker des champs supplémentaires. Heureusement, j'utilise django-model-utils qui inclut un TimeStampedModel qui inclut un champ created.

En fournissant un delta assez petit, il était relativement facile de vérifier le temps créé lors de la capture du signal.

@receiver(m2m_changed,sender=MyModel.somerelation.though) 
def my_signal(sender, instance, created,**kwargs): 
    if action in ['post_add','post_remove','post_clear']: 
     created = instance.created >= timezone.now() - datetime.timedelta(seconds=5) 
     if created: 
      logger.INFO("The item changed - %s"%(instance))