如何在Django模板中按序渲染OrderedDict数据为表格
我之前也遇到过类似的Django模板渲染分组表格的需求,给你两种可行的方案,优先推荐第一种——逻辑更清晰,调试也更方便:
方案一:视图层预处理数据(推荐)
与其在模板里折腾复杂的逻辑,不如先在Python视图里把你的OrderedDict数据转换成模板需要的结构化分组格式,再传给模板渲染。这样模板只需要负责展示,代码更易维护。
假设你的数据里,quantity_balance为正代表买入(Buys)、为负代表卖出(Sells),我们可以按日期聚合每个时段的买入、卖出和总余额:
from collections import OrderedDict from django.shortcuts import render def your_view(request): # 这是你已有的OrderedDict数据 original_table = OrderedDict([ ('1', {'date': '2024-01-01', 'document': 'INV-001', 'quantity_balance': 100}), ('2', {'date': '2024-01-01', 'document': 'INV-002', 'quantity_balance': -50}), ('3', {'date': '2024-01-02', 'document': 'INV-003', 'quantity_balance': 200}), ]) # 按日期分组,整理Buys、Sells、Total数据 grouped_data = OrderedDict() for item in original_table.values(): date = item['date'] # 初始化该日期的分组结构 if date not in grouped_data: grouped_data[date] = {'buys': [], 'sells': [], 'total': 0} # 归类买入/卖出数据 if item['quantity_balance'] > 0: grouped_data[date]['buys'].append({ 'doc': item['document'], 'qty': item['quantity_balance'] }) else: grouped_data[date]['sells'].append({ 'doc': item['document'], 'qty': abs(item['quantity_balance']) }) # 累计总余额 grouped_data[date]['total'] += item['quantity_balance'] # 把处理好的分组数据传给模板 return render(request, 'your_template.html', {'grouped_table': grouped_data})
然后在模板里直接渲染结构化数据即可:
<table border="1" cellpadding="8" cellspacing="0"> <thead> <tr> <th>Date</th> <th>Buys</th> <th>Sells</th> <th>Total Balance</th> </tr> </thead> <tbody> {% for date, data in grouped_table.items %} <tr> <td>{{ date }}</td> <td> {% for buy in data.buys %} <p>{{ buy.doc }}: {{ buy.qty }}</p> {% empty %} <p>-</p> {% endfor %} </td> <td> {% for sell in data.sells %} <p>{{ sell.doc }}: {{ sell.qty }}</p> {% empty %} <p>-</p> {% endfor %} </td> <td><strong>{{ data.total }}</strong></td> </tr> {% endfor %} </tbody> </table>
方案二:自定义模板过滤器(适合简单过滤场景)
如果你坚持要在模板里处理数据,可能是之前的自定义标签写法有问题。可以试试写一个专用的过滤器来筛选Buys/Sells数据:
- 在你的app目录下创建
templatetags文件夹,里面新建table_filters.py和__init__.py文件:
from django import template register = template.Library() @register.filter def filter_transactions(items, trans_type): """筛选买入或卖出数据,trans_type传'buys'或'sells'""" filtered = [] for item in items: if trans_type == 'buys' and item['quantity_balance'] > 0: filtered.append(item) elif trans_type == 'sells' and item['quantity_balance'] < 0: # 复制并转换负数为正数显示 item_copy = item.copy() item_copy['quantity_balance'] = abs(item_copy['quantity_balance']) filtered.append(item_copy) return filtered @register.filter def calculate_total(items): """计算总余额""" return sum(item['quantity_balance'] for item in items)
- 在模板里加载过滤器并使用:
{% load table_filters %} <table border="1" cellpadding="8" cellspacing="0"> <thead> <tr> <th>Date</th> <th>Buys</th> <th>Sells</th> <th>Total Balance</th> </tr> </thead> <tbody> <!-- 这里假设你按日期去重后遍历,或者直接遍历原始数据的日期 --> {% for date in unique_dates %} {% with date_items=table.values|filter_by_date:date %} {# 你还需要补充一个按日期过滤的过滤器,写法类似上面的filter_transactions #} <tr> <td>{{ date }}</td> <td> {% for buy in date_items|filter_transactions:'buys' %} <p>{{ buy.document }}: {{ buy.quantity_balance }}</p> {% empty %} <p>-</p> {% endfor %} </td> <td> {% for sell in date_items|filter_transactions:'sells' %} <p>{{ sell.document }}: {{ sell.quantity_balance }}</p> {% empty %} <p>-</p> {% endfor %} </td> <td><strong>{{ date_items|calculate_total }}</strong></td> </tr> {% endwith %} {% endfor %} </tbody> </table>
小提示
- 优先用方案一,Python处理数据比模板标签灵活得多,调试起来也更方便;
- 如果你的分组逻辑和我假设的(按正负判断买卖)不一样,只需要修改视图里的归类条件即可;
OrderedDict在Django模板里可以直接用{% for key, value in table.items %}迭代。
内容的提问来源于stack exchange,提问作者Jose Amair
相关产品推荐
相关产品推荐

