背景
之前写过一个博客项目,在归档页面用Django的标签regroup
实现了按年分组之后在按月分组的功能,本次在开发Todo任务项目的时候,给任务做了分组,想在首页实现分组展示,同时的使用regroup
标签,却无法实现分组。
确认用法是没有问题的。那原因就出现在被分组的对象上
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
函数说
Python 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 任务首页如下
django-todo-index-v1.png项目代码地址
Todo项目V1版本,适用于新手学习,如果觉得不错希望不吝点赞关注哦~
如果觉得文章对你有所帮忙,欢迎点赞收藏,或者可以关注个人公众号 全栈运维
哦,一起学习,一起健身~
网友评论