Подтвердить что ты не робот

Списки с запятыми в шаблонах django

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

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

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

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

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

Здесь их пример

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

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

fruits_text = ", ".join( fruits )

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

Ответ 3

Здесь супер простое решение. Поместите этот код в 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 %}

Обновление: @user3748764 дает нам немного более компактную версию без устаревшего синтаксиса ifequal:

{% if not forloop.first %}{% if forloop.last %} and {% else %}, {% endif %}{% endif %}

Обратите внимание, что его следует использовать перед элементом, а не после.

Ответ 4

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

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

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

Ответ 5

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

{% if not forloop.last %}, {% endif %}

Ответ 6

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

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

Ответ 7

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

UPDATE:

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

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

Ответ 8

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

  • Они переписывают что-то (плохо!), Что в стандартной библиотеке шаблонов (ack, top answer!)
  • Они не используют 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 }}

Ответ 9

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 %} в вашем шаблоне.

Ответ 10

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

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

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

{{ fruits|lineup }}

Ответ 11

Я думаю, что самое простое решение может быть:

@register.filter
def comma_list(p_values: Iterable[str]) -> List[str]:
    values = list(p_values)
    if len(values) > 1:
        values[-1] = u'and %s' % values[-1]
    if len(values) > 2:
        return u', '.join(values)
    return u' '.join(values)