web-dev-qa-db-fra.com

Listes séparées par des virgules dans les modèles Django

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.

60
Alasdair

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

7
Alasdair

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.

124
S.Lott

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 %}
62

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!

33
Alex Martelli

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 %}
18
Tommygun

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 %}
4
Todd Davies

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.

1
Wilfred Hughes

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.

0
Yiğit Genç

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 }}
0
F. Malina