Danh sách được phân tách bằng dấu phẩy trong các mẫu django


75

Nếu fruitslà danh sách ['apples', 'oranges', 'pears'],

có cách nào nhanh chóng bằng cách sử dụng các thẻ mẫu django để tạo "táo, cam và lê" không?

Tôi biết không khó để làm điều này bằng cách sử dụng vòng lặp và {% if counter.last %}câu lệnh, nhưng vì tôi sẽ sử dụng điều này nhiều lần nên tôi nghĩ rằng tôi sẽ phải học cách viết tùy chỉnhthẻ bộ lọc và tôi không muốn phát minh lại bánh xe nếu nó đã được thực hiện.

Như một phần mở rộng, nỗ lực của tôi để bỏ Dấu phẩy Oxford (tức là trả về "táo, cam và lê") thậm chí còn lộn xộn hơn.


4
Tại sao bạn không sử dụng thẻ mẫu tham gia hiện có?
S.Lott

1
@ S.Lott: Tôi không tìm thấy thẻ mẫu tham gia khi xem qua danh sách trên trang tài liệu. Giáo sư. Phải nói rằng, giai đoạn tiếp theo là gói từng mục trong danh sách trong một siêu liên kết, mà tôi nghĩ rằng tôi sẽ cần viết một bộ lọc.
Alasdair

Nếu bạn đang sử dụng các liên kết đến URL Django của mình, bạn sẽ cần sử dụng {% url %}thẻ. Các {% for %}vòng lặp đột nhiên trông hấp dẫn hơn nhiều. "Lặp đi lặp lại" thường có nghĩa là các mẫu của bạn cần {% include %}có các tính năng chung.
S.Lott

Câu trả lời:


140

Lựa chọn đầu tiên: sử dụng thẻ mẫu tham gia hiện có.

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

Đây là ví dụ của họ

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

Lựa chọn thứ hai: làm điều đó trong khung cảnh.

fruits_text = ", ".join( fruits )

Cung cấp fruits_textcho mẫu để kết xuất.


Tôi có thể yêu cầu các danh sách khác (ví dụ vegetables_text), và tôi có thể sử dụng các danh sách này trong nhiều dạng xem, vì vậy tôi muốn có một giải pháp chỉ yêu cầu tôi thay đổi các mẫu. Một trong những lý do tôi nghĩ về việc viết thẻ tùy chỉnh là tôi có thể sử dụng Python - joinchắc chắn là thanh lịch hơn các vòng lặp for.
Alasdair

8
Điều này cũng không chèn "và" cuối cùng.
Meekohi

Có bất kỳ phương pháp hay nhất nào về việc nên thực hiện điều này trong mẫu hay trong dạng xem không?
Rikki

69

Đây là một giải pháp siêu đơn giản. Đặt mã này vào comma.html:

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

Và bây giờ bất cứ nơi nào bạn đặt dấu phẩy, hãy bao gồm "comma.html" thay thế:

{% for cat in cats %}
Kitty {{cat.name}}{% include "comma.html" %}
{% endfor %}

Cập nhật: @ user3748764 cung cấp cho chúng tôi phiên bản nhỏ gọn hơn một chút, không có cú pháp cũ không được dùng nữa:

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

Lưu ý rằng nó nên được sử dụng trước phần tử, không phải sau.


3
giải pháp tốt nhất nếu bạn cần tham gia nhiều hơn các chuỗi trong một mảng
BiAiB

Tất cả những gì bạn cần làm để thêm dấu phẩy Oxford là thay thế andbằng , and.
Sardathrion - chống lại sự lạm dụng SE,

1
Một phiên bản nhỏ gọn hơn một chút không có cú pháp cũ không được dùng nữa. {% if not forloop.first %}{% if forloop.last %} and {% else %}, {% endif %}{% endif %}Lưu ý rằng nó nên được sử dụng trước phần tử, không phải sau.
user3748764,

35

Tôi sẽ đề xuất một bộ lọc tạo mẫu django tùy chỉnh hơn là một thẻ tùy chỉnh - bộ lọc đơn giản hơn và đơn giản hơn (nếu thích hợp, như ở đây). {{ fruits | joinby:", " }}trông giống như những gì tôi muốn có cho mục đích ... với joinbybộ lọc tùy chỉnh :

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

mà như bạn thấy chính là sự đơn giản!


Tôi không biết sự phân biệt giữa thẻ và bộ lọc. Trong khi các thẻ tùy chỉnh có vẻ hơi khó khăn khi tôi xem tài liệu, các bộ lọc có vẻ đơn giản hơn và chính xác là những gì tôi cần trong trường hợp này. Cảm ơn!
Alasdair

