ChatGPT解决这个技术问题 Extra ChatGPT

Numeric for loop in Django templates

How do I write a numeric for loop in a Django template? I mean something like

for i = 1 to n

a
alias51

I've used a simple technique that works nicely for small cases with no special tags and no additional context. Sometimes this comes in handy

{% for i in '0123456789'|make_list %}
    {{ forloop.counter }}
{% endfor %}

FWIW, 012 == 12, so it will only loop over 1 and 2.
{% for i in '0123456789'|make_list %} to iterate over all 10, not skipping 0.
very clever. what about a list comprehension though? Kind of takes the hackishness out but uses the same idea x = 5, y=[i for i in range(x)] context={'iterateover':y}
@Dave_750 context={'iterateover': range(x)} would work just as well
Generate a string of arbitrary length with 'rjust' {% for i in "x"|rjust:"100" %}
T
Tomasz Żyźniewski
{% with ''|center:n as range %}
{% for _ in range %}
    {{ forloop.counter }}
{% endfor %}
{% endwith %}

Great answer. Works because center creates a string of n spaces that are then looped over. Each space char is then ignored, but the current value in range can be found from forloop.counter (or forloop.counter0). See docs.djangoproject.com/en/dev/ref/templates/builtins/#center
This should be marked as the accepted answer - way more dynamic.
t
tghw

Unfortunately, that's not supported in the Django template language. There are a couple of suggestions, but they seem a little complex. I would just put a variable in the context:

...
render_to_response('foo.html', {..., 'range': range(10), ...}, ...)
...

and in the template:

{% for i in range %}
     ...
{% endfor %}

The motivations the Django authors had for disallowing plain python in templates seem pointless and inconsequential compared to the pain and lost time involved in working around not having it, not to mention the need to invent an entirely new langauge when a perfectly awesome one (python!) is already right there!
@Bogatyr If that's what you want, just use Jinja2: docs.djangoproject.com/en/1.9/topics/templates/…
@Bogatyr Because obviously nothing bad happens, when you can call clear_full_page_cache() in a template (seen in php/magento store, took considerable time to debug). Finally, it's not very sensible to let a server render this, when client side this can be done just as easy and cheaper, given that no data is associated with it (else you just iterate the data).
r
radtek

My take on this issue, i think is the most pythonic. Create a my_filters.py in your apps templatetags directory.

@register.filter(name='times') 
def times(number):
    return range(number)

Usage in your template:

{% load my_filters %}
{% for i in 15|times %}
    <li>Item</li>
{% endfor %}

I think this is right solution. Do range(1, 16) to get numbers starting from 1, not 0.
Also create an empty file _ init _.py in templatetags directory. Also add these line to top of my_filters.py from django.template import Library;register = Library()
Add a second filter parameter and you get the full range function built into python. @register.filter(name='range') def filter_range(start, end): return range(start, end) Then gets used as {% for i in 1|range:6 %}{% endfor %}. See full answer below....
I altered this slightly (excuse formatting): try: return range(number) except: return []. That way it never raises an error and returns an empty array (similar to how most template functions work).
In order to have a python solution, this seemed to me the best one
D
Dave W. Smith

You can pass a binding of

{'n' : range(n) }

to the template, then do

{% for i in n %}
...
{% endfor %}

Note that you'll get 0-based behavior (0, 1, ... n-1).

(Updated for Python3 compatibility)


Use range(n) in python 3, if I remember it correctly, xrange was deprecated on it
Indeed yes. And that was one of two lines of code I had to chance in transitioning an app to Python3.
Working in 2021! Voted up!
P
Przemysław Pietrzkiewicz

Maybe like this?

{% for i in "x"|rjust:"100" %}
...
{% endfor %}

Works but not easy to read for another dev that comes along and sees it.
a
ajinzrathod

I'm just taking the popular answer a bit further and making it more robust. This lets you specify any start point, so 0 or 1 for example. It also uses python's range feature where the end is one less so it can be used directly with list lengths for example.

@register.filter(name='range')
def filter_range(start, end):
    return range(start, end)

Then in your template just include the above template tag file and use the following:

{% load myapp_filters %}

{% for c in 1|range:6 %}
    {{ c }}
{% endfor %}

Now you can do 1-6 instead of just 0-6 or hard coding it. Adding a step would require a template tag, this should cover more uses cases so it's a step forward.


This is an extension of @guillermo-siliceo-trueba answer.
Great answer, you saved my time
This is the perfect answer
r
rkoots

You can pass :

{ 'n' : range(n) }

To use template :

{% for i in n %} ... {% endfor %}


readable and simple, easy to understand what's happening if you're the next guy maintaining the code
C
Community

I tried very hard on this question, and I find the best answer here: (from how to loop 7 times in the django templates)

You can even access the idx!

views.py:

context['loop_times'] = range(1, 8)

html:

{% for i in loop_times %}
        <option value={{ i }}>{{ i }}</option>
{% endfor %}

Be more specific. How should we write context['loop_times'] = range(1, 8). If possible give the entire code
A
Alex Martelli

You don't pass n itself, but rather range(n) [the list of integers from 0 to n-1 included], from your view to your template, and in the latter you do {% for i in therange %} (if you absolutely insist on 1-based rather than the normal 0-based index you can use forloop.counter in the loop's body;-).


D
David Wolever

Just incase anyone else comes across this question… I've created a template tag which lets you create a range(...): http://www.djangosnippets.org/snippets/1926/

