django的model中多个分割字段转为tree_json‘name‘‘children

文章描述了一个使用Django框架中的models类来定义的数据库结构,包括字段如CharField和ForeignKey,以及创建了一个自定义方法department_call_back用于构建部门层级树。

models

class Department(models.Model):
    serial_number = models.CharField(max_length=100,blank=True,null=True, verbose_name="序号")
    name = models.CharField(max_length=100,blank=True,null=True, verbose_name="名称",unique=True)
    name_0 = models.CharField(max_length=100,blank=True,null=True, default='河南营',verbose_name="名称_0")
    name_1 = models.CharField(max_length=100,blank=True,null=True,default='',verbose_name="名称_1")
    name_2 = models.CharField(max_length=100,blank=True,null=True,default='',verbose_name="名称_2")
    name_3 = models.CharField(max_length=100,blank=True,null=True,default='',verbose_name="名称_3")
    name_4 = models.CharField(max_length=100,blank=True,null=True,default='',verbose_name="名称_4")
    name_5 = models.CharField(max_length=100,blank=True,null=True,default='',verbose_name="名称_5")
    name_6 = models.CharField(max_length=100,blank=True,null=True,default='',verbose_name="名称_6")
    name_7 = models.CharField(max_length=100,blank=True,null=True,default='',verbose_name="名称_7")
    short_name = models.CharField(max_length=100,blank=True,null=True, verbose_name="简称")
    content = models.CharField(max_length=1000,blank=True,null=True, verbose_name="职责")
    remember = models.CharField(max_length=1000,blank=True,null=True, verbose_name="成员")
    parent_name = models.CharField(max_length=100,blank=True,null=True, verbose_name="上级名称")
    main_position_num = models.CharField(max_length=100,blank=True,null=True, verbose_name="正职职数")
    deputy_position_num = models.CharField(max_length=100,blank=True,null=True, verbose_name="副职职数")
    member_position_num = models.SmallIntegerField(default=0,blank=True,null=True, verbose_name="岗位职数")
    file_company = models.CharField(max_length=100,blank=True,null=True, verbose_name="发文单位")
    priority = models.CharField(max_length=20,blank=True,null=True,verbose_name='优先级')
    remark = models.TextField(max_length=1000,blank=True,null=True,default="",verbose_name='备注')

    parent = models.ForeignKey(to='self',blank=True,null=True,db_constraint=False,on_delete=models.SET_NULL,verbose_name='上级')
    leaf = models.SmallIntegerField(default=1,verbose_name='叶子')

views


