Search code examples
djangodjango-modelsdjango-signals

Django Signals: Can't Use For Loop?


When i remove the for loop in the signals then it works (creates an object properly) but when i use the for loop it should create an object for each post in the Collection object but this doesn't work. It doesn't even create an object of the Collection_List_Item model. Is there a reason why this for loop doesn't work? Is there a way to work around this?

models

class Collection(models.Model):
    posts = models.ManyToManyField(Post, related_name='collection_posts', blank=True)
    author = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
    collection_name = models.CharField(max_length=100)
    collection_description = models.CharField(max_length=1000, blank=True)
    collection_likes = models.ManyToManyField(User, related_name='liked_collections', blank=True)
    collection_image = models.ImageField(upload_to="images/")
    private = models.BooleanField(default=False)
    follows = models.ManyToManyField(User, related_name='collection_follows', blank=True)

    def __str__(self):
        return self.collection_name

class Collection_List_Item(models.Model):
    post = models.ForeignKey(Post, on_delete=models.CASCADE, null=True)
    user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
    collection = models.ForeignKey(Collection, on_delete=models.CASCADE, null=True)
    saved = models.BooleanField(default=False)
    created_date = models.DateTimeField(auto_now_add=True)
    modified_date = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.collection.collection_name

signals:

@receiver(post_save, sender=Collection)
def create_collection_list_item(sender, instance, created, **kwargs):
    if created:
        for i in instance.posts.all():
            collection_list_item = Collection_List_Item.objects.create(collection=instance, user=instance.author, post=i)
        
            collection_list_item.save()

Solution

  • For ManyToManyField fields you have to use m2m_changed (Django Docs) signal.

    Because ManyToManyField are saved after instance is saved and thus there won't be any record at all of the ManyToManyField updates.

    from django.db.models.signals import m2m_changed
    from django.db import IntegrityError
    
    @receiver(m2m_changed, sender=Collection.posts.through)
    def create_collection_list_item(sender, instance, action, *args, **kwargs):
        if action == "post_add":
            for i in instance.posts.all():
                try:
                    collection_list_item = Collection_List_Item.objects.create(collection=instance, user=instance.author, post=i)
                except IntegrityError:
                    pass