美文网首页
django regroup对象是外键时无法正常使用

django regroup对象是外键时无法正常使用

作者: 菩提老鹰 | 来源:发表于2022-08-06 16:15 被阅读0次
    晚霞

    背景

    之前写过一个博客项目,在归档页面用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的内部方法 groupbyvar_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版本,适用于新手学习,如果觉得不错希望不吝点赞关注哦~


    如果觉得文章对你有所帮忙,欢迎点赞收藏,或者可以关注个人公众号 全栈运维 哦,一起学习,一起健身~

    相关文章

      网友评论

          本文标题:django regroup对象是外键时无法正常使用

          本文链接:https://www.haomeiwen.com/subject/nfhvwrtx.html