Search code examples
pythondjangodjango-signals

How to prevent Duplicate Signals in Django


I have added a notification where every time a user submits a Like button to a post, the owner of the liked post receives a notification.

I have set Value options for each Like button to be Like or Unlike I have restricted the notification to be sent only if the Value of the like clicked is Like only.

So, now in the Like Model I have added signal and a condition that if the Like.value== Like the signal activates but for some reason it is sent twice only when a new post is created and a new user clicks like for the very first time, afterwards if he unike and likes again it is sent once.

My question is:

Why is the signal duplicated everytime a new user clicks the Like button? How to fix it?

Here is the post models.py:

class Post(models.Model):
    title = models.CharField(max_length=100, unique=True)
    likes = models.ManyToManyField(User, related_name='liked', blank=True)

here is the Like model.py:

LIKE_CHOICES = (
    ('Like', 'Like'),
    ('Unlike', 'Unlike')
)


class Like(models.Model):
    # To know Who liked
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    post = models.ForeignKey(Post, on_delete=models.CASCADE)
    value = models.CharField(choices=LIKE_CHOICES, default='Like', max_length=8)
    updated = models.DateTimeField(auto_now=True)
    created = models.DateTimeField(auto_now=True)

    def __str__(self):
        return f"{self.post}-{self.user}-{self.value}"

    def user_liked_post(sender, instance, *args, **kwargs):
        like = instance
        if like.value=='Like':
            post = like.post
            sender = like.user
            notify = Notification(post=post, sender=sender, user=post.author, notification_type=1)
            notify.save()


    def user_unlike_post(sender, instance, *args, **kwargs):
        like = instance
        post = like.post
        sender = like.user
        notify = Notification.objects.filter(post=post, sender=sender, user=post.author, notification_type=1)
        notify.delete()


# Likes
post_save.connect(Like.user_liked_post, sender=Like)
post_delete.connect(Like.user_unlike_post, sender=Like)

Here is the views.py:

def ShowNotifications(request):
    user=request.user
    notifications= Notification.objects.filter(user=user).order_by('-date')
    template= loader.get_template('notifications/notifications.html')

    context= {
        'notifications':notifications
    }
    return HttpResponse(template.render(context, request))

Solution

  • You can try this way:

    from django.db import models
    from django.db.models.signals import post_save, post_delete
    from django.contrib.auth.models import User
    from django.dispatch import receiver
    
    # Create your models here.
    class Post(models.Model):
        title = models.CharField(max_length=100, unique=True)
        author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='post_author')
    
        def __str__(self):
            return self.title
    
    class Like(models.Model):
        user = models.ForeignKey(User, on_delete=models.CASCADE)
        post = models.ForeignKey(Post, on_delete=models.CASCADE)
        created = models.DateTimeField(auto_now_add=True)
    
        def __str__(self):
            return f"{self.post.title}@_{self.user}"
    
    @receiver(post_save, sender=Like)
    def user_liked_post(sender, instance, created, *args, **kwargs):
        try:
            if created:
                notify = Notification(
                    post=instance.post,
                    sender=instance.user,
                    user=instance.post.author,
                    notification_type=1
                )
                notify.save()
        except Exception as e:
            print('Error sending liked notification!')
            print(e)
    
    @receiver(post_delete, sender=Like)
    def user_unlike_post(sender, instance, *args, **kwargs):
        try:
            notify = Notification.objects.filter(
                post=instance.post,
                sender=instance.user,
            )
            notify.delete()
        except Exception as e:
            print('Error deleting liked notification!')
            print(e)
    

    Since you are deleting the liked record then the status of the record will never be unlike and the updated field is also not going to update. So remove the update and value fields from Like model.

    And in below line you are fetching the author from Post model but there is no author field in Post model.

    notify = Notification(post=post, sender=sender, user=post.author, notification_type=1)