我不时会使用Django编写一些简单的摘要报告。
首先,我尝试使用Django ORM聚合和交错结果,但它可能会有点混乱,我放松了所有的ORM懒惰 - 只是感觉不对。
最近我写了一个泛型迭代器类,可以对数据集进行分组/汇总。在视图中它的工作方式如下:
s_data = MyIterator(dataset, group_by='division', \
sum_fields=[ 'sales', 'travel_expenses'])
在模板中,它的工作方式如下:
{% for g, reg in s_data %}
{% if g.group_changed %}
<tr><!-- group summary inside the loop -->
<td colspan="5">{{ g.group }} Division</td>
<td>{{ g.group_summary.sales }}</td>
<td>{{ g.group_summary.travel_expenses }}</td>
</tr>
{% endif %}
<tr><!-- detail report lines -->
<td>{{ reg.field }}<td>
<td>{{ reg.other_field_and_so_on }}<td>
...
</tr>
{% endfor %}
<tr><!-- last group summary -->
<td colspan="5">{{ s_data.group }} Division</td>
<td>{{ s_data.group_summary.sales }}</td>
<td>{{ s_data.group_summary.travel_expenses }}</td>
</tr>
<tr>
<td colspan="5">Total</td>
<td>{{ s_data.summary.sales }}</td>
<td>{{ s_data.travel_expenses }}</td>
</tr>
我认为它比我之前的方法更优雅,但不得不重复最后一组摘要的代码违反了DRY原则。
我看过“杰拉尔多的报道”,但并不是“一见钟情”。
为什么没有组/摘要模板标签,我应该写一个吗?
答案 0 :(得分:0)
我可能在面前丢失了一些东西,但为什么你在循环之外有最后一组呢?
答案 1 :(得分:0)
我想通了,它可以用迭代器完成,不需要模板标签。诀窍是延迟迭代一个周期。
class GroupSummaryIterator(object):
"""GroupSummaryIterator(iterable, group_by, field_list)
- Provides simple one level group/general summary for data iterables.
- Suports both key and object based records
- Assumes data is previously ordered by "group_by" property *before* use
Parameters:
===========
iterable: iterable data
group_by: property or key name to group by
field_list: list of fileds do sum
Example:
======
data = [{'label': 'a', 'field_x': 1, 'field_c': 2},
{'label': 'a', 'field_x': 3, 'field_c': 4},
{'label': 'b', 'field_x': 1, 'field_c': 2},
{'label': 'c', 'field_x': 5, 'field_c': 6},
{'label': 'c', 'field_x': 1, 'field_c': 2}]
s = GroupSummaryIterator(data, 'label', ['field_x', 'field_c'])
for x,y in s:
print y
if x['group_changed']:
print x['group'], 'summary:', x['group_summary']
print 'general summary:', s.summary
"""
def __init__(self, iterable, group_by, field_list):
self.iterable = iterable
self.group_by = group_by
self.field_list = field_list
def _a(self, obj, key):
"""Get property or key value"""
if isinstance(key, basestring) and hasattr(obj, key):
return getattr(obj, key)
try:
return obj[key]
except:
return None
def _sum(self, item):
if self.group_changed:
self.cur_summary = dict()
for field in self.field_list:
value = self._a(item, field)
if not value:
value = 0.0
else:
value = float(value)
if self.summary.has_key(field):
self.summary[field] += value
else:
self.summary[field] = value
if self.cur_summary.has_key(field):
self.cur_summary[field] += value
else:
self.cur_summary[field] = value
def _retval(self, item, summary):
"""If each item from the source iterable is itself an iterable, merge
everything so you can do "for summ, a, b in i" where you would have
done "for a, b in i" without this object."""
if isinstance(item, dict):
retval = (item,)
else:
try:
retval = tuple(item)
except:
retval = (item,)
return (dict(group_changed=self.group_changed, group=self.group, group_summary=summary),) + retval
def __iter__(self):
self.cur_group = None
self.group = None
self.finished = False
self.group_changed = False
self.cur_item = None
self.last_item = None
self.summary = dict()
self.group_summary = dict()
self.cur_summary = dict()
for item in self.iterable:
self.group = self.cur_group
self.group_summary = self.cur_summary
self.cur_group = self._a(item, self.group_by)
self.group_changed = self.group and self.cur_group != self.group
self.last_item = self.cur_item
self.cur_item = item
self._sum(item)
if self.last_item is None:
continue
yield self._retval(self.last_item, self.group_summary)
if self.cur_item:
self.group_changed = True
yield self._retval(self.cur_item, self.group_summary)