Loop for numérico nos modelos do Django


261

Como eu escrevo um forloop numérico em um modelo do Django? Quero dizer algo como

for i = 1 to n

Respostas:


392

Eu usei uma técnica simples que funciona bem para casos pequenos, sem tags especiais e sem contexto adicional. Às vezes isso é útil

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

9
FWIW, 012 == 12, por isso só vai varrer 1 e 2.
jason

22
{% para i em '0123456789' | make_list%} para iterar entre todos os 10, sem pular 0. #
Rick Rick

7
Gere uma string de comprimento arbitrário com 'rjust'{% for i in "x"|rjust:"100" %}
Aaron

27
Pela 3ª vez, essa resposta É MAU . NÃO use isso. Use uma tag de modelo e faça-a corretamente. Não vejo como dizer que uma resposta é ruim, é motivo suficiente para excluir um comentário.
Rebs

6
@ Rebs O que há de tão ruim nisso? Claro que é hacky, mas adicionar uma tag de modelo apenas porque você precisa percorrer um intervalo pequeno por uma vez em um projeto também não é uma solução tão boa.
tobltobs

114
{% with ''|center:n as range %}
{% for _ in range %}
    {{ forloop.counter }}
{% endfor %}
{% endwith %}

9
Ótima resposta. Funciona porque o centro cria uma sequência de n espaços que são repetidos. Cada caractere de espaço é ignorado, mas o valor atual no intervalo pode ser encontrado em forloop.counter (ou forloop.counter0). Consulte docs.djangoproject.com/en/dev/ref/templates/builtins/#center
isedwards

2
Ótima resposta! Não foi necessário criar um novo filtro.
Miguel Ike

Não há necessidade de fazer nada à vista. Excelente corte
Mohammed Shareef C

106

Infelizmente, isso não é suportado na linguagem de modelo do Django. Há um par de sugestões , mas eles parecem um pouco complexo. Eu apenas colocaria uma variável no contexto:

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

e no modelo:

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

13
As motivações que os autores do Django tinham para desabilitar o python simples em modelos parecem inúteis e inconseqüentes em comparação com a dor e o tempo perdido envolvidos em contornar a falta dele, sem mencionar a necessidade de inventar uma linguagem totalmente nova quando uma linguagem perfeitamente impressionante (python! ) já está aí!
Bogatyr

2
@Bogatyr Se é isso que você quer, é só usar Jinja2: docs.djangoproject.com/en/1.9/topics/templates/...
tghw

78

Minha opinião sobre esta questão, acho a mais legal. Eu mantenho um my_filters.py no diretório templatetags.

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

E você usaria assim:

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

1
Eu acho que essa é a solução certa. Fazer range(1, 16)para obter números a partir de 1, não 0.
chhantyal

Crie também um arquivo vazio _ init _.py no diretório templatetags. Adicione também esta linha ao topo de my_filters.py from django.template import Library;register = Library()
Ajeeb.KP 23/03

Adicione um segundo parâmetro de filtro e você terá a função de gama completa incorporada no python. @register.filter(name='range') def filter_range(start, end): return range(start, end)Então se acostuma como {% for i in 1|range:6 %}{% endfor %}. Veja a resposta completa abaixo ....
Paul Kenjora 5/16

Eu alterei esta ligeiramente (desculpem formatação): try: return range(number) except: return []. Dessa forma, nunca gera um erro e retorna uma matriz vazia (semelhante à maneira como a maioria das funções de modelo funciona).
Tim Tisdall 28/09


41

Você pode passar uma ligação de

{'n' : range(n) }

para o modelo, então faça

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

Observe que você obterá um comportamento baseado em 0 (0, 1, ... n-1).

(Atualizado para compatibilidade com Python3)


1
Use range(n)em python 3, se eu me lembro corretamente, xrange foi depreciado sobre ele
Felício

De fato sim. E essa foi uma das duas linhas de código que tive a chance de fazer a transição de um aplicativo para Python3.
31516 Dave

9

Você não passa por nsi só, mas sim range(n)[a lista de números inteiros de 0 a n-1 incluídos], da sua exibição para o seu modelo e, no último, você o faz {% for i in therange %}(se você absolutamente insistir em 0 com base em 1 e não no 0 normal) índice baseado em você pode usar forloop.counterno corpo do loop ;-).


9

Apenas coloque alguém que se depare com esta pergunta ... Eu criei uma tag de modelo que permite criar uma range(...): http://www.djangosnippets.org/snippets/1926/

