【问题标题】:Comma separated lists in django templatesdjango 模板中的逗号分隔列表
【发布时间】:2010-11-17 05:17:12
【问题描述】:

如果fruits是列表['apples', 'oranges', 'pears']

有没有使用 django 模板标签生成“苹果、橙子和梨”的快速方法?

我知道使用循环和{% if counter.last %} 语句来做到这一点并不难,但是因为我要反复使用它,所以我想我将不得不学习如何编写自定义 标签 del> 过滤器,如果已经完成,我不想重新发明轮子。

作为扩展,我尝试删除 Oxford Comma(即返回“apples, oranges and pears”)更加混乱。

【问题讨论】:

  • 你为什么不使用现有的连接模板标签?
  • @S.Lott:当我查看文档页面上的列表时,我没有发现 join 模板标签。哎呀。话虽如此,下一阶段是将列表中的每个项目包装在一个超链接中,我认为我需要为此编写一个过滤器。
  • 如果您使用指向 Django URL 的链接,则需要使用 {% url %} 标签。 {% for %} 循环突然看起来更有吸引力了。 “反复”通常意味着您的模板需要{% include %} 常用功能。

标签: python django list django-templates


【解决方案1】:

第一选择:使用现有的连接模板标签。

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

这是他们的例子

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

第二个选择:在视图中做。

fruits_text = ", ".join( fruits )

fruits_text提供给模板进行渲染。

【讨论】:

  • 我可能需要其他列表(例如vegetables_text),并且我可能会在很多视图中使用这些列表,所以我宁愿有一个只需要我更改模板的解决方案。我考虑编写自定义标签的原因之一是我可以使用 Python - join 绝对比 for 循环更优雅。
  • 这也不会插入最后的“and”。
  • 在模板中还是在视图中是否有最佳实践?
【解决方案2】:

这是一个超级简单的解决方案。将此代码放入 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 %}

注意,它应该在元素之前使用,而不是之后。

【讨论】:

  • 如果您需要加入的不仅仅是数组中的字符串,最好的解决方案
  • 添加Oxford comma所需要做的就是将and替换为, and
  • 一个稍微更紧凑的版本,没有弃用的 ifequal 语法。 {% if not forloop.first %}{% if forloop.last %} and {% else %}, {% endif %}{% endif %}注意,它应该在元素之前使用,而不是之后。
【解决方案3】:

在 Django 模板中,您只需在每个水果之后建立一个逗号即可。逗号到达最后一个果实时将停止。

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

【讨论】:

  • 非常干净和简单的解决方案。
【解决方案4】:

我建议使用自定义 django 模板 filter 而不是自定义 tag -- 过滤器更方便和更简单(在适当的情况下,如这里)。 {{ fruits | joinby:", " }} 看起来像我想要的……带有自定义 joinby 过滤器:

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

如您所见,这本身就是简单!

【讨论】:

  • 我不知道标签和过滤器之间的区别。当我查看文档时,自定义标签似乎有点令人生畏,过滤器似乎更简单,这正是我在这种情况下所需要的。谢谢!
  • 这不会插入最后的“and”。
  • @Meekohi,所以 return arg.join(value[:-1]) + ' and ' + value[-1](对于 AP 样式,即 and 之前没有逗号;对于“牛津逗号”样式,在 ` + 'and' 之前添加一个 + arg )。我,我更喜欢 asyndeton 的强度,每 literarydevices.net/asyndeton 。无论如何,这些关于英语风格的精彩辩论都不属于 StackOverflow —— 把它带到english.stackexchange.com!-)
  • +1 用于返回 6 年前的问题以回答 3 年前的评论,但是当数组中只有一个项目时,您的过滤器会做什么? :) 让事物变得易于阅读似乎并不容易。
  • 我原来的异步过滤器工作正常;插入'and' 的新代码在上述注释中编码为无条件插入'and'。对于非常短的value,只要对[[as above] if len(value)>1 else value 进行编码,就可以表现出不同的行为。顺便说一句,asyndeton 的可读性非常高——亚里士多德、莎士比亚、乔伊斯(当然还有其他许多人)都有效地使用了它,而我对它的偏好首先源于我是一名诗人。
【解决方案5】:

这是我为解决我的问题而编写的过滤器(它不包括牛津逗号)

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

【讨论】:

    【解决方案6】:

    如果你想要一个 '.'在 Michael Matthew Toomim 的回答结尾,然后使用:

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

    【讨论】:

      【解决方案7】:

      这里的所有答案都不符合以下一项或多项:

      • 他们重写了标准模板库中的一些内容(很糟糕!)(确认,最佳答案!)
      • 他们不使用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 }}
      

      【讨论】:

        【解决方案8】:

        我会简单地使用', '.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"
        • 哦,看来我错过了。我已经更新了我的答案。请看一看。 @Alasdair
        【解决方案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)
            
            

            【讨论】:

              猜你喜欢
              • 2014-05-14
              • 2014-08-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-08-12
              • 2013-07-03
              • 2021-12-29
              • 2011-08-13
              相关资源
              最近更新 更多