
之前写过一个博客项目,在归档页面用Django的标签regroup 实现了按年分组之后在按月分组的功能,本次在开发Todo任务项目的时候,给任务做了分组,想在首页实现分组展示,同时的使用regroup 标签,却无法实现分组。
确认用法是没有问题的。那原因就出现在被分组的对象上
通过查看源码了解到
1、regroup 是定义在 django.template.defaulttags 中函数,其核心是调用 RegroupNode 函数
@register.tag
def regroup(parser, token):
... ...
return RegroupNode(target, expression, var_name)
2、RegroupNode 执行 render函数,其核心是调用 python的内部方法 groupby 给 var_name 赋值(var_name 就是使用regroup的时候最后的as xxx)
3、所以问题的核心是在使用 groupby 函数说
1、我们先用一个普通对象来介绍下groupby的用法
todos_v2 = [
{'group': 'group-1', 'name': 'todo-1-1'},
{'group': 'group-1', 'name': 'todo-1-2'},
{'group': 'group-2', 'name': 'todo-2-1'},
{'group': 'group-2', 'name': 'todo-2-2'},
{'group': 'group-3', 'name': 'todo-3-1'}
]
for k, v in groupby(todos_v2, key=lambda x: x.get('group')):
print(k, v)
for item in v:
print(" ", item)
输出的结果是:
group-1 <itertools._grouper object at 0x102e21c40>
{'group': 'group-1', 'name': 'todo-1-1'}
{'group': 'group-1', 'name': 'todo-1-2'}
group-2 <itertools._grouper object at 0x102dfe5e0>
{'group': 'group-2', 'name': 'todo-2-1'}
{'group': 'group-2', 'name': 'todo-2-2'}
group-3 <itertools._grouper object at 0x102e21c40>
{'group': 'group-3', 'name': 'todo-3-1'}
看到是有效分组的。
2、groupby 的源码是在python的itertools.py文件中 369行开始。其继承 object 实现了一些私有方法,
核心代码
@staticmethod # known case of __new__
def __new__(*args, **kwargs): # real signature unknown
""" Create and return a new object. See help(type) for accurate signature. """
pass
关于 groupby 的使用说明:
itertools.groupby
@overload
def __new__(cls,
iterable: Iterable[_T1],
key: None = ...) -> groupby[_T1, _T1]
Create and return a new object. See help(type) for accurate signature.
从上面提到的博客项目(按created_time分组, created_time是Post对象的中的一个普通字段)和任务项目(按group分组,group是外键关联字段) 做regroup分组的时候,唯一的区别是分组对象不同。
另外知道在对QuerySet分组排序的时候,key对应的不能是instance ,也从侧面说明 问题出现在 外键字段上。
比如
from todo.models import Todo
todos = Todo.objects.all()
# 如下会报错, obj.group 是 group instance
# TypeError: '<' not supported between instances of 'TodoGroup' and 'TodoGroup'
# todos_sorted = sorted(todos, key=lamba obj: obj.group)
# 如下正确
todos_sorted_1 = sorted(todos, key=lamba obj: obj.group.name)
todos_sorted_2 = sorted(todos, key=lamba obj: obj.status)
3、结合上面提到的RegroupNode 代码中的groupby用法,核心在于key 参数。
所以对于特殊的分组 建立在外键字段上的 regroup,可以通过对
queryset 列表进行在外键字段属性上的排序结果传给 template进行regroup分组
所以最终的代码实现如下:
# views.py
def todo_list(request):
param = request.GET.get('status')
if param:
todos = Todo.objects.filter(is_deleted=False).filter(status__exact=int(param))
else:
todos = Todo.objects.filter(is_deleted=False)
todos_sorted = sorted(todos, key=lambda x: x.group.name)
return render(request, 'todo/list.html', {'todos': todos_sorted})
# todo/list.html
... ...
{% regroup todos by group as group_list %}
{% for group in group_list %}
{{ group.grouper }}
{% for todo in group.list %}
{{ todo.name }}
{% endfor %}
{% endfor %}
... ...
最终的TODO 任务首页如下

项目代码地址
Todo项目V1版本,适用于新手学习,如果觉得不错希望不吝点赞关注哦~
如果觉得文章对你有所帮忙,欢迎点赞收藏,或者可以关注个人公众号 全栈运维 哦,一起学习,一起健身~