Comma separated lists in django templates

后端 未结 11 1211
梦如初夏
梦如初夏 2021-01-30 16:00

If fruits is the list [\'apples\', \'oranges\', \'pears\'],

is there a quick way using django template tags to produce \"apples, oranges, and p

相关标签:
11条回答
  • 2021-01-30 16:08

    All of the answers here fail one or more of the following:

    • They rewrite something (poorly!) that's in the standard template library (ack, top answer!)
    • They don't use and for the last item.
    • They lack a serial (oxford) comma.
    • They use negative indexing, which won't work for django querysets.
    • They don't usually handle string sanitation properly.

    Here's my entry into this canon. First, the tests:

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

    And now the code. Yes, it gets a bit messy, but you'll see that it doesn't use negative indexing:

    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)
    

    You can use this in a django template with:

    {% load my_filters %}
    {{ items|oxford_comma }}
    
    0 讨论(0)
  • 2021-01-30 16:13

    I would simply use ', '.join(['apples', 'oranges', 'pears']) before sending it to the template as a context data.

    UPDATE:

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

    You will get apples, oranges and pears output.

    0 讨论(0)
  • 2021-01-30 16:18

    First choice: use the existing join template tag.

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

    Here's their example

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

    Second choice: do it in the view.

    fruits_text = ", ".join( fruits )
    

    Provide fruits_text to the template for rendering.

    0 讨论(0)
  • 2021-01-30 16:21

    If you want a '.' on the end of Michael Matthew Toomim's answer, then use:

    {% if not forloop.last %}{% ifequal forloop.revcounter 2 %} and {% else %}, {% endifequal %}{% else %}{% endif %}{% if forloop.last %}.{% endif %}
    
    0 讨论(0)
  • 2021-01-30 16:22

    I would suggest a custom django templating filter rather than a custom tag -- filter is handier and simpler (where appropriate, like here). {{ fruits | joinby:", " }} looks like what I'd want to have for the purpose... with a custom joinby filter:

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

    which as you see is simplicity itself!

    0 讨论(0)
  • 2021-01-30 16:22

    On the Django template this all you need to do for establishing a comma after each fruit. The comma will stop once its reached the last fruit.

    {% if not forloop.last %}, {% endif %}
    
    0 讨论(0)
提交回复
热议问题