在Django中如何按年份显示对象列表
我有这些模型:
class Year(models.Model):
name = models.CharField(max_length=15)
date = models.DateField()
class Period(models.Model):
name = models.CharField(max_length=15)
date = models.DateField()
class Notice(models.Model):
year = models.ForeignKey(Year)
period = models.ForeignKey(Period, blank=True, null=True)
text = models.TextField()
order = models.PositiveSmallIntegerField(default=1)
在我的视图中,我想按年份和时期显示所有通知,但如果某个通知的年份和/或时期与前一个相同,我就不想重复显示。我想要这样的结果:
1932-1940
仲夏
通知1的内容 ...
通知2的内容 ...
九月
- 通知3的内容 ...
1950
一月
- 通知4的内容 ...
等等。
我找到了一种解决方案,通过遍历所有行来构建嵌套列表,像这样:
years = [('1932-1940', [
('Mid-summer', [Notice1, Notice2]),
('September', [Notice3])
]),
('1950', [
('January', [Notice4])
])
]
这是视图中的代码:
years = []
year = []
period = []
prev_year = ''
prev_period = ''
for notice in Notice.objects.all():
if notice.year != prev_year:
prev_year = notice.year
year = []
years.append((prev_year.name, year))
prev_period = ''
if notice.periode != prev_period:
prev_period = notice.periode
period = []
if prev_period:
name = prev_period.name
else:
name = None
year.append((name, period))
period.append(notice)
但这样做速度慢且不够优雅。有什么好的方法可以做到这一点呢?
如果我能在模板中设置一些变量,我就可以遍历所有通知,并通过检查它们是否与前一个相同来只打印年份和时期。但在模板中无法设置临时变量。
2 个回答
1
你的问题其实并不难,主要是因为你遇到了不太好的数据结构设计。我会忽略Year
这个类来回答你的问题,因为我不太明白它和时间段逻辑之间的关系。
简单来说,在模板里,你可以放:
{% for period in periods %}
period.name
{% for notice in period.notice_set.all %}
notice.text
{% endfor %}
{% endfor %}
这样做完全没有考虑排序,所以如果你愿意,可以在你的Period
模型里定义:
def order_notices(self):
return self.notice_set.order_by('order')
然后使用
{% for period in periods %}
period.name
{% for notice in period.order_notices %}
notice.text
{% endfor %}
{% endfor %}
如果你一定要用年份,我强烈建议在Year
模型里定义一个方法,形式如下:
def ordered_periods(self):
... #your logic goes here
... #should return an iterable (list or queryset) or periods
然后在你的模板里:
{% for year in years %}
year.name
{% for period in year.ordered_periods %}
period.name
{% for notice in period.order_notices %}
notice.text
{% endfor %}
{% endfor %}
补充说明:请记住,成功的关键在于模板只能调用那些不需要任何参数(除了self)的函数。
2
幸运的是,Django 有一些内置的模板标签可以帮助你。你可能最想用的是 regroup:
{% regroup notices by year as year_list %}
{% for year in year_list %}
<h2>{{ year.grouper }}<h2>
<ul>
{% for notice in year.list %}
<li>{{ notice.text }}</li>
{% endfor %}
</ul>
{% endfor %}
还有一个 {% ifchanged %}
,它可以在遍历列表时帮助你处理某个值保持不变的情况。