Skip to content

分类管理

分类管理模块用于管理树形分类数据,如文章分类、产品分类等。通过 parent_id 字段实现无限级分类。

模块结构

application/category/
├── models.py     # 分类模型
├── forms.py      # 表单验证
├── services.py   # 业务逻辑层
├── views.py      # 视图层
└── urls.py       # 路由配置

分类模型

python
# 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 分组),性能优于递归:

python
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 字段

删除分类(带子级校验)

删除分类前会检查是否存在子级分类:

python
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 的死循环:

python
def update_category(request):
    """更新分类(含循环引用校验)"""
    # 校验:不能将分类设置为自己的子级
    if parent_id == category_id:
        return R.failed("上级分类不能是自己")

    # 校验:不能将分类设置为自己子级的子级
    if _is_descendant(category_id, parent_id):
        return R.failed("上级分类不能是当前分类的子级")

API 接口

接口方法权限节点说明
/category/listGETsys:category:list分类列表(树形)
/category/detail/<int:category_id>GETsys:category:detail分类详情
/category/addPOSTsys:category:add新增分类
/category/updatePUTsys:category:update编辑分类
/category/delete/<int:category_ids>DELETEsys:category:delete删除分类(支持批量)

前端使用

树形选择组件

分类选择通常使用 el-tree-select 组件:

vue
<template>
    <el-tree-select
        v-model="form.category_id"
        :data="categoryTree"
        :props="{ label: 'categoryName', value: 'id' }"
        placeholder="请选择分类"
        check-strictly
    />
</template>

树形表格

分类列表页使用树形表格展示:

vue
<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>

拖拽排序

支持拖拽调整分类层级和顺序:

vue
<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、树形表格、拖拽排序

小蚂蚁云团队 · 提供技术支持