How to fix: TypeError: QuerySet.annotate() received non-expression(s): eqmunir

18,324

Solution 1

I don't quite understand what do you want to do. If you want to check if the user liked at least one post, you can do it like this:

liked_or_not = Like.objects.filter(user=request.user).exists

If you want to check if a user liked a specific post, you can dot it like this:

liked_or_not = Likes.objects.filter(post_id=post_id, user=request.user).exists()

annotate has a different purpose. It annotates each object in the QuerySet with the provided list of query expressions. An expression may be a simple value, a reference to a field on the model (or any related models), or an aggregate expression (averages, sums, etc.) that has been computed over the objects that are related to the objects in the QuerySet. read more here https://docs.djangoproject.com/en/2.2/ref/models/querysets/#annotate

Solution 2

For those coming here for other possibilities in debugging the error message, I had made a mistake and entered in a query analogous to

Like.objects.filter(user=request.user).values('status', flat=True)
# instead of the correct
Like.objects.filter(user=request.user).values_list('status', flat=True)

Solution 3

I encounterd similar problem and solved using .annotate
and below is my definition of Like in models.py

class Like(models.Model):
    user = models.ForeignKey(
        "users.User", on_delete=models.CASCADE, related_name="likes"
    )
    writing = models.ForeignKey(
        "writings.Writing", on_delete=models.CASCADE, related_name="likes"
    )

in views.py

    filter_kwargs = {}
    filter_kwargs["writing"] = OuterRef("pk")
    filter_kwargs["user"] = request_user
    subquery = Like.objects.filter(**filter_kwargs)
    writing = (
        Writing.objects.annotate(user_likes=Exists(subquery))
        .get(id=id)
        )
Share:
18,324
djangoninja
Author by

djangoninja

Updated on June 04, 2022

Comments

  • djangoninja
    djangoninja almost 2 years

    I am adding a like functionality in my website where users can like each others posts.

    I have done this successfully, however have one issue. This is checking whether the user has already liked the post, which has to be performed specifically in my HOME view.

    This is so I can render my home page. To encounter this issue, I perform a .annotate() on my posts when retrieving them, and see if a user has liked a post.

    I then pass this onto my home template and check if the user exists within the posts likes property.

    Here's the related code.

    models.py:

    class Post(models.Model):
        file = models.ImageField(upload_to='images/')
        summary = models.TextField(max_length=600)
        pub_date = models.DateTimeField(auto_now=True)
        user = models.ForeignKey(User, on_delete=models.CASCADE)
        likes = models.ManyToManyField(User, through='Like', related_name='likes')
    
        def __str__(self):
            return self.user.username
    
        def pub_date_pretty(self):
            return self.pub_date.strftime('%b %e %Y')
    
        def summary_pretty(self):
            return self.summary[:50]
    
        @property
        def total_likes(self):
            return self.likes.count()
    
    class Like(models.Model):
        status = models.BooleanField()
        post = models.ForeignKey(Post, on_delete=models.CASCADE)
        user = models.ForeignKey(User, on_delete=models.CASCADE)
    

    views.py:

    def home(request):
        posts = Post.objects.all()
        liked_or_not = Post.objects.annotate(likes__user=request.user)
        return render(request, 'posts/home.html', {'posts': posts, 'liked_or_not': liked_or_not})
    

    home.html:

    {% if liked_or_not == True %}
          <a href="javascript:{document.getElementById('likepost{{ post.id }}').submit()}"><button class="btn btn-primary btn-lg btn-block"><span class="oi oi-caret-top"></span> Unlike {{ post.total_likes }} </button></a>
    {% else %}
          <a href="javascript:{document.getElementById('likepost{{ post.id }}').submit()}"><button class="btn btn-primary btn-lg btn-block"><span class="oi oi-caret-top"></span> Like {{ post.total_likes }} </button></a>
    {% endif %}
    <form id="likepost{{ post.id }}" method="POST" action="{% url 'likepost' post.id %}">
      {% csrf_token%}
      <input type="hidden">
    </form>