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

引言

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

python 复制代码
[
    {
        "data": {"name": "牛奶"},
        "children": [
            {"data": {"name": "蒙牛"}, },
            {"data": {"name": "伊利"}, }
        ]
    },
    {
        "data": {"name": "面包"},
        "children": [
            {"data": {"name": "黑面包"}, },
            {"data": {"name": "白面包"}, }
        ]
    }
]

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

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

一、创建和使用模型

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

shell 复制代码
pip install django-treebeard

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

python 复制代码
class Category(MP_Node):
    name = models.CharField(max_length=30)
    node_order_by = ['name']

    def __str__(self):
        return 'Category: {}'.format(self.name)

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

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

python 复制代码
>>> 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模型中自动生成模型,你需要自己定义:

python 复制代码
from pydantic import BaseModel

class CategoryNodelModel(BaseModel):
    name: str

class CategoryTreeModel(BaseModel):
    data: CategoryNodelModel
    children: list['CategoryTreeModel'] | None = None

使用方法:

python 复制代码
# 假设用户输入
>>> 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的简单演示:

python 复制代码
from rest_framework.test import APITestCase
from api.models import Category
from api.views import CategoryS, CategoryNodelModel, CategoryTreeModel


class 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。选择合适的工具会为我们节省很多时间。

相关推荐
多想和从前一样4 小时前
Django 创建表时 “__str__ ”方法的使用
后端·python·django
IT古董6 小时前
【开源向量数据库】Milvus简介
数据库·开源·milvus
web150850966416 小时前
SQL 建表语句详解
java·数据库·sql
宇智波云7 小时前
mysql增加字段操作以及关键字报错
java·数据库·mysql
怠惰_u7 小时前
使用Redis实现分布式锁,基于原本单体系统进行业务改造
数据库·redis·分布式
lozhyf7 小时前
后端开发:高效数据库查询优化实战指南
数据库·oracle
云泽野7 小时前
50道题快速复习MySQL之准备篇
数据库·mysql·oracle
林林总肿7 小时前
Mybatis后端数据库查询多对多查询解决方案
数据库·spring boot·mybatis
jay丿7 小时前
Redis简介
数据库·redis·缓存
格雷亚赛克斯8 小时前
Qt笔记31-69
数据库·笔记·qt