在Django模板中对相关项目进行排序


87

是否可以在DJango模板中对一组相关项目进行排序?

也就是说:此代码(为清晰起见,省略了HTML标记):

{% for event in eventsCollection %}
   {{ event.location }}
   {% for attendee in event.attendee_set.all %}
     {{ attendee.first_name }} {{ attendee.last_name }}
   {% endfor %}
 {% endfor %}

显示几乎完全是我想要的。我唯一想更改的是我要按姓氏排序的与会者列表。我试图说这样的话:

{% for event in events %}
   {{ event.location }}
   {% for attendee in event.attendee_set.order_by__last_name %}
     {{ attendee.first_name }} {{ attendee.last_name }}
   {% endfor %}
 {% endfor %}

las,上面的语法不起作用(它产生一个空列表),我想到的任何其他变体也没有(报告了很多语法错误,但没有喜悦)。

在我看来,我当然可以生成某种类型的已排序与会者列表,但这是一个丑陋且脆弱(我提到过丑陋)的解决方案。

不用说,但无论如何我还是会说,我仔细阅读了在线文档,搜索了Stack Overflow和django-user的档案,但没有发现任何帮助(啊,如果只有查询集是字典dictsort会做工作,但不是,不是)

==============================================

在接受Tawmas的回答后进行了编辑,以增加其他想法。


Tawmas完全按照我的介绍解决了这个问题-尽管解决方案不是我期望的。结果,我学到了一种可以在其他情况下使用的有用技术。

汤姆的答案提出了一种方法,我已经在我的OP中提到过,但暂时拒绝了这种方法“丑陋”。

“丑陋”是一种直觉,我想澄清一下这是怎么回事。这样做的时候,我意识到这是一个丑陋的方法,是因为我对将查询集传递到要呈现的模板的想法感到困惑。如果我放宽该要求,那么应该采取一种难看的方法。

我还没有尝试过,但是假设视图代码没有传递查询集,而是遍历了查询集,生成了事件列表,然后用WAS排序(或过滤,等等)。像这样:

eventCollection = []   
events = Event.object.[filtered and sorted to taste]
for event in events:
   event.attendee_list = event.attendee_set.[filtered and sorted to taste]
   eventCollection.append(event)

现在,模板变为:

{% for event in events %}
   {{ event.location }}
   {% for attendee in event.attendee_list %}
     {{ attendee.first_name }} {{ attendee.last_name }}
   {% endfor %}
 {% endfor %}

缺点是视图必须立即“实现”所有事件,如果存在大量事件,这可能会成为问题。当然,可以增加分页功能,但这会使视图变得相当复杂。

好的一面是视图所属的视图中的“准备要显示的数据”代码,使模板着重于格式化视图提供的数据以进行显示。这是正确的。

因此,我的计划是将Tawmas的技术用于大型表,将上述技术用于小型表,并由读者来定义大与小的定义(笑)。

Answers:


135

您需要像这样在与会者模型中指定顺序。例如(假设您的模型类名为Attendee):

class Attendee(models.Model):
    class Meta:
        ordering = ['last_name']

请参阅手册以获取更多参考。

编辑。另一种解决方案是向事件模型添加属性,您可以从模板访问该属性:

class Event(models.Model):
# ...
@property
def sorted_attendee_set(self):
    return self.attendee_set.order_by('last_name')

您可以根据需要定义更多这些...


感谢您的评论,但这仅在我希望将显示订单定为与会者的永久财产时才有效,而我不是。例如,我可能希望显示按与会者注册日期排序的与会者,因此我知道应该通知哪些与会者没有空位。
戴尔·威尔逊

我为您添加了替代解决方案。它应该为您提供所需的灵活性。
tawmas 2011年

@Mark确实如此。据我了解@property是矫枉过正这里,因为没有getter方法或setter方法涉及:stackoverflow.com/questions/1554546/...
里克Westera

尽管模板并不是严格要求的,但我发现这里的@property可以从应用程序代码中进行更清晰的访问,尽管这会涉及到很小的性能损失(笔记本电脑上<30 ns)。当然,这是一个风格问题,而且非常主观。
tawmas

如果要针对两个属性对集合进行排序,请使用以下命令:class Meta:ordering = ['last_name','first_name']
Tms91

135

您可以使用模板过滤器dictsort https://docs.djangoproject.com/en/dev/ref/templates/builtins/#std:templatefilter-dictsort

这应该工作:

{% for event in eventsCollection %}
   {{ event.location }}
   {% for attendee in event.attendee_set.all|dictsort:"last_name" %}
     {{ attendee.first_name }} {{ attendee.last_name }}
   {% endfor %}
 {% endfor %}

22
太好了!而对于那些谁不知道,也有dictsortreverseddocs.djangoproject.com/en/dev/ref/templates/builtins/...
的Mickaël

1
引用我的原始帖子:啊,如果只有查询集是字典dictsort就能完成这项工作,但不是,也没有。
戴尔·威尔逊

我认为实际上应该少一些,让模型在获取记录之前处理排序。
acpmasquerade

1
@DaleWilson,实际上我dictsort几乎可以像您一样正确地处理代码。有趣的是,它似乎可以在查询集上很好地工作。
mlissner

2
只是为了进一步清楚起见,空格很重要: {% for attendee in event.attendee_set.all|dictsort:"last_name" %}对与会者{% for attendee in event.attendee_set.all | dictsort:"last_name" %}进行排序,但是尝试对for循环的输出进行排序并中断for
mattsl

3

一种解决方案是制作自定义模板:

@register.filter
def order_by(queryset, args):
    args = [x.strip() for x in args.split(',')]
    return queryset.order_by(*args)

像这样使用:

{% for image in instance.folder.files|order_by:"original_filename" %}
   ...
{% endfor %}

0

重新组合应该能够执行您想要的操作,但是是否有原因导致您无法按照想要的方式在视图中对它们进行排序?


要在视图中对它们进行排序,我需要遍历事件,并为每个事件创建一个容器,该容器以适当的排序顺序与该事件相关的某种参与者,然后将容器的整个集合以以下形式传递给模板:先给模板找到与会者的适当集合作为迭代通过事件。就像我说的,可以做到,但这不是一个好的解决方案。它要求视图与模板之间的耦合过多,并行迭代等。我希望可以更好地解决常见问题。
戴尔·威尔逊

我查看了重新组合,但它似乎没有执行我想要的操作。特别是文档说:[quote]注意{%regroup%}不会对输入进行排序!我们的示例基于以下事实:人员列表首先按性别排序。[endquote]
Dale Wilson
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.