Accepts the same arguments as the 'range' builtin and creates a list containing
the result of 'range'.

Syntax:
    {% mkrange [start,] stop[, step] as context_name %}

For example:
    {% mkrange 5 10 2 as some_range %}
    {% for i in some_range %}
      {{ i }}: Something I want to repeat\n
    {% endfor %}

Produces:
    5: Something I want to repeat 
    7: Something I want to repeat 
    9: Something I want to repeat

-1 in favour of Alex Pi's snippet which adds support of variable arguments.
V
Vinta

You should use "slice" in template, a example like this:

in views.py

contexts = {
    'ALL_STORES': Store.objects.all(),
}

return render_to_response('store_list.html', contexts, RequestContext(request, processors=[custom_processor]))

in store_list.html:

<ul>
{% for store in ALL_STORES|slice:":10" %}
    <li class="store_item">{{ store.name }}</li>
{% endfor %}
</ul>

Not sure if this is what the OP was looking for, but it's exactly what I was looking for. =)
R
Rebs

This method supports all the functionality of the standard range([start,] stop[, step]) function

<app>/templatetags/range.py

from django import template

register = template.Library()


@register.filter(name='range')
def _range(_min, args=None):
    _max, _step = None, None
    if args:
        if not isinstance(args, int):
            _max, _step = map(int, args.split(','))
        else:
            _max = args
    args = filter(None, (_min, _max, _step))
    return range(*args)

Usage:

{% load range %}

<p>stop 5
{% for value in 5|range %}
{{ value }}
{% endfor %}
</p>

<p>start 5 stop 10
{% for value in 5|range:10 %}
{{ value }}
{% endfor %}
</p>

<p>start 5 stop 10 step 2
{% for value in 5|range:"10,2" %}
{{ value }}
{% endfor %}
</p>

Output

<p>stop 5
0 1 2 3 4
</p>

<p>start 5 stop 10
5 6 7 8 9
</p>

<p>start 5 stop 10 step 2
5 7 9
</p>

your solution does not work on for value in 0|range:"10,2". You have to change your code as follow: args = filter(lambda x: isinstance(x, int) and x >= 0, (_min, _max, _step))
@Bedilbek this code mimics the standard python range. even it doesn't support negative ranges without an explicit step parameter. >>> list(range(10,2)) [] >>> list(range(10,2,-1)) [10, 9, 8, 7, 6, 5, 4, 3]
A
Alastair McCormack

This essentially requires a range function. A Django feature ticket was raised (https://code.djangoproject.com/ticket/13088) for this but closed as "won't fix" with the following comment.

My impression of this idea is that it is trying to lead to programming in the template. If you have a list of options that need to be rendered, they should be computed in the view, not in the template. If that's as simple as a range of values, then so be it.

They have a good point - Templates are supposed to be very simple representations of the view. You should create the limited required data in the view and pass to the template in the context.


The view should be for data, the template should be for presentation. The view should not require knowledge of the contents of the template, specifically ranges. Django's reason for ignoring these feature requests is utter rubbish.
that's actually hard to tell if it's a good point. Templates aren't supposed to "host programmation" but they do today, with for loops and such... And actually, the solution today is to add tags, providing syntax support to allows method like calls in templates and default tags isn't gonna change much
J
Jan Kyu Peblik
{% for _ in ''|center:13 %}
    {{ forloop.counter }}
{% endfor %}

While this code may provide a solution to the question, it's better to add context as to why/how it works. This can help future users learn and eventually apply that knowledge to their own code. You are also likely to have positive-feedback/upvotes from users, when the code is explained.
@AmitVerma I forgot what I said in the last two replies to your comment that someone deleted. Sorry about that.
A
Alper

If the number is coming from a model, I found this to be a nice patch to the model:

def iterableQuantity(self):
    return range(self.quantity)

Not sure why you're getting down voted, it's a valid answer. I don't like this solution compared to implementing a proper filter as I've provided above. DB models should be kept lean. But it's still better than the majority accepted answer.
I don't even know…
I am 9 years too late but I upvoted you fam, don't even worry about it.
o
olibiaz

You can use: {% with ''|center: i as range %}


Can you provide an example/explanation of how this works?
E
Elias Prado

For those who are looking to simple answer, just needing to display an amount of values, let say 3 from 100 posts for example just add {% for post in posts|slice:"3" %} and loop it normally and only 3 posts will be added.


D
Darwin

This shows 1 to 20 numbers:

{% for i in "x"|rjust:"20"|make_list %}
 {{ forloop.counter }}
{% endfor %}

also this can help you: (count_all_slider_objects come from views)

{% for i in "x"|rjust:count_all_slider_objects %}
  {{ forloop.counter }}
{% endfor %}

or

  {% with counter=count_all_slider_objects %}
    {% if list_all_slider_objects %}
      {%  for slide in list_all_slider_objects %}
        {{forloop.counter|add:"-1"}}
        {% endfor%}
      {% endif %}
    {% endwith %}

S
Srishti Ahuja

You can pass range(n) instead of n in the context in views.py. This will give you an iterable list.

context['range']= range(n)

Then you can iterate in your template this way:

{% for i in range %}
   <!-- your code -->
{% endfor %}

M
Muhammad Abdullah
{% for i in range(10) %}
   {{ i }}

{% endfor %}

While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value.
Could not parse the remainder: (10) from range(10)
You can't use range() function in django template.But you can use filter to overcome this problem