Weird Django error when adding annotations annotations

Question:

I am trying to add comments to my django forum, but am running into an issue with annotate. whenever I add instructions to add comments com=Comment.objects.order_by("-published_date") it gives me an issue with a compleatly unrelated part of my template that checks the username.

here is my view:

def index(request):
    # check if there is any incomming data (comments) and make sure the user is authenticated
    POST = request.POST
    if POST != {} and request.user.is_authenticated:
        # a comment has been recived, time to move forward with creating it

        # figure out if the post even exists
        get_object_or_404(Post, id = POST['post_id'])

        # grab the user object
        user_active = User.objects.get(id = POST['user'])

        # grab the post object
        post_active = Post.objects.get(id = POST['post_id'])

        # make and save the comment
        n_comment = Comment(user = user_active, comment = POST['comment'], post = post_active)
        n_comment.save()

    posts = Post.objects.order_by("-published_date").annotate(num_likes=Count("likes"),com=Comment.objects.order_by("-published_date"))
    return render(request, 'home.html', {'posts': posts})

here is my model:

class Comment(models.Model):
    comment = models.CharField(max_length=500)
    post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='comments')
    published_date = models.DateTimeField('date_published')

and here is the error:

Environment:


Request Method: GET
Request URL: http://localhost:8000/

Django Version: 3.2.18
Python Version: 3.6.9
Installed Applications:
['core',
 'django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'accounts']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']


Template error:
In template /vagrant/templates/base.html, error at line 12
   subquery must return only one column
LINE 1: ...d_by_id", COUNT("core_like"."id") AS "num_likes", (SELECT "c...
                                                             ^

   2 : <title>{% block title %}{% endblock %} - Pelican</title>
   3 : 
   4 : {%load static%}
   5 : 
   6 : <link rel='stylesheet' href="{% static 'core/style.css' %}">
   7 : 
   8 : <nav>
   9 :   <h1>Pelican</h1>
   10 :   <ul>
   11 :     {% if user.is_authenticated %}
   12 :       <li><span>{{ u ser.username }}</span>
    13 :       <li><a href="{% url 'logout' %}">Log Out</a>
   14 :     {% else %}
   15 :       <li><a href="{% url 'signup' %}">Sign Up</a>
   16 :       <li><a href="{% url 'login' %}">Log In</a>
   17 :     {% endif %}
   18 :   </ul>
   19 : </nav>
   20 : <section class="content">
   21 :   <header>
   22 :     {% block header %}{% endblock %}


Traceback (most recent call last):
  File "/vagrant/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)