def department_call_back():
    objs = models.Department.objects.exclude(name__contains='相关')[0:]
    root = {'name':'河南','id':170,'children':[]}
    for obj_1 in objs:
        index_1 = 0
        if len(obj_1.name_1)>0  and obj_1.id != 170:
            children_1_name_list = [f['name'] for f in root['children']]
            if obj_1.name_1 not in children_1_name_list:
                root['children'].append({'name':obj_1.name_1,'id':obj_1.id,'children':[]})

    children_1_name_list = [f['name'] for f in root['children']]
    for obj_2 in objs:
        if len(obj_2.name_2)>0:
            index_1 = children_1_name_list.index(obj_2.name_1)
            children_2_name_list = [f['name'] for f in root['children'][index_1]['children']]
            if obj_2.name_2 not in children_2_name_list:
                root['children'][index_1]['children'].append({'name':obj_2.name_2,'id':obj_2.id,'children':[]})

            children_2_name_list = [f['name'] for f in root['children'][index_1]['children']]
            for obj_3 in objs:
                if len(obj_3.name_3)>0 and obj_3.name_2 == obj_2.name_2 and obj_3.name_1 == obj_2.name_1:
                    index_2 = children_2_name_list.index(obj_3.name_2)
                    children_3_name_list = [f['name'] for f in root['children'][index_1]['children'][index_2]['children']]
                    if len(obj_3.name_3)>0 and (obj_3.name_3 not in children_3_name_list):
                        root['children'][index_1]['children'][index_2]['children'].append({'name':obj_3.name_3,'id':obj_3.id,'children':[]})
                    
                    children_3_name_list = [f['name'] for f in root['children'][index_1]['children'][index_2]['children']]
                    for obj_4 in objs:
                        if len(obj_4.name_3)>0 and obj_4.name_3 == obj_3.name_3 and obj_4.name_2 == obj_3.name_2 and obj_4.name_1 == obj_3.name_1:
                            index_3 = children_3_name_list.index(obj_4.name_3)
                            children_4_name_list = [f['name'] for f in root['children'][index_1]['children'][index_2]['children'][index_3]['children']]
                            if len(obj_4.name_4)>0 and (obj_4.name_4 not in children_4_name_list):
                                root['children'][index_1]['children'][index_2]['children'][index_3]['children'].append({'name':obj_4.name_4,'id':obj_4.id,'children':[]})
                            
                            children_4_name_list = [f['name'] for f in root['children'][index_1]['children'][index_2]['children'][index_3]['children']]
                            for obj_5 in objs:
                                if len(obj_5.name_4)>0 and obj_5.name_4 == obj_4.name_4 and obj_5.name_3 == obj_4.name_3 and obj_5.name_2 == obj_4.name_2 and obj_5.name_1 == obj_4.name_1:
                                    index_4 = children_4_name_list.index(obj_5.name_4)
                                    children_5_name_list = [f['name'] for f in root['children'][index_1]['children'][index_2]['children'][index_3]['children'][index_4]['children']]
                                    if len(obj_5.name_5)>0 and (obj_5.name_5 not in children_5_name_list):
                                        root['children'][index_1]['children'][index_2]['children'][index_3]['children'][index_4]['children'].append({'name':obj_5.name_5,'id':obj_5.id,'children':[]})

                                    children_5_name_list = [f['name'] for f in root['children'][index_1]['children'][index_2]['children'][index_3]['children'][index_4]['children']]
                                    for obj_6 in objs:
                                        if len(obj_6.name_5)>0 and obj_6.name_5 == obj_5.name_5 and obj_6.name_4 == obj_5.name_4 and obj_6.name_3 == obj_5.name_3 and obj_6.name_2 == obj_5.name_2 and obj_6.name_1 == obj_5.name_1:
                                            index_5 = children_5_name_list.index(obj_6.name_5)
                                            children_6_name_list = [f['name'] for f in root['children'][index_1]['children'][index_2]['children'][index_3]['children'][index_4]['children'][index_5]['children']]
                                            if len(obj_6.name_6)>0 and (obj_6.name_6 not in children_6_name_list):
                                                root['children'][index_1]['children'][index_2]['children'][index_3]['children'][index_4]['children'][index_5]['children'].append({'name':obj_6.name_6,'id':obj_6.id,'children':[]})

    return root
        

