Django template: check for empty query set
Asked Answered
S

9

39

Is there a way to check for an empty query set in the Django template? In the example below, I only want the NOTES header to be displayed if there are notes.

If I put an {% empty %} inside the "for" then it does display whatever is inside the empty tag, so it knows it's empty.

I'm hoping for something that does not involve running the query twice.

{% if notes - want something here that works %}
     NOTES:
     {% for note in notes %}
         {{note.text}}  
     {% endfor  %}
{% endif  %}

Clarification: the above example "if notes" does not work - it still displays the header even with an empty query set.

Here's a simplified version of the view

sql = "select * from app_notes, app_trips where"
notes = trip_notes.objects.raw(sql,(user_id,))

return render_to_response(template, {"notes":notes},context_instance=RequestContext(request))  

Edit: the view select selects from multiple tables.

Swedenborgianism answered 2/7, 2013 at 20:24 Comment(6)
Are you sure it runs the query twice? And, an easy suggestion would be "caching", but it doesn't answer your question.Popliteal
My issue is not that it runs the query twice. My example doesn't work. It shows the header even though the query set is empty.Swedenborgianism
post your view pleaseVaish
Why {% if notes %} does not work ?Esemplastic
Should it? I hoped it would, but it doesn't. I use the "if" for single variables, but it never works (for me, anyway) for query results.Swedenborgianism
notes is a raw query set, which unlike regular query sets doesn't define a __nonzero__ method. So they evaluate to True even when empty. docs.python.org/2/reference/datamodel.html#object.__nonzero__Edulcorate
Q
52

Have a look at the {% empty %} tag. Example from the documentation

<ul>
{% for athlete in athlete_list %}
    <li>{{ athlete.name }}</li>
{% empty %}
    <li>Sorry, no athletes in this list.</li>
{% endfor %}
</ul>

Link: https://docs.djangoproject.com/en/1.8/ref/templates/builtins/#for-empty

If you are interested in a table, or some kind of heading if there are results, add the forloop.first:

    {% for athlete in athlete_list %} {% if forloop.first %} Athlete Name: {% endif %}
  • {{ athlete.name }}
  • {% empty %}
  • Sorry, no athletes in this list.
  • {% endfor %}
Quidnunc answered 25/1, 2016 at 22:48 Comment(2)
While very useful, it does not actually answer the question. empty will run once when there are no elements. In the question, the OP wishes to display a text when there are elements in the list. (But only once)Characteristic
@T'n'E look at my updated, which includes the listsFossette
C
39

Try {% if notes.all %}. It works for me.

Chrystalchryste answered 1/10, 2013 at 9:16 Comment(5)
Works for me too, and I prefer this to .count, as that would issue a select count(*) query whereas I think this will cache the queryset if there is one.Pronator
Do not use queryset as a boolean value: instead of if queryset: use if queryset.exists(): Remember, that querysets are lazy, and if you use queryset as a boolean value, an inappropriate query to a database will be carried out. @Pronator approach is betterAndersen
Thanks for the solution @KishorPawar, though in the template it doesn't have the parentheses: if queryset.exists.Sargeant
@KishorPawar It's fine to use if queryset: because the QuerySet class overrides the __bool__ magic method. See github.com/django/django/blob/….Mare
Yes, it is @RuneKaagaard . Though the document says to use exists(). See the explanation for bool(). docs.djangoproject.com/en/3.2/ref/models/querysets/…Andersen
E
8

It's unfortunate that you're stuck using a raw query set - they're missing a lot of useful behavior.

You could convert the raw query set into a list in the view:

notes_as_list = list(notes)
return render_to_response(template, {"notes":notes_as_list},context_instance=RequestContext(request))

Then check it as a boolean in the template:

{% if notes %}
    Header
    {% for note in notes %}
        {{ note.text }}
    {% endfor %}
{% endif %}

You could also make it happen without conversions using forloop.first:

{% for note in notes %}
    {% if forloop.first %}
         Header
    {% endif %}
    {{ note.text }}
{% endfor %}
Edulcorate answered 2/7, 2013 at 21:33 Comment(2)
The forloop.first works for me. Nice hack. The other one doesn't. It lists all the letters of the query itself: R A W Q U E R Y S E L ESwedenborgianism
Yeah, I checked the source for make_list but overlooked that it's registered as a stringfilter, which means it converts the argument to a unicode object if it isn't already. I'll update my answer.Edulcorate
R
6

In your view check whether notes is empty or not. If it is then you pass None instead:

{"notes": None}

In your template you use {% if notes %} as normal.

Roussel answered 2/7, 2013 at 21:19 Comment(3)
This would require running it twice, no?Swedenborgianism
what? why? you should just assign your query to a variable notes and then do something like {"notes": notes if len(notes) else None} and you can then check if notes is None in your templates like Simeon suggests. This is the proper way to do this.Lactescent
I think its better to do notes.count() - last I checked len(notes) pulls every record from the database whereas count simply issues a count query.Quixotic
S
4

What about:

{% if notes != None %}
    {% if notes %}
        NOTES:
        {% for note in notes %}
            {{ note.text }}  
        {% endfor  %}
    {% endif %}
{% else %}
    NO NOTES AT ALL
{% endif %}
Sumption answered 2/7, 2013 at 20:58 Comment(1)
Nope. It still prints the header (but no notes)Swedenborgianism
B
3

Your original solution

{% if notes %}
    Header
    {% for note in notes %}
        {{ note.text }}
    {% endfor %}
{% endif %}

Works now with Django 1.7 and thanks to QuerySet caching, it does not cost and extra query.

Broadcloth answered 16/1, 2015 at 19:10 Comment(0)
C
3

Often the right way to do this is to use the {% with ... %} tag. This caches the query so it runs only once and also gives you more flexibility with your markup than using {% empty %}.

{% with notes as my_notes %}
{% if my_notes %}
<ul>
  {% for note in my_notes %}
  <li>{{ note }}</li>
  {% endfor %}
</ul>
{% else %}
<p>Sorry, no notes available</p>
{% endif %}
{% endwith %}

With this particular example I'm not sure how useful it is but if you're querying Many-to-Many field, for instance, it's likely what you want to do.

Convertite answered 27/12, 2019 at 18:16 Comment(0)
U
1

Use {% empty %} in django templates

{% if list_data %}
    {% for data in list_data %}
        {{ data.field_1 }}
    {% endfor %}
{% else %}
    <p>No data found!</p>
{% endif %}

We can write above code with {% empty %}.

{% for data in list_data %}
    {{ data.field_1 }}
{% empty %}
    <p>No data found!</p>
{% endfor %}
Unbind answered 10/12, 2021 at 9:19 Comment(0)
R
0

you can use the {% empty %} for a "for loop" as in:

{% for object in list %}
<p>{{ object }}</p>
{% empty %}
<p>list is empty</p>
{% endfor %}

use '.all'

{% if object.something.all %}
Radiography answered 5/7, 2023 at 21:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.