Search code examples
pythondjangodjango-signals

Restricting Signal Notifications in a Django Project to specific Attribute in Model


I have added a notification everytime 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 so now I am trying to restrict 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 not working and I am not sure why.

My question is:

How do I set the notification signal to be sent only when the Like.value== Like?

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 models.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):
        if Like.value=='Like':
            like = instance
            post = like.post
            sender = like.user
            notify = Notification(post=post, sender=sender, user=post.author, notification_type=1)
            notify.save()
        else:
            None

    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 notifications models.py

class Notification(models.Model):
    NOTIFICATION_TYPES=((1,'Like'),(2,'Comment'),(3,'Follow'))

    post = models.ForeignKey('blog.Post', on_delete=models.CASCADE, related_name="noti_post", blank=True, null=True)
    sender = models.ForeignKey(User, on_delete=models.CASCADE, related_name="noti_from_user")
    user = models.ForeignKey(User, on_delete=models.CASCADE,related_name="noti_to_user")
    notification_type= models.IntegerField(choices=NOTIFICATION_TYPES)

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

  • Within the post_save handler you compare as following if Like.value=='Like':

    But Like is a class, of which value is a Field, thus the compare is never True.

        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()
    

    Another thing that I notice, of what I'm not sure what the impact is. def user_liked_post is a method on the model. Thus the first argument it expects is self. But the post save handler is using it as a classmethod. But because of that sender will probably be cls, thus Like.