Списки с запятыми в шаблонах django
Если fruits
- это список ['apples', 'oranges', 'pears']
,
Есть ли быстрый способ использовать теги шаблона django для создания "яблок, апельсинов и груш"?
Я знаю, что это не сложно сделать с помощью операторов цикла и {% if counter.last %}
, но поскольку я буду использовать это повторно, я думаю, что мне нужно научиться писать пользовательские теги , и я не хочу изобретать велосипед, если это уже сделано.
В качестве расширения мои попытки сбросить Oxford Comma (т.е. вернуть "яблоки, апельсины и груши" ) даже беспорядочно.
Ответы
Ответ 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)