Become a sponsor

分类管理
分类管理模块用于管理树形分类数据,如文章分类、产品分类等。通过 parent_id 字段实现无限级分类。
application/category/
├── models.py # 分类模型
├── forms.py # 表单验证
├── services.py # 业务逻辑层
├── views.py # 视图层
└── urls.py # 路由配置# application/category/models.py
from application.models import BaseModel
class Category(BaseModel):
"""文章分类模型类"""
class Meta:
db_table = 'django_category'
category_name = models.CharField(max_length=100, verbose_name='分类名称')
category_code = models.CharField(max_length=100, null=True, blank=True, verbose_name='分类编码')
parent_id = models.IntegerField(default=0, verbose_name='上级ID')
sort = models.IntegerField(default=0, verbose_name='排序')
note = models.CharField(max_length=100, null=True, blank=True, verbose_name='分类备注')字段说明
category_name:分类名称,带索引用于模糊搜索category_code:分类编码,可选字段,用于程序化标识分类parent_id:上级分类 ID,0 表示顶级分类分类通过 parent_id 字段形成无限级树形结构:
文章分类 (parent_id=0)
+-- 技术文章 (parent_id=1)
| +-- 前端技术 (parent_id=2)
| | +-- Vue (parent_id=5)
| | +-- React (parent_id=5)
| +-- 后端技术 (parent_id=2)
| +-- Django (parent_id=6)
| +-- Flask (parent_id=6)
+-- 产品动态 (parent_id=1)
+-- 公司新闻 (parent_id=1)分类树构建采用非递归方案(children_map 分组),性能优于递归:
def get_tree(data, parent_id):
"""获取树状结构(非递归,children_map 分组)"""
children_map = {}
for item in data:
pid = item["parentId"]
if pid not in children_map:
children_map[pid] = []
children_map[pid].append(item)
def build(pid):
result = []
for item in children_map.get(pid, []):
item["children"] = build(item["id"])
result.append(item)
return result
return build(parent_id)1. 查询所有分类数据
2. 按 parent_id 分组到 children_map
3. 从 parent_id=0 开始递归构建树
4. 每个节点添加 children 字段删除分类前会检查是否存在子级分类:
def delete_categories(category_ids):
"""删除分类(批量删除)"""
id_list = [int(id_str.strip()) for id_str in category_ids.split(',')]
# 检查是否有子级分类
has_children = Category.objects.filter(
parent_id__in=id_list, is_delete=False
).exists()
if has_children:
return R.failed("请先删除子级分类")
# 批量逻辑删除
updated_count = Category.objects.filter(
id__in=id_list, is_delete=False
).update(is_delete=True)
return R.ok(msg=f"本次共删除{updated_count}条数据")删除规则
分类采用自下而上的删除策略:必须先删除最底层的子分类,再删除父分类。这保证了数据的完整性。
更新分类时需要校验循环引用,避免 A→B→C→A 的死循环:
def update_category(request):
"""更新分类(含循环引用校验)"""
# 校验:不能将分类设置为自己的子级
if parent_id == category_id:
return R.failed("上级分类不能是自己")
# 校验:不能将分类设置为自己子级的子级
if _is_descendant(category_id, parent_id):
return R.failed("上级分类不能是当前分类的子级")| 接口 | 方法 | 权限节点 | 说明 |
|---|---|---|---|
/category/list | GET | sys:category:list | 分类列表(树形) |
/category/detail/<int:category_id> | GET | sys:category:detail | 分类详情 |
/category/add | POST | sys:category:add | 新增分类 |
/category/update | PUT | sys:category:update | 编辑分类 |
/category/delete/<int:category_ids> | DELETE | sys:category:delete | 删除分类(支持批量) |
分类选择通常使用 el-tree-select 组件:
<template>
<el-tree-select
v-model="form.category_id"
:data="categoryTree"
:props="{ label: 'categoryName', value: 'id' }"
placeholder="请选择分类"
check-strictly
/>
</template>分类列表页使用树形表格展示:
<template>
<BasicTable :data="categoryTree" row-key="id" default-expand-all>
<el-table-column prop="categoryName" label="分类名称" />
<el-table-column prop="categoryCode" label="分类编码" />
<el-table-column prop="sort" label="排序" />
</BasicTable>
</template>支持拖拽调整分类层级和顺序:
<el-tree
:data="categoryTree"
draggable
allow-drop="allowDrop"
@node-drop="handleDrop"
/>分类管理模块具备以下特点:
1. 树形结构:parent_id 实现无限级分类,非递归构建
2. 通用设计:适用于文章分类、产品分类等场景
3. 分类编码:category_code 字段支持程序化标识
4. 排序控制:sort 字段控制分类显示顺序
5. 删除校验:删除前检查子级,保证数据完整性
6. 循环引用校验:更新时防止 A→B→C→A 死循环
7. 前端集成:el-tree-select、树形表格、拖拽排序