Разделенные запятыми списки в шаблонах Django - PullRequest
65 голосов
/ 06 августа 2009

Если fruits - это список ['apples', 'oranges', 'pears'],

Существует ли быстрый способ использования тегов шаблонов django для получения "яблок, апельсинов и груш"?

Я знаю, что это не сложно сделать, используя цикл и операторы {% if counter.last %}, но поскольку я собираюсь использовать это неоднократно, я думаю, что мне придется научиться писать собственные теги фильтры, и я не хочу изобретать велосипед, если это уже сделано.

В качестве продолжения мои попытки отбросить Оксфордскую запятую (т.е. вернуть "яблоки, апельсины и груши") еще более беспорядочные.

Ответы [ 10 ]

127 голосов
/ 06 августа 2009

Первый выбор: использовать существующий тег шаблона соединения.

http://docs.djangoproject.com/en/dev/ref/templates/builtins/#join

Вот их пример

{{ value|join:" // " }}

Второй выбор: сделать это в представлении.

fruits_text = ", ".join( fruits )

Предоставить fruits_text шаблону для рендеринга.

63 голосов
/ 06 сентября 2010

Вот супер простое решение. Поместите этот код в comma.html:

{% if not forloop.last %}{% ifequal forloop.revcounter 2 %} and {% else %}, {% endifequal %}{% else %}{% endif %}

А теперь, куда бы вы ни поставили запятую, вместо этого включите «comma.html»:

{% for cat in cats %}
Kitty {{cat.name}}{% include "comma.html" %}
{% endfor %}
34 голосов
/ 06 августа 2009

Я бы предложил использовать пользовательский шаблонный шаблон django filter , а не пользовательский тег - фильтр удобнее и проще (при необходимости, как здесь). {{ fruits | joinby:", " }} выглядит так, как я хотел бы иметь для этой цели ... с пользовательским фильтром joinby:

def joinby(value, arg):
    return arg.join(value)

что, как видите, сама простота!

23 голосов
/ 02 июня 2016

В шаблоне Django это все, что вам нужно сделать для установки запятой после каждого фрукта. Запятая остановится, как только достигнет последнего фрукта.

{% if not forloop.last %}, {% endif %}
7 голосов
/ 07 августа 2009

Вот фильтр, который я написал, чтобы решить мою проблему (он не включает оксфордскую запятую)

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

Для использования в шаблоне: {{ fruits|join_with_commas }}

4 голосов
/ 10 февраля 2013

Если вы хотите '.' в конце ответа Майкла Мэтью Тоомима, затем используйте:

{% if not forloop.last %}{% ifequal forloop.revcounter 2 %} and {% else %}, {% endifequal %}{% else %}{% endif %}{% if forloop.last %}.{% endif %}
2 голосов
/ 11 октября 2018

Все ответы здесь не соответствуют одному или нескольким из следующих:

  • Они переписывают что-то (плохо!), Которое находится в стандартной библиотеке шаблонов (ack, лучший ответ!)
  • Они не используют and для последнего элемента.
  • У них нет серийной (оксфордской) запятой.
  • Они используют отрицательную индексацию, которая не будет работать для наборов запросов django.
  • Они обычно не справляются со санацией струн должным образом.

Вот мое вступление в этот канон. Сначала тесты:

class TestTextFilters(TestCase):

    def test_oxford_zero_items(self):
        self.assertEqual(oxford_comma([]), '')

    def test_oxford_one_item(self):
        self.assertEqual(oxford_comma(['a']), 'a')

    def test_oxford_two_items(self):
        self.assertEqual(oxford_comma(['a', 'b']), 'a and b')

    def test_oxford_three_items(self):
        self.assertEqual(oxford_comma(['a', 'b', 'c']), 'a, b, and c')

А теперь код. Да, это немного запутанно, но вы увидите, что не не использует отрицательную индексацию:

from django.utils.encoding import force_text
from django.utils.html import conditional_escape
from django.utils.safestring import mark_safe

@register.filter(is_safe=True, needs_autoescape=True)
def oxford_comma(l, autoescape=True):
    """Join together items in a list, separating them with commas or ', and'"""
    l = map(force_text, l)
    if autoescape:
        l = map(conditional_escape, l)

    num_items = len(l)
    if num_items == 0:
        s = ''
    elif num_items == 1:
        s = l[0]
    elif num_items == 2:
        s = l[0] + ' and ' + l[1]
    elif num_items > 2:
        for i, item in enumerate(l):
            if i == 0:
                # First item
                s = item
            elif i == (num_items - 1):
                # Last item.
                s += ', and ' + item
            else:
                # Items in the middle
                s += ', ' + item

    return mark_safe(s)

Вы можете использовать это в шаблоне django с:

{% load my_filters %}
{{ items|oxford_comma }}
1 голос
/ 20 июля 2018

Я бы просто использовал ', '.join(['apples', 'oranges', 'pears']) перед отправкой в ​​шаблон в качестве данных контекста.

UPDATE:

data = ['apples', 'oranges', 'pears']
print(', '.join(data[0:-1]) + ' and ' + data[-1])

Вы получите apples, oranges and pears вывод.

1 голос
/ 29 мая 2013

Django не имеет поддержки для этого из коробки. Вы можете определить собственный фильтр для этого:

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

Однако, если вы хотите иметь дело с чем-то более сложным, чем просто списки строк, вам придется использовать явный цикл {% for x in y %} в вашем шаблоне.

0 голосов
/ 20 февраля 2016

Если вам нравятся однострочники:

@register.filter
def lineup(ls): return ', '.join(ls[:-1])+' and '+ls[-1] if len(ls)>1 else ls[0]

и затем в шаблоне:

{{ fruits|lineup }}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...