当前位置: 首页 > news >正文

Django一分钟:在Django中怎么存储树形结构的数据,DRF校验递归嵌套模型的替代方案

引言

在开发过程中我们可能需要这样的树形结构:

[{"data": {"name": "牛奶"},"children": [{"data": {"name": "蒙牛"}, },{"data": {"name": "伊利"}, }]},{"data": {"name": "面包"},"children": [{"data": {"name": "黑面包"}, },{"data": {"name": "白面包"}, }]}
]

这种需求很常见,比如:分层的用户组、产品组件表等等,你都会用到树形结构,为这样的需求在django中自己从头创建模型并实现一系列的API是一个相当大的工作,好在我们有很多现成的库可以使用。

如果你在想:Django能不能做到某件事情;来这个网站看看是个好习惯。

一、创建和使用模型

本文中我们用到的库是django-treebeard

pip install django-treebeard

django-treebeard提供了三种实现方式:邻接表、嵌套集、路径树(MPTT)。下面我们使用路径树来演示,创建模型如下:

class Category(MP_Node):name = models.CharField(max_length=30)node_order_by = ['name']def __str__(self):return 'Category: {}'.format(self.name)

路径树会为你额外创建三个字段来辅助定义结构分别是pathdepthnumchild,它的插入和更新等维护比较繁重,但查询速度相当快。本文中我们不具体讨论实现原理,只是做简单使用演示,并为你可能遇到的问题提供思路,想详细了解其所有API阅读其文档。

定义好模型之后,模型中的一些方法可以辅助我们完成序列化和反序列化,快速的在数据库中保存和拉取数据:

>>> data =[{# tree1"data": {"name": "牛奶"},"children": [{"data": {"name": "蒙牛"}, },{"data": {"name": "伊利"}, }]},{# tree2"data": {"name": "面包"},"children": [{"data": {"name": "黑面包"}, },{"data": {"name": "白面包"}, }]}
]
>>> Category.load_bulk(data)
>>> Category.dump_bulk()
# [{'data': {'name': '牛奶'}, 'id': 1, 'children': [{'data': {'name': '伊利'}, 'id': 3}, {'data': {'name': '蒙牛'}, 'id': 2}]}, {'data': {'name': '面包'}, 'id': 4, 'children': [{'data': {'name': '白面包'}, 'id': 6}, {'data': {'name': '黑面包'}, 'id': 5}]}]

二、数据校验

假如你使用django rest framework为这样的递归嵌套的模型创建序列化器非常的困难,我推荐的解决方法是使用pydantic,如果你学习过FastAPI框架你应该对它不陌生。pydantic无法从django模型中自动生成模型,你需要自己定义:

from pydantic import BaseModelclass CategoryNodelModel(BaseModel):name: strclass CategoryTreeModel(BaseModel):data: CategoryNodelModelchildren: list['CategoryTreeModel'] | None = None

使用方法:

# 假设用户输入
>>> data = [{# tree1"data": {"name": "牛奶"},"children": [{"data": {"name": "蒙牛"}, },{"data": {"name": "伊利"}, }]},{# tree2"data": {"name": "面包"},"children": [{"data": {"name": "黑面包"}, },{"data": {"name": "白面包"}, }]}
]
# 测试用户输入的结构是否正确
>>> [CategoryTreeModel(**tree) for tree in data]
# 保存到数据库
>>> Category.load_bulk(data)

下面是测试代码,包含对一些api的简单演示:

from rest_framework.test import APITestCase
from api.models import Category
from api.views import CategoryS, CategoryNodelModel, CategoryTreeModelclass TestCategory(APITestCase):def setUp(self):# 用户输入data = [{# tree1"data": {"name": "牛奶"},"children": [{"data": {"name": "蒙牛"}, },{"data": {"name": "伊利"}, }]},{# tree2"data": {"name": "面包"},"children": [{"data": {"name": "黑面包"}, },{"data": {"name": "白面包"}, }]}]# 测试用户输入的结构是否正确[CategoryTreeModel(**tree) for tree in data]Category.load_bulk(data)def test_get_all(self):print(Category.dump_bulk())def test_get_tree(self):milk = Category.objects.filter(name='牛奶').first()if milk:print(Category.dump_bulk(milk))def test_delete(self):milk = Category.objects.filter(name='面包').first()milk.delete()print(Category.dump_bulk())

总结

对于在django中使用树、图等复杂结构之类需求,可以去尝试寻找现成的库;对于复杂数据的校验可以尝试使用pydantic。选择合适的工具会为我们节省很多时间。

http://www.lryc.cn/news/453886.html

相关文章:

  • 【Docker从入门到进阶】06.常见问题与解决方案 07.总结与资源
  • 快速排序的非递归实现:借助栈实现、借助队列实现
  • Finops成本优化企业实践-可视化篇
  • Spring Boot中线程池使用
  • Python机器学习:自然语言处理、计算机视觉与强化学习
  • Vue2 + ElementUI + axios + VueRouter入门
  • GO网络编程(四):海量用户通信系统2:登录功能核心【重难点】
  • 某项目实战分析代码二
  • 全面指南:探索并实施解决Windows系统中“mfc140u.dll丢失”的解决方法
  • QT学习笔记1(QT和QT creator介绍)
  • 存储电话号码的数据类型,用 int 还是用 string?
  • 【目标检测】工程机械车辆数据集2690张4类VOC+YOLO格式
  • target_link_libraries()
  • Javascript数组研究09_Array.prototype[Symbol.unscopables]
  • SkyWalking 自定义链路追踪
  • Linux驱动开发(速记版)--设备模型
  • 动手学深度学习(李沐)PyTorch 第 6 章 卷积神经网络
  • 新编英语语法教程
  • Golang 服务器虚拟化应用案例
  • Elasticsearch基础_4.ES搜索功能
  • Elasticsearch要点简记
  • 【通信协议】IIC通信协议详解
  • 2024年中国科技核心期刊目录(社会科学卷)
  • 用Python集成免费IP归属地查询API
  • C 数组
  • 【Unity】unity安卓打包参数(个人复习向/有不足之处欢迎指出/侵删)
  • C0016.Clion中qDebug()打印输出中文时,都是问号??????的解决办法
  • C++ priority_queue 优先队列构造大根堆和小根堆
  • 音视频入门基础:FLV专题(9)——Script Tag简介
  • Vue页面,基础配置