Aceita os mesmos argumentos que o 'intervalo' interno e cria uma lista contendo
o resultado de 'range'.

Sintaxe:
    {% mkrange [start,] stop [, step] como context_name%}

Por exemplo:
    {% mkrange 5 10 2 como some_range%}
    {% para i em some_range%}
      {{i}}: algo que quero repetir \ n
    {% endfor%}

Produz:
    5: Algo que quero repetir 
    7: Algo que quero repetir 
    9: Algo que quero repetir


1
-1 a favor do snippet de Alex Pi, que adiciona suporte a argumentos variáveis.
M000


9

Você pode passar:

{'n': intervalo (n)}

Para usar o modelo:

{% para i em n%} ... {% endfor%}


legível e simples, fácil de entender o que está acontecendo se você é o cara ao lado mantendo o código
rossdavidh

7

Você deve usar " fatia " no modelo, um exemplo como este:

em views.py

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

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

em store_list.html:

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

1
Não tenho certeza se é isso que o OP estava procurando, mas é exatamente o que eu estava procurando. =)
GChorn

7

Este método suporta toda a funcionalidade da range([start,] stop[, step])função padrão

<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)

Uso:

{% 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>

Resultado

<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>

sua solução não funciona for value in 0|range:"10,2". Você deve alterar seu código da seguinte maneira:args = filter(lambda x: isinstance(x, int) and x >= 0, (_min, _max, _step))
Bedilbek

@ Bedilbek este código imita o intervalo python padrão. mesmo ele não suporta intervalos negativos sem um parâmetro de etapa explícito. >>> list (range (10,2)) [] >>> list (range (10,2, -1)) [10, 9, 8, 7, 6, 5, 4, 3]
Rebs

5

Estou apenas levando a resposta popular um pouco mais longe e tornando-a mais robusta. Isso permite que você especifique qualquer ponto de início, então 0 ou 1, por exemplo. Ele também usa o recurso range do python, onde o final é um a menos, para que possa ser usado diretamente com comprimentos de lista, por exemplo.

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

Em seu modelo, inclua o arquivo de tag de modelo acima e use o seguinte:

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

Agora você pode fazer 1-6 em vez de apenas 0-6 ou codificá-lo. A adição de uma etapa exigiria uma tag de modelo. Isso deve abranger mais casos de uso, portanto é um passo à frente.


Esta é uma extensão da resposta de @ guillermo-siliceo-trueba.
Paul Kenjora 5/09/16

5

Isso requer essencialmente uma rangefunção. Um ticket de recurso do Django foi gerado ( https://code.djangoproject.com/ticket/13088 ) para isso, mas fechado como "não será corrigido" com o seguinte comentário.

Minha impressão dessa idéia é que ela está tentando levar à programação no modelo. Se você tiver uma lista de opções que precisam ser renderizadas, elas deverão ser computadas na visualização, não no modelo. Se isso é tão simples quanto um intervalo de valores, que assim seja.

Eles têm um bom argumento - os modelos devem ser representações muito simples da exibição. Você deve criar os dados necessários limitados na visualização e passar para o modelo no contexto.


6
A visualização deve ser para dados, o modelo deve ser para apresentação. A visualização não deve exigir conhecimento do conteúdo do modelo, especificamente intervalos. A razão do Django de ignorar essas solicitações de recursos é um lixo total.
Rebs

3

Você pode usar: {% with ''|center: i as range %}


1
Você pode fornecer um exemplo / explicação de como isso funciona?
Rebs 17/05

1

Se o número é proveniente de um modelo, achei esse um bom patch para o modelo:

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

2
Não sei por que você está sendo votado, é uma resposta válida. Não gosto desta solução em comparação com a implementação de um filtro adequado, como forneci acima. Os modelos de banco de dados devem ser mantidos enxutos. Mas ainda é melhor do que a resposta aceita pela maioria.
Rebs 17/05

Eu nem sei ...
Alper

Estou 9 anos atrasado, mas votei em você fam, nem se preocupe.
Sahil

1

Para aqueles que procuram uma resposta simples, apenas precisando exibir uma quantidade de valores, digamos 3 entre 100 postagens, por exemplo, basta adicionar {% for post in posts|slice:"3" %}e repetir normalmente e apenas 3 postagens serão adicionadas.


-5
{% for i in range(10) %}
   {{ i }}

{% endfor %}

Embora esse código possa responder à pergunta, fornecer um contexto adicional a respeito de por que e / ou como esse código responde à pergunta melhora seu valor a longo prazo.
Xiawi
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.