Si fruits
est la liste ['apples', 'oranges', 'pears']
,
existe-t-il un moyen rapide d'utiliser les balises de modèle Django pour produire "des pommes, des oranges et des poires"?
Je sais que ce n'est pas difficile de faire cela en utilisant une boucle et des instructions {% if counter.last %}
, mais parce que je vais l'utiliser à plusieurs reprises, je pense que je vais devoir apprendre à écrire des mots mots clés filtres, et je ne veux pas réinventer la roue si cela a déjà été fait.
En prolongement, mes tentatives pour supprimer le Oxford Comma (c’est-à-dire le retour "pommes, oranges et poires") sont encore plus compliquées.
Voici le filtre que j'ai écrit pour résoudre mon problème (il n'inclut pas la virgule Oxford)
def join_with_commas(obj_list):
"""Takes a list of objects and returns their string representations,
separated by commas and with 'and' between the penultimate and final items
For example, for a list of fruit objects:
[<Fruit: apples>, <Fruit: oranges>, <Fruit: pears>] -> 'apples, oranges and pears'
"""
if not obj_list:
return ""
l=len(obj_list)
if l==1:
return u"%s" % obj_list[0]
else:
return ", ".join(str(obj) for obj in obj_list[:l-1]) \
+ " and " + str(obj_list[l-1])
Pour l'utiliser dans le modèle: {{ fruits|join_with_commas }}
Premier choix: utilisez la balise de gabarit de jointure existante.
http://docs.djangoproject.com/en/dev/ref/templates/builtins/#join
Voici leur exemple
{{ value|join:" // " }}
Deuxième choix: faites-le dans la vue.
fruits_text = ", ".join( fruits )
Fournissez fruits_text
au modèle pour le rendu.
Voici une solution super simple. Mettez ce code dans comma.html:
{% if not forloop.last %}{% ifequal forloop.revcounter 2 %} and {% else %}, {% endifequal %}{% else %}{% endif %}
Et maintenant, où que vous mettiez la virgule, incluez "comma.html" à la place:
{% for cat in cats %}
Kitty {{cat.name}}{% include "comma.html" %}
{% endfor %}
Je suggérerais un modèle personnalisé Django filter plutôt qu'un tag personnalisé - le filtre est plus pratique et plus simple (le cas échéant, comme ici). {{ fruits | joinby:", " }}
ressemble à ce que je voudrais avoir pour le but ... avec un filtre joinby
personnalisé:
def joinby(value, arg):
return arg.join(value)
qui, comme vous le voyez, est la simplicité même!
Sur le modèle Django, c’est tout ce que vous devez faire pour établir une virgule après chaque fruit. La virgule s'arrêtera une fois le dernier fruit atteint.
{% if not forloop.last %}, {% endif %}
Si vous voulez un '.' sur la fin de la réponse de Michael Matthew Toomim, utilisez:
{% if not forloop.last %}{% ifequal forloop.revcounter 2 %} and {% else %}, {% endifequal %}{% else %}{% endif %}{% if forloop.last %}.{% endif %}
Django ne prend pas en charge cette solution prête à l'emploi. Vous pouvez définir un filtre personnalisé pour cela:
from Django import template
register = template.Library()
@register.filter
def join_and(value):
"""Given a list of strings, format them with commas and spaces, but
with 'and' at the end.
>>> join_and(['apples', 'oranges', 'pears'])
"apples, oranges, and pears"
"""
# convert numbers to strings
value = [str(item) for item in value]
if len(value) == 1:
return value[0]
# join all but the last element
all_but_last = ", ".join(value[:-1])
return "%s, and %s" % (all_but_last, value[-1])
Toutefois, si vous souhaitez gérer quelque chose de plus complexe que de simples listes de chaînes, vous devez utiliser une boucle explicite {% for x in y %}
dans votre modèle.
J'utiliserais simplement ', '.join(['apples', 'oranges', 'pears'])
avant de l'envoyer au modèle en tant que données contextuelles.
METTRE À JOUR:
data = ['apples', 'oranges', 'pears']
print(', '.join(data[0:-1]) + ' and ' + data[-1])
Vous obtiendrez une sortie apples, oranges and pears
.
Si vous aimez les one-liners:
@register.filter
def lineup(ls): return ', '.join(ls[:-1])+' and '+ls[-1] if len(ls)>1 else ls[0]
puis dans le template:
{{ fruits|lineup }}