Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Template Slice - Reversing Order

Thanks to a very helpful hint from another question I learned I can limit the amount of values in a list by slicing it in the template as such:

{% for comment in thread.comment_set.all|slice:":3" %}

Now I would like to get the last 3 results of my comments so I figured a simple ":-3" or "-3" would do the trick, alas:

Caught an exception while rendering: Negative indexing is not supported.

Also using:

{% for comment in thread.comment_set.all|slice:":3" reversed %}

Does not do the trick because if I have 5 comments, instead of 1,2,3 it displays the first three in 3,2,1 order.

Is there any way I can display the last 3 comments of a post without going into my database? I'd love to be able to do this purely using the templating system.

SOLUTION

{% for comment in thread.comment_set.all|dictsortreversed:"created"|slice:"3" %}

Displays the last three thanks to my table having the created timestamp.

like image 609
TheLizardKing Avatar asked Dec 14 '09 01:12

TheLizardKing


People also ask

What does {% %} mean in Django?

The {% if %} tag evaluates a variable, and if that variable is “true” (i.e. exists, is not empty, and is not a false boolean value) the contents of the block are output. One can use various boolean operators with Django If Template tag.

What is Forloop counter in Django?

Django for loop counter All the variables related to the counter are listed below. forloop. counter: By using this, the iteration of the loop starts from index 1. forloop. counter0: By using this, the iteration of the loop starts from index 0.17-Sept-2021.

What built in Django template filter capitalizes the first character of the value?

Use {{ obj | capfirst }} to make uppercase the first character only. Using {{ obj | title }} makes it Camel Case.

Which characters are illegal in template variable names in Django?

Variable names consist of any combination of alphanumeric characters and the underscore ( "_" ) but may not start with an underscore, and may not be a number.


2 Answers

Django's database queries are evaluated lazily, so the result of thread.comment_set.all is a QuerySet, not a list. A QuerySet supports many list-like functions, but not negative slicing, so the indexing error is not coming from the template filter itself. (If you're curious, slices on QuerySet objects get translated into a limit clause on the SQL statement, which is why you can't use a negative number).

In general, Django encourages a strict decoupling of templates and models; the views.py module is the glue where you do any work that requires knowledge of database models and queryset methods to translate your model data into simple variables and structures for the template.

Running a related query on a model from a template is not something you typically see in a Django template, and there's a good reason for this. Right now, it may seem very simple to slice the last three elements from the comment_set. Keep in mind, though, that the database will not return results in any guaranteed order. This means that, in addition to your slice, you now also need to add an order_by clause; there's simply no way to express this in a template, nor should there be. Better to think of the view as the translation between your model and the template, and let such database-facing work be done there, rather than embedded in HTML.

In this case, I would encourage you to pass an ordered slice to your template from the view:

# take first three sorted descending
comments = thread.comment_set.order_by('-something')[:3]

context = Context({'comments':comments})
return HttpResponse(tmplt.render(context))

If you must do the slicing in the template, and you really don't care about sorting the results, pass a list to the template. The slice filter will happily do negative slicing:

comments = list(thread.comment_set.all())
context = Context('comments':comments)

In the template:

{% for comment in comments|slice:"-3:" %}
like image 96
Jarret Hardie Avatar answered Sep 18 '22 20:09

Jarret Hardie


I haven't seen the dictsortreversed filter used too often, and according to the docs it takes a key to sort by

{% for comment in thread.comment_set.all|dictsortreversed:"name"|slice:"3" %}
like image 37
czarchaic Avatar answered Sep 22 '22 20:09

czarchaic