Boucle numérique dans les modèles Django


261

Comment écrire une forboucle numérique dans un modèle Django? Je veux dire quelque chose comme

for i = 1 to n

Réponses:


392

J'ai utilisé une technique simple qui fonctionne bien pour les petits cas sans balises spéciales et sans contexte supplémentaire. Parfois, cela est utile

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

9
FWIW, 012 == 12, donc il ne bouclera que sur 1 et 2.
Jason

22
{% for i in '0123456789' | make_list%} pour itérer sur les 10, sans sauter 0.
Rick

7
Générer une chaîne de longueur arbitraire avec 'rjust'{% for i in "x"|rjust:"100" %}
Aaron

27
Pour la 3ème fois, cette réponse EST MAUVAISE . Ne l'utilisez PAS. Utilisez une balise de modèle et faites-le correctement. Je ne vois pas comment dire qu'une réponse est mauvaise, est une raison suffisante pour supprimer un commentaire.
Rebs

6
@Rebs Quel est le problème avec cela? Bien sûr, c'est hacky, mais l'ajout d'une balise de modèle simplement parce que vous devez faire une boucle sur une petite plage pour une fois dans un projet n'est pas non plus une excellente solution.
tobltobs

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

9
Très bonne réponse. Fonctionne parce que le centre crée une chaîne de n espaces qui sont ensuite bouclés. Chaque caractère d'espace est ensuite ignoré, mais la valeur actuelle dans la plage peut être trouvée dans forloop.counter (ou forloop.counter0). Voir docs.djangoproject.com/en/dev/ref/templates/builtins/#center
isedwards

2
Très bonne réponse! N'a pas eu à créer un nouveau filtre.
Miguel Ike

Pas besoin de faire quoi que ce soit en vue. Excellent hack
Mohammed Shareef C

106

Malheureusement, cela n'est pas pris en charge dans le langage de modèle Django. Il y a deux ou trois des suggestions , mais ils semblent un peu complexe. Je voudrais juste mettre une variable dans le contexte:

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

et dans le modèle:

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

13
Les motivations des auteurs de Django pour interdire le python ordinaire dans les modèles semblent inutiles et sans conséquence par rapport à la douleur et au temps perdu à contourner le fait de ne pas l'avoir, sans parler de la nécessité d'inventer une langue entièrement nouvelle lorsqu'elle est parfaitement impressionnante (python! ) est déjà là!
Bogatyr

2
@Bogatyr Si c'est ce que vous voulez, utilisez simplement Jinja2
tghw

78

Mon point de vue sur cette question, je pense que c'est le plus beau. Je garde un my_filters.py dans le répertoire templatetags.

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

Et vous utiliseriez comme ceci:

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

1
Je pense que c'est la bonne solution. Faire range(1, 16)pour obtenir des nombres à partir de 1, pas de 0.
chhantyal

Créez également un fichier vide _ init _.py dans le répertoire templatetags. Ajoutez également ces lignes en haut de my_filters.py from django.template import Library;register = Library()
Ajeeb.KP

Ajoutez un deuxième paramètre de filtre et vous obtenez la fonction de gamme complète intégrée à python. @register.filter(name='range') def filter_range(start, end): return range(start, end)S'habitue alors comme {% for i in 1|range:6 %}{% endfor %}. Voir la réponse complète ci-dessous ....
Paul Kenjora

J'ai modifié légèrement cette (excusez le formatage): try: return range(number) except: return []. De cette façon, il ne déclenche jamais d'erreur et renvoie un tableau vide (similaire à la façon dont la plupart des fonctions de modèle fonctionnent).
Tim Tisdall


41

Vous pouvez passer une liaison de

{'n' : range(n) }

au modèle, puis faites

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

Notez que vous obtiendrez un comportement basé sur 0 (0, 1, ... n-1).

(Mis à jour pour la compatibilité Python3)


1
Utilisation range(n)en python 3, si je me souviens bien, xrange était obsolète
Felício

Oui en effet. Et c'était l'une des deux lignes de code que j'ai eu à utiliser lors de la transition d'une application vers Python3.
Dave W. Smith

9

Vous ne vous passez pas n, mais plutôt range(n)[la liste des entiers de 0 à n-1 inclus], de votre vue à votre modèle, et dans ce dernier, vous le faites {% for i in therange %}(si vous insistez absolument sur 1 basé sur le 0 normal index basé sur vous pouvez utiliser forloop.counterdans le corps de la boucle ;-).


9

Au cas où quelqu'un d'autre tomberait sur cette question… J'ai créé une balise de modèle qui vous permet de créer un range(...): http://www.djangosnippets.org/snippets/1926/

Accepte les mêmes arguments que la fonction intégrée 'range' et crée une liste contenant
le résultat de «gamme».

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

