如果fruits
是清单['apples', 'oranges', 'pears']
,
有没有一种使用Django模板标签生成“苹果,橘子和梨”的快速方法?
我知道使用循环和{% if counter.last %}
语句执行此操作并不难,但是由于我将反复使用它,所以我认为我将必须学习如何编写自定义标签 过滤器,如果已经完成,我不想重新发明轮子。
作为扩展,我尝试删除牛津逗号(即返回“苹果,橘子和梨”)的操作更加混乱。
如果fruits
是清单['apples', 'oranges', 'pears']
,
有没有一种使用Django模板标签生成“苹果,橘子和梨”的快速方法?
我知道使用循环和{% if counter.last %}
语句执行此操作并不难,但是由于我将反复使用它,所以我认为我将必须学习如何编写自定义标签 过滤器,如果已经完成,我不想重新发明轮子。
作为扩展,我尝试删除牛津逗号(即返回“苹果,橘子和梨”)的操作更加混乱。
{% url %}
标记。该{% for %}
回路突然看起来更吸引人。“重复”通常意味着您的模板需要{% include %}
通用功能。
Answers:
首选:使用现有的联接模板标记。
http://docs.djangoproject.com/en/dev/ref/templates/builtins/#join
这是他们的例子
{{ value|join:" // " }}
第二选择:在视图中进行操作。
fruits_text = ", ".join( fruits )
提供fruits_text
给模板进行渲染。
vegetables_text
),并且可能在很多视图中使用这些列表,所以我宁愿有一个只需要更改模板的解决方案。我考虑编写自定义标签的原因之一是我可以使用Python-join
绝对比for循环更优雅。
这是一个超级简单的解决方案。将此代码放入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 %}
请注意,应在元素之前而不是之后使用它。
{% if not forloop.first %}{% if forloop.last %} and {% else %}, {% endif %}{% endif %}
请注意,应在元素之前而不是之后使用它。
我建议使用自定义django模板过滤器,而不是自定义标签-过滤器更方便,更简单(在适当的地方,例如这里)。{{ fruits | joinby:", " }}
看起来像我想要的...使用自定义joinby
过滤器:
def joinby(value, arg):
return arg.join(value)
如您所见,这本身就是简单性!
return arg.join(value[:-1]) + ' and ' + value[-1]
(对于AP样式,即,之前没有逗号and
;对于“ Oxford逗号”样式,请+ arg
在“ +”和“`”之前添加一个权利)。我,我更喜欢asyndeton的力量,请参阅literarydevices.net/asyndeton。无论如何,关于英语风格的这场激烈辩论都没有落在StackOverflow上–将其转到english.stackexchange.com!-)
'and'
代码在上面的注释中编码为无条件插入'and'
。在很短的时间内表现不同所需要做的value
就是编写代码[[as above] if len(value)>1 else value
。BTW asyndeton具有很高的可读性-亚里斯多德,莎士比亚,乔伊斯(当然还有很多人)都有效地使用了它,而我对它的偏爱首先源于我是诗人。
这是我为解决问题而写的过滤器(不包括牛津逗号)
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 }}
此处的所有答案均不满足以下一项或多项要求:
and
用于最后一项。这是我进入该经典的内容。一,测试:
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 }}
我会', '.join(['apples', 'oranges', 'pears'])
在将其作为上下文数据发送到模板之前简单地使用。
更新:
data = ['apples', 'oranges', 'pears']
print(', '.join(data[0:-1]) + ' and ' + data[-1])
您将获得apples, oranges and pears
输出。
"apples, oranges, pears"
。所需的输出是"apples, oranges, and pears"
。
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 %}
在模板中使用显式循环。