7
Điều này không chèn "và" cuối cùng.
Meekohi,

2
@Meekohi, vì vậy return arg.join(value[:-1]) + ' and ' + value[-1](đối với kiểu AP, tức là không có dấu phẩy trước and; đối với kiểu "dấu phẩy Oxford", hãy thêm một + argquyền trước dấu `+ 'và' '). Tôi, tôi thích sức mạnh của asyndeton hơn, theo văn họcdevices.net/asyndeton . Và không ai trong cuộc tranh luận này tốt trên phong cách tiếng Anh thuộc về StackOverflow anyway - mang nó đến english.stackexchange.com -!)
Alex Martelli

+1 để quay lại câu hỏi 6 tuổi để trả lời nhận xét 3 tuổi, nhưng bộ lọc của bạn sẽ làm gì khi chỉ có một mục trong mảng? :) Làm cho mọi thứ con người có thể đọc được dường như không dễ dàng như vậy.
Meekohi

1
Bộ lọc asyndeton ban đầu của tôi hoạt động tốt; những cái mới chèn 'and'được mã hóa trong nhận xét ở trên để chèn vô điều kiện 'and'. Tất cả những gì cần thiết để hành xử khác biệt trong thời gian ngắn valuelà viết mã [[as above] if len(value)>1 else value. Và BTW asyndeton rất dễ đọc cho con người - Aristotle, Shakespeare, Joyce (tất nhiên là trong số nhiều người khác) đều đã sử dụng nó một cách hiệu quả, và sở thích của tôi đối với nó bắt nguồn từ việc tôi là một nhà thơ, trước hết.
Alex Martelli

32

Trên mẫu Django, tất cả những gì bạn cần làm để thiết lập dấu phẩy sau mỗi quả. Dấu phẩy sẽ dừng lại khi nó đến quả cuối cùng.

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

2
Giải pháp rất sạch sẽ và dễ dàng.
SaeX

8

Đây là bộ lọc tôi đã viết để giải quyết vấn đề của mình (nó không bao gồm dấu phẩy Oxford)

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

Để sử dụng nó trong mẫu: {{ fruits|join_with_commas }}


4

Nếu bạn muốn một '.' ở cuối câu trả lời của Michael Matthew Toomim, sau đó sử dụng:

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

4

Tất cả các câu trả lời ở đây đều không đạt một hoặc nhiều câu sau:

  • Họ viết lại thứ gì đó (thật tệ!) Trong thư viện mẫu chuẩn (ack, câu trả lời hàng đầu!)
  • Họ không sử dụng andcho mục cuối cùng.
  • Chúng thiếu dấu phẩy nối tiếp (oxford).
  • Chúng sử dụng lập chỉ mục phủ định, điều này sẽ không hoạt động đối với các bộ truy vấn django.
  • Họ thường không xử lý vệ sinh chuỗi đúng cách.

Đây là mục nhập của tôi vào kinh điển này. Đầu tiên, các bài kiểm tra:

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

Và bây giờ là mã. Có, nó hơi lộn xộn, nhưng bạn sẽ thấy rằng nó không sử dụng lập chỉ mục phủ định:

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)

Bạn có thể sử dụng điều này trong một mẫu django với:

{% load my_filters %}
{{ items|oxford_comma }}

2

Tôi chỉ cần sử dụng ', '.join(['apples', 'oranges', 'pears'])trước khi gửi nó đến mẫu làm dữ liệu ngữ cảnh.

CẬP NHẬT:

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

Bạn sẽ nhận được apples, oranges and pearsđầu ra.


Điều đó mang lại "apples, oranges, pears". Đầu ra yêu cầu là "apples, oranges, and pears".
Alasdair,

Ồ, có vẻ như tôi đã bỏ lỡ nó. Tôi đã cập nhật câu trả lời của mình. Mời các bạn xem qua. @Alasdair
yigidix

1

Django không có hỗ trợ cho tính năng này. Bạn có thể xác định một bộ lọc tùy chỉnh cho điều này:

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

Tuy nhiên, nếu bạn muốn xử lý thứ gì đó phức tạp hơn chỉ là danh sách các chuỗi, bạn sẽ phải sử dụng một {% for x in y %}vòng lặp rõ ràng trong mẫu của mình.


0

Nếu bạn thích một lớp lót:

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

và sau đó trong mẫu:

{{ fruits|lineup }}

0

Tôi nghĩ giải pháp đơn giản nhất có thể là:

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.