Par exemple:
    {% mkrange 5 10 2 as some_range%}
    {% for i in some_range%}
      {{i}}: Quelque chose que je veux répéter \ n
    {% endfor%}

Produit:
    5: Quelque chose que je veux répéter 
    7: Quelque chose que je veux répéter 
    9: Quelque chose que je veux répéter


1
-1 en faveur de l'extrait d'Alex Pi qui ajoute le support des arguments variables.
m000

9

J'ai essayé très dur sur cette question, et je trouve la meilleure réponse ici: (de la façon de boucler 7 fois dans les modèles django )

Vous pouvez même accéder à l'idx!

views.py:

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

html:

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

9

Tu peux passer :

{'n': plage (n)}

Pour utiliser le modèle:

{% pour i dans n%} ... {% endfor%}


lisible et simple, facile à comprendre ce qui se passe si vous êtes le prochain gars à maintenir le code
rossdavidh

7

Vous devez utiliser " tranche " dans le modèle, un exemple comme celui-ci:

dans views.py

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

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

dans store_list.html:

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

1
Je ne sais pas si c'est ce que le PO recherchait, mais c'est exactement ce que je cherchais. =)
GChorn

7

Cette méthode prend en charge toutes les fonctionnalités de la range([start,] stop[, step])fonction standard

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

Production

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

votre solution ne fonctionne pas for value in 0|range:"10,2". Vous devez changer votre code comme suit:args = filter(lambda x: isinstance(x, int) and x >= 0, (_min, _max, _step))
Bedilbek

@Bedilbek ce code imite la plage standard de python. même il ne prend pas en charge les plages négatives sans paramètre d'étape explicite. >>> liste (plage (10,2)) [] >>> liste (plage (10,2, -1)) [10, 9, 8, 7, 6, 5, 4, 3]
Rebs

5

J'amène juste la réponse populaire un peu plus loin et la rend plus robuste. Cela vous permet de spécifier n'importe quel point de départ, donc 0 ou 1 par exemple. Il utilise également la fonction de plage de python où la fin est un de moins afin qu'il puisse être utilisé directement avec des longueurs de liste par exemple.

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

Ensuite, dans votre modèle, incluez simplement le fichier de balises de modèle ci-dessus et utilisez ce qui suit:

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

Maintenant, vous pouvez faire 1-6 au lieu de seulement 0-6 ou le coder en dur. L'ajout d'une étape nécessiterait une balise de modèle, cela devrait couvrir plus de cas d'utilisation, c'est donc un pas en avant.


Il s'agit d'une extension de la réponse @ guillermo-siliceo-trueba.
Paul Kenjora

5

Cela nécessite essentiellement une rangefonction. Un ticket de fonctionnalité Django a été généré ( https://code.djangoproject.com/ticket/13088 ) pour cela, mais fermé comme "ne résoudra pas" avec le commentaire suivant.

Mon impression de cette idée est qu'elle essaie de conduire à la programmation dans le modèle. Si vous avez une liste d'options qui doivent être rendues, elles doivent être calculées dans la vue, pas dans le modèle. Si c'est aussi simple qu'une plage de valeurs, qu'il en soit ainsi.

Ils ont un bon point - les modèles sont censés être des représentations très simples de la vue. Vous devez créer les données requises limitées dans la vue et passer au modèle dans le contexte.


6
La vue doit être pour les données, le modèle doit être pour la présentation. La vue ne doit pas nécessiter la connaissance du contenu du modèle, en particulier des plages. La raison pour laquelle Django a ignoré ces demandes de fonctionnalités est la foutaise.
Rebs

3

Vous pouvez utiliser: {% with ''|center: i as range %}


1
Pouvez-vous fournir un exemple / une explication de la façon dont cela fonctionne?
Rebs

1

Si le numéro provient d'un modèle, j'ai trouvé que c'était un joli patch pour le modèle:

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

2
Je ne sais pas pourquoi vous avez voté, c'est une réponse valable. Je n'aime pas cette solution par rapport à l'implémentation d'un filtre approprié comme je l'ai indiqué ci-dessus. Les modèles DB doivent rester simples. Mais c'est toujours mieux que la réponse majoritaire acceptée.
Rebs

Je ne sais même pas…
Alper

J'ai 9 ans de retard mais j'ai voté pour toi fam, ne t'inquiète même pas.
Sahil

1

Pour ceux qui recherchent une réponse simple, ayant juste besoin d'afficher une quantité de valeurs, disons 3 sur 100 messages par exemple, ajoutez-le {% for post in posts|slice:"3" %}et bouclez-le normalement et seuls 3 messages seront ajoutés.


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

{% endfor %}

Bien que ce code puisse répondre à la question, fournir un contexte supplémentaire concernant pourquoi et / ou comment ce code répond à la question améliore sa valeur à long terme.
xiawi
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.