通过Pythonのrest_framework&element-plusのCascader实现多级级联记录
需求层面:后端使用Django&rest_framework,前端使用vue3&element-plus,实现分类多级选择。有点儿类似省市区。
models
class Category(models.Model):
name = models.CharField(verbose_name='XX分类名称', max_length=100, help_text='请输入XX分类名称')
parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children',verbose_name='父级分类',)
create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
update_time = models.DateTimeField(verbose_name='更新时间', blank=True, null=True)
order_id = models.IntegerField(verbose_name='排序', default=0)
is_active = models.BooleanField(default=True, verbose_name='是否可用')
class Meta:
db_table = 'category'
verbose_name = 'XX分类'
verbose_name_plural = verbose_name
ordering = ['-order_id']
unique_together = ('parent', 'name')
def __str__(self):
if self.parent:
return f"{self.parent.name} - {self.name}"
return self.name
def save(self, *args, **kwargs):
# 对象已存在,执行更新操作
if self.pk is not None:
self.update_time = datetime.now()
super().save(*args, **kwargs)
serializers
from .models import Category
from rest_framework import serializers
class CategoryCascaderSerializer(serializers.ModelSerializer):
# Vue3 Cascader需要的value字段
value=serializers.CharField(source='id')
# Vue3 Cascader需要的label字段
label=serializers.CharField(source='name')
# 递归处理子分类
children=serializers.SerializerMethodField()
class Meta:
model = TransactionCategory
fields=['value','label','children']
def get_children(self, obj):
# 获取活跃的子分类
children = obj.children.filter(is_active=True)
if children.exists():
# 递归序列化子节点
serializer = self.__class__(
children,
many=True,
context=self.context
)
# 获取序列化后的数据
data = serializer.data
# 重要:只移除完全没有children字段的项
# 保留有children字段但值为None的项(表示该节点没有子节点)
data = [item for item in data if item]
# 如果子节点列表不为空,返回数据
if data:
return data
# 无子节点时返回None
return None
def to_representation(self, instance):
# 重写序列化方法,移除空的children字段
data = super().to_representation(instance)
# 如果children字段存在但值为None,则移除该字段
if 'children' in data and data['children'] is None:
data.pop('children')
return data
views
from rest_framework.response import Response
from .models import Category
from .serializers import CategoryCascaderSerializer
from rest_framework import views
class CategoryCascaderView(views.APIView):
def get(self,request,*args,**kwargs):
# 获取所有顶级分类并按order_id排序
top_level_category = Category.objects.filter(parent=None,is_active=True).order_by('-order_id')
# 序列化顶级分类及其子类 serializer=CategoryCascaderSerializer(top_level_category,many=True)
return Response(data=serializer.data)
urls
from django.urls import path
from .views import CategoryCascaderView
urlpatterns = [
path('api/categories/cascader/', CategoryCascaderView.as_view(), name='category-cascader'),
]
使用方法
前端只需发送 GET 请求到/api/categories/cascader/即可获取类似以下格式的数据:
[{
"value": "1",
"label": "一级分类001",
"children": [{
"value": "7",
"label": "二级分类01",
"children": [{
"value": "11",
"label": "三级分类01"
}, {
"value": "12",
"label": "三级分类02"
}, {
"value": "13",
"label": "三级分类05"
}
]
}, {
"value": "8",
"label": "二级分类02",
"children": [{
"value": "14",
"label": "三级分类01"
}, {
"value": "15",
"label": "三级分类02"
}
]
}, {
"value": "9",
"label": "二级分类03"
}, {
"value": "10",
"label": "二级分类04"
}
]
}, {
"value": "2",
"label": "一级分类002"
}, {
"value": "3",
"label": "一级分类003"
}
]
这个 API 可以直接集成到你的 Vue3 应用的 Cascader 组件中:
<el-cascader
:options="categoryOptions"
v-model="selectedCategory"
@change="handleChange"
/>
<script setup>
import { ref, onMounted } from 'vue'
const categoryOptions = ref([])
const selectedCategory = ref([])
const fetchCategories = async () => {
const res = await fetch('/api/categories/cascader/')
categoryOptions.value = await res.json()
}
onMounted(fetchCategories)
</script>
知识点补充
SerializerMethodField
SerializerMethodField
是 Django REST Framework (DRF) 提供的一种特殊字段类型,用于在序列化过程中添加非模型字段的数据。它允许你通过定义一个方法来计算并返回自定义值,这个方法的返回值会被包含在序列化结果中。
在上面的序列化器中,children
字段被定义为 SerializerMethodField
,用于递归获取并序列化子分类:
class CategorySerializer(serializers.ModelSerializer):
# ... 其他字段 ...
children = serializers.SerializerMethodField() # 声明一个自定义方法字段
def get_children(self, obj):
...
深度限制
如果层级可能非常深,可以添加深度参数控制:
def get_children(self, obj):
# 获取当前深度(从上下文中传递)
current_depth = self.context.get('depth', 0)
max_depth = self.context.get('max_depth', 3)
children = obj.children.filter(is_active=True)
if children and current_depth < max_depth:
# 传递递增的深度到子序列化器
self.context['depth'] = current_depth + 1
return CategorySerializer(children, many=True, context=self.context).data
return None
THE END
0
二维码
打赏
海报


通过Pythonのrest_framework&element-plusのCascader实现多级级联记录
需求层面:后端使用Django&rest_framework,前端使用vue3&element-plus,实现分类多级选择。有点儿类似省市区。
models
class Category(models.Model)……

文章目录
关闭
共有 0 条评论