Home > OS >  Django Conditional ORM Query
Django Conditional ORM Query

Time:03-17

I am creating a Blog website where for each blog a user can like or dislike the post. Now every time user goes on Index page I want to change the like button i.e if user has liked the post then a dislike button otherwise like button. For this I need to get the variable IsLiked from the views.py file.

This is the Query that I have written.

posts =  Post.objects.exclude(users=request.user).select_related('user__people','ProductAvailability').prefetch_related('images_set').annotate(comments_Count = Count('comments_post',distinct=True)).annotate(Count('Likes',distinct=True)).all().order_by('-id')

for post in posts:
        if(post.Likes.filter(id=user_id).exists()):
            isLiked = True
        else:
            isLiked = False

Here the problem is that for every post there is a separate query sent to DB.

This is my Blog Post Model ->

class Post(models.Model):
    user = models.ForeignKey(User, on_delete=models.PROTECT)
    # category = models.ForeignKey(Category, on_delete=models.PROTECT)
    ProductAvailability = models.ForeignKey(ProductAvailability, on_delete=models.PROTECT, null=True, blank=True)
    title = models.CharField(max_length=255,null=True)   
    description = models.CharField(max_length=1000,null=True)
    Likes = models.ManyToManyField(to=User, related_name='Post_likes')
    favourites = models.ManyToManyField(to=User,blank=True,related_name="favourite")
    Tag1 = models.CharField(max_length=255,null=True,blank=True)
    Tag2 = models.CharField(max_length=255,null=True,blank=True)
    Tag3 = models.CharField(max_length = 255, null = True, blank = True)
    Tag1_Name = models.CharField(max_length=255,null=True,blank=True)
    Tag2_Name = models.CharField(max_length=255,null=True,blank=True)
    Tag3_Name = models.CharField(max_length=255,null=True,blank=True)
    users = models.ManyToManyField(User, related_name='users_hidden_from_post')
    Created_date = models.DateTimeField(auto_now_add=True)
    Updated_date = models.DateTimeField(auto_now=True)

PS: Please Ignore the redundant Info in the Post Model

I want to send the User id with the Query and check if individual post is liked by the user or not.

CodePudding user response:

You can annotate the Posts with a condition is_liked that checks if such User appears in the through model of the likes of the Post with an Exists subquery [Django-doc]:

from django.db.models import Exists, OuterRef

posts = Post.objects.exclude(
    users=request.user
).select_related(
'user__people', 'ProductAvailability'
).prefetch_related(
    'images_set'
).annotate(
    comments_Count = Count('comments_post',distinct=True),
    Count('Likes',distinct=True),
    is_liked=Exists(
        Post.Likes.through.objects.filter(
            post_id=OuterRef('pk'), user_id=user_id
        )
    )
).order_by('-id')

The Post objects that arise from this queryset will have an extra attribute named .is_liked that is True if the user_id appears in the Likes for that Post.


Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.


Note: normally the name of the fields in a Django model are written in snake_case, not PascalCase, so it should be: likes instead of Likes.

  • Related