<think>我们有一个Category模型,假设它有以下字段(根据常见的树形结构设计): - id - name: 分类名称 - parent: 外键指向自身('self'),表示父类别,如果为一级类别则parent为null 目标:生成一个树形下拉选项框,使用Layui的select组件,并且支持树形结构(通常使用optgroup分组,但layui的select不支持树形,所以我们需要使用其他方式。但Layui本身并没有树形下拉组件,我们可以使用第三方插件如xmSelect或者使用layui的扩展。不过,根据要求,我们使用原生的layui select组件只能通过分组来实现一级和二级,但这样二级不能继续展开,只能显示两级。或者我们可以使用树形菜单组件,但这里要求生成select的树形下拉选项框,所以我们需要一个支持树形的下拉选择器。 然而,Layui 2.7原生select不支持树形结构。因此,我们需要借助第三方树形下拉插件,或者使用Layui扩展。但考虑到问题中指定使用Layui 2.7,并且要求生成select树形下拉选项框,这里我们可以使用xmSelect(一个流行的多选下拉树插件)或者使用layui的formSelects(来自第三方扩展)?但是,由于问题没有指定具体插件,且要求使用select,我们可以这样处理: 方案1:使用optgroup进行分组(只支持两级,且不能有选择事件区分层级) 方案2:使用树形结构的下拉插件(如xmSelect) 根据实际需求,这里用户希望提取一级类别和二级类别,生成树形下拉选项框。由于Layui原生select不支持树形,我们采用一个折中方案:使用optgroup将二级类别分组到一级类别下。这样虽然不能叫完全的树形(因为只能展开一级分组),但能够清晰展示层级关系。 但是,请注意:使用optgroup时,optgroup的label属性用于显示一级类别名称,里面的option就是二级类别。但是,用户不能选择一级类别(因为optgroup不能作为选项),只能选择二级类别。如果一级类别也需要作为可选项,则不能使用optgroup。 所以,我们需要明确:是否一级类别也要作为可选项?还是仅作为分组? 通常,在分类选择中,我们可能希望既能选一级也能选二级。那么optgroup的方式就不满足要求了。 因此,我们可以考虑另一种常见的做法:用缩进来表示层级关系。即: <option value="一级id">一级名称</option> <option value="二级id"> 二级名称</option> <!-- 二级前面加空格或特殊符号 --> 这样,我们就可以在同一个select中同时选择一级和二级。但是,这样也有弊端:没有树形的折叠效果。 考虑到这些,如果我们必须使用树形结构(可折叠),那么就需要引入树形下拉组件。但Layui原生没有,所以我们可以采用第三方基于Layui的树形下拉组件,或者使用其他UI组件。但是题目要求使用Layui 2.7,并且没有说明可以引入其他插件,所以我们这里先提供两种方案: 方案A(简单缩进):使用空格缩进表示层级(适用于简单的两级) 方案B(使用树形下拉插件):引入第三方树形下拉组件(如xmSelect,但需要额外引入资源) 由于问题描述中未明确是否可以引入第三方插件,且要求生成select的树形下拉选项框,这里我们先按照方案A来实现(因为方案A不需要额外插件,使用原生select即可),虽然不够美观但功能可用。如果后续需要树形交互,再引入插件。 另外,我们也可以使用layui的formSelects插件(这是一个第三方扩展,支持树形),但需要单独引入。由于问题没有明确,我们这里先不使用。 因此,我们选择方案A:用空格缩进表示层级关系。 步骤: 1. 从数据库中获取所有类别,并分为两级:一级类别(parent为None)和二级类别(parent非None)。 2. 构造一个列表,先遍历一级类别,然后遍历每个一级类别的二级子类别(在二级类别中过滤)。 3. 在生成二级类别的选项时,在名称前加上空格(例如:全角空格两个,或者四个半角空格)来表示层级。 注意:如果一级类别也可以被选择,那么一级类别也要作为选项,二级类别则缩进显示。 假设我们的Category模型如下: ```python class Category(models.Model): name = models.CharField(max_length=50) parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children') ``` 视图函数(views.py)中获取数据并传递到模板: ```python def category_view(request): # 获取所有一级类别(没有父类别) top_categories = Category.objects.filter(parent__isnull=True) # 获取所有二级类别(有父类别,且父类别是一级类别) # 注意:我们这里只考虑两级,如果有更多级,这里需要调整 categories_list = [] for cat in top_categories: # 添加一级类别 categories_list.append({ 'id': cat.id, 'name': cat.name, 'level': 0 }) # 获取该一级类别的所有直接子类别(二级) children = cat.children.all() # 使用related_name='children' for child in children: categories_list.append({ 'id': child.id, 'name': child.name, 'level': 1 }) return render(request, 'your_template.html', {'categories': categories_list}) ``` 在模板(your_template.html)中,我们用select展示: ```html <select name="category" lay-verify="required"> <option value="">请选择分类</option> {% for cat in categories %} <option value="{{ cat.id }}" {% if cat.level == 1 %} style="padding-left: 20px;" {% endif %}> {% if cat.level == 1 %}  {% endif %}{{ cat.name }} </option> {% endfor %} </select> ``` 这里,我们在二级类别前面加了两个全角空格(或者可以用两个普通空格,但为了明显,用全角空格)。同时,我们也可以通过样式(padding-left)来缩进。 但是,请注意:这种方式在视觉上可以区分层级,但用户无法折叠/展开。 如果我们希望使用树形下拉(可折叠),那么就需要使用JavaScript插件。考虑到Layui没有自带,我们可以使用第三方插件xmSelect。下面提供使用xmSelect的示例: 1. 引入xmSelect资源(CSS和JS) 2. 在Django模板中生成符合xmSelect需要的数据结构(树形结构) 由于xmSelect支持树形结构,我们需要将数据组织成树形: 视图函数中: ```python def category_view(request): top_categories = Category.objects.filter(parent__isnull=True).prefetch_related('children') categories_tree = [] for cat in top_categories: node = { 'value': cat.id, 'name': cat.name, 'children': [] } for child in cat.children.all(): node['children'].append({ 'value': child.id, 'name': child.name }) categories_tree.append(node) return render(request, 'your_template.html', {'categories_tree': categories_tree}) ``` 然后在模板中使用xmSelect: 首先,在head中引入xmSelect(假设已经下载到静态目录或使用CDN): ```html <link href="https://cdn.jsdelivr.net/npm/xm-select@1.1.1/dist/xm-select.css" rel="stylesheet" /> <script src="https://cdn.jsdelivr.net/npm/xm-select@1.1.1/dist/xm-select.js"></script> ``` 然后,在页面中放置一个div作为容器: ```html <div id="category-select"></div> ``` 接着,使用JavaScript初始化xmSelect: ```html <script> var xmSelect = xmSelect.render({ el: '#category-select', model: { label: { type: 'text' } }, tree: { show: true, strict: false, // 是否严格遵守父子不互相关联 }, data: {{ categories_tree|safe }} }) </script> ``` 注意:这里我们直接将Django模板变量categories_tree传递给JavaScript,并使用|safe过滤器,因为数据是安全的(由我们构造的字典列表)。确保categories_tree确实是JSON可序列化的。 但是,上面的方式直接将Python列表转换为JavaScript对象,我们需要确保它在模板中输出为JSON。所以,更安全的做法是使用json_script: 在视图函数中,我们将数据JSON序列化: ```python import json def category_view(request): # ... 同上,构建categories_tree categories_tree_json = json.dumps(categories_tree) return render(request, 'your_template.html', {'categories_tree_json': categories_tree_json}) ``` 然后在模板中: ```html <div id="category-select"></div> {{ categories_tree_json|json_script:"categories-tree-data" }} <script> var treeData = JSON.parse(document.getElementById('categories-tree-data').textContent); var xmSelect = xmSelect.render({ el: '#category-select', model: { label: { type: 'text' } }, tree: { show: true, strict: false, // 是否严格遵守父子不互相关联 }, data: treeData }) </script> ``` 这样更安全。 综上所述,我们提供了两种方案。用户可以根据需要选择。 由于问题要求生成select树形下拉选项框代码,并且没有特别说明交互要求,我们可以先提供第一种简单方案(缩进方案)的完整代码,然后提供第二种方案(xmSelect)的示例。 但是,用户指定使用Layui 2.7,而我们第二种方案引入了第三方插件xmSelect,可能不符合要求。因此,如果用户希望完全使用Layui,那么第一种方案更合适。 现在,我们按照第一种方案(缩进)来写代码。 视图函数(views.py): ```python from django.shortcuts import render from .models import Category def category_select(request): # 获取所有一级类别并预取二级类别 categories = Category.objects.filter(parent__isnull=True).prefetch_related('children') # 构造列表:一级类别和二级类别(带有层级标记) category_list = [] for cat in categories: category_list.append({ 'id': cat.id, 'name': cat.name, 'level': 0 }) for child in cat.children.all(): category_list.append({ 'id': child.id, 'name': child.name, 'level': 1 }) return render(request, 'category_form.html', {'categories': category_list}) ``` 模板(category_form.html): ```html {% load static %} <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>分类选择</title> <!-- 引入Layui CSS --> <link rel="stylesheet" href="{% static 'layui/css/layui.css' %}"> </head> <body> <form class="layui-form" action=""> <div class="layui-form-item"> <label class="layui-form-label">分类选择</label> <div class="layui-input-block"> <select name="category" lay-verify="required"> <option value="">请选择分类</option> {% for cat in categories %} <option value="{{ cat.id }}">{% if cat.level == 1 %}  {% endif %}{{ cat.name }}</option> {% endfor %} </select> </div> </div> </form> <!-- 引入Layui JS --> <script src="{% static 'layui/layui.js' %}"></script> <script> layui.use('form', function(){ var form = layui.form; form.render(); // 渲染表单 }); </script> </body> </html> ``` 这样,我们就实现了一个简单的树形下拉(通过缩进区分层级)。注意:静态文件的路径需要根据实际项目调整。 如果用户需要更高级的树形下拉,则可以考虑使用xmSelect等第三方插件。但注意,xmSelect不是Layui的一部分,需要额外引入。 希望这个回答能够满足需求。 </think>根据您的需求,我将提供完整的Django视图和模板代码,实现从Category模型提取两级分类并生成Layui树形下拉选择框。 ### 模型假设(models.py) ```python from django.db import models class Category(models.Model): name = models.CharField('名称', max_length=50) parent = models.ForeignKey( 'self', on_delete=models.CASCADE, null=True, blank=True, related_name='children' ) def __str__(self): return self.name ``` ### 视图函数(views.py) ```python from django.shortcuts import render from .models import Category def category_select(request): # 获取一级分类(无父级)及其二级子分类 top_categories = Category.objects.filter(parent=None).prefetch_related('children') # 构建树形结构数据 category_tree = [] for cat in top_categories: children = [{ 'id': child.id, 'name': child.name } for child in cat.children.all()] category_tree.append({ 'id': cat.id, 'name': cat.name, 'children': children }) return render(request, 'select_demo.html', {'categories': category_tree}) ``` ### 模板文件(select_demo.html) ```html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>分类选择</title> <!-- 引入Layui CSS --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/layui@2.7.6/dist/css/layui.min.css"> </head> <body> <div class="layui-form"> <div class="layui-form-item"> <label class="layui-form-label">分类选择</label> <div class="layui-input-block"> <select name="category" lay-filter="category" id="category-select" lay-search> <option value="">请选择分类</option> {% for cat in categories %} <!-- 一级分类 --> <optgroup label="{{ cat.name }}"> <!-- 二级分类 --> {% for child in cat.children %} <option value="{{ child.id }}">{{ child.name }}</option> {% endfor %} <!-- 一级分类自身也作为可选项 --> <option value="{{ cat.id }}">[一级] {{ cat.name }}</option> </optgroup> {% endfor %} </select> </div> </div> </div> <!-- 引入Layui JS --> <script src="https://cdn.jsdelivr.net/npm/layui@2.7.6/dist/layui.min.js"></script> <script> layui.use('form', function(){ var form = layui.form; // 监听分类选择变化 form.on('select(category)', function(data){ console.log('已选择:', data.value); // 这里可以添加AJAX请求或其他操作 }); // 渲染表单元素 form.render(); }); </script> </body> </html> ``` ### 关键实现说明: 1. **数据准备**: - 使用`prefetch_related`优化查询效率,避免N+1问题 - 构建树形数据结构:一级分类包含children数组存放二级分类 2. **模板渲染**: - 使用`<optgroup>`标签创建一级分类分组 - 二级分类作为分组内的`<option>` - 一级分类自身也添加为可选项(标注"[一级]"前缀) 3. **Layui功能**: - `lay-filter`监听选择变化 - `lay-search`启用搜索功能 - `form.render()`确保动态内容正确渲染 4. **注意事项**: - 确保URL配置正确指向视图函数 - 如果分类层级超过两级,需要调整数据获取逻辑 - 实际使用时需替换静态资源路径为本地路径 最终效果: - 下拉框显示树形分类结构 - 支持按分类名称搜索 - 一级分类显示为分组标题 - 二级分类显示在分组内 - 选择变化可在控制台查看 [^1]: 参考列表生成式数据提取逻辑 [^2]: Layui官方表单文档
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值