The above exception (subquery must return only one column
LINE 1: ...d_by_id", COUNT("core_like"."id") AS "num_likes", (SELECT "c...
                                                             ^
) was the direct cause of the following exception:
  File "/vagrant/venv/lib/python3.6/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/vagrant/venv/lib/python3.6/site-packages/django/core/handlers/base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/vagrant/venv/lib/python3.6/site-packages/django/contrib/auth/decorators.py", line 21, in _wrapped_view
    return view_func(request, *args, **kwargs)
  File "/vagrant/core/views.py", line 33, in index
    return render(request, 'home.html', {'posts': posts})
  File "/vagrant/venv/lib/python3.6/site-packages/django/shortcuts.py", line 19, in render
    content = loader.render_to_string(template_name, context, request, using=using)
  File "/vagrant/venv/lib/python3.6/site-packages/django/template/loader.py", line 62, in render_to_string
    return template.render(context, request)
  File "/vagrant/venv/lib/python3.6/site-packages/django/template/backends/django.py", line 61, in render
    return self.template.render(context)
  File "/vagrant/venv/lib/python3.6/site-packages/django/template/base.py", line 170, in render
    return self._render(context)
  File "/vagrant/venv/lib/python3.6/site-packages/django/template/base.py", line 162, in _render
    return self.nodelist.render(context)
  File "/vagrant/venv/lib/python3.6/site-packages/django/template/base.py", line 938, in render
    bit = node.render_annotated(context)
  File "/vagrant/venv/lib/python3.6/site-packages/django/template/base.py", line 905, in render_annotated
    return self.render(context)
  File "/vagrant/venv/lib/python3.6/site-packages/django/template/loader_tags.py", line 150, in render
    return compiled_parent._render(context)
  File "/vagrant/venv/lib/python3.6/site-packages/django/template/base.py", line 162, in _render
    return self.nodelist.render(context)
  File "/vagrant/venv/lib/python3.6/site-packages/django/template/base.py", line 938, in render
    bit = node.render_annotated(context)
  File "/vagrant/venv/lib/python3.6/site-packages/django/template/base.py", line 905, in render_annotated
    return self.render(context)
  File "/vagrant/venv/lib/python3.6/site-packages/django/template/loader_tags.py", line 62, in render
    result = block.nodelist.render(context)
  File "/vagrant/venv/lib/python3.6/site-packages/django/template/base.py", line 938, in render
    bit = node.render_annotated(context)
  File "/vagrant/venv/lib/python3.6/site-packages/django/template/base.py", line 905, in render_annotated
    return self.render(context)
  File "/vagrant/venv/lib/python3.6/site-packages/django/template/defaulttags.py", line 171, in render
    len_values = len(values)
  File "/vagrant/venv/lib/python3.6/site-packages/django/db/models/query.py", line 262, in __len__
    self._fetch_all()
  File "/vagrant/venv/lib/python3.6/site-packages/django/db/models/query.py", line 1324, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
  File "/vagrant/venv/lib/python3.6/site-packages/django/db/models/query.py", line 51, in __iter__
    results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
  File "/vagrant/venv/lib/python3.6/site-packages/django/db/models/sql/compiler.py", line 1175, in execute_sql
    cursor.execute(sql, params)
  File "/vagrant/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 98, in execute
    return super().execute(sql, params)
  File "/vagrant/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 66, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "/vagrant/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 75, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "/vagrant/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
  File "/vagrant/venv/lib/python3.6/site-packages/django/db/utils.py", line 90, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/vagrant/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)

Exception Type: ProgrammingError at /
Exception Value: subquery must return only one column
LINE 1: ...d_by_id", COUNT("core_like"."id") AS "num_likes", (SELECT "c...
                                                             ^


the weird thing is, whenever I remove the annotation everything works. right now the template is very basic, it should only show the comment objects.

Asked By: caleballen44

||

Answers:

You don’t use annotations for this. As the error says, a Subquery can, at most, return a single item.

You can prefetch the comments in an ordered manner, so:

from django.db.models import Count, Prefetch
from django.shortcuts import redirect


def index(request):
    # check if there is any incomming data (comments) and make sure the user is authenticated
    POST = request.POST
    if request.method == 'POST' and request.user.is_authenticated:
        # a comment has been recived, time to move forward with creating it

        # figure out if the post even exists
        active_post = get_object_or_404(Post, id=POST['post_id'])

        # make and save the comment
        n_comment = Comment(
            user=request.user, comment=POST['comment'], post=post_active
        )
        n_comment.save()
        return redirect('name-of-some-view')

    posts = (
        Post.objects.order_by('-published_date')
        .prefetch_related(
            Prefetch('comments', Comment.objects.order_by('-published_data'))
        )
        .annotate(num_likes=Count('likes'))
    )
    return render(request, 'home.html', {'posts': posts})

In the template you render this with:

{% for post in posts %}
    …
    {% for comment in post.comments.all %}
    {% endfor %}
{% endfor %}

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: In case of a successful POST request, you should make a redirect
[Django-doc]

to implement the Post/Redirect/Get pattern [wiki].
This avoids that you make the same POST request when the user refreshes the
browser.


Note: It is better to use a Form [Django-doc]
than to perform manual validation and cleaning of the data. A Form will not
only simplify rendering a form in HTML, but it also makes it more convenient
to validate the input, and clean the data to a more convenient type.

Answered By: Willem Van Onsem
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.