Django 框架 深度学习 第二课程
2026/6/11 18:28:52 网站建设 项目流程

Django 框架高级

路由中尽可能以名词为主,不要出现太多动词

useradd 模块 + 功能

adduser 功能 + 模块

get/post/put/delete

user get 用户列表

user post---- 请求体参数 用户添加

user post 用户列表

user/1 delete 用户删除

user put--- 请求体 用户修改

上述请求方式,是网站访问过程中的一种设计风格,RESTFULL 风格

Django + RESTFULL ======== Django Rest Framework (DRF)

Django Rest Framework(简称DRF

它就是 Django 的「接口专用增强包」

专门用来在 Django 里快速、规范、轻松地写出 RESTful API 接口,给前端、小程序、APP 提供数据。


1. 它到底是干嘛的?

你用原生 Django 只能做:

  • 网页渲染(返回 HTML)

  • 表单提交

但你要做前后端分离项目(前端 Vue/React/ 小程序,后端只返回 JSON),原生 Django 很麻烦。

DRF 就是解决这个问题的:

让你用极少代码,直接生成一套标准 RESTful 增删改查接口


2. 用 DRF 能得到什么?(最实用的 4 个功能)

① 自动生成 RESTful 接口(你最关心的)

写几行代码,直接拥有:

  • GET 获取列表

  • POST 新增

  • PUT 修改

  • DELETE 删除

URL 全是名词,不带动词,完全符合 RESTful


② 自动序列化(模型 ↔ JSON 自动转换)

不用你手动拼 JSON,DRF 帮你:

  • 把数据库数据 → JSON 给前端

  • 把前端传的 JSON → 存进数据库


③ 自带接口调试页面(超级好用)

运行项目后,直接在浏览器访问接口,就能:

  • 看数据

  • 发 POST/PUT/DELETE 请求测试

    不用装 Postman!


④ 内置权限、分页、过滤、认证

开箱即用:

  • 登录才能访问

  • 接口分页

  • 搜索过滤

  • 接口限流


3. 用一句话总结它的地位

Django = 做网站 Django Rest Framework = 用 Django 做 API 接口

接口 = 后端给前端提供的数据通道
API(应用程序编程接口) 长什么样?

为一个地址

  • 不是页面

  • 不是图片

  • 就是一串数据(JSON)

示例:

{ "code": 200, "msg": "成功", "data": { "username": "小明", "email": "test@qq.com" } }

4. 最简单对比(一看就懂)

不用 DRF(原生 Django 写接口)

  • 代码多

  • 要自己处理 JSON

  • 要自己处理增删改查

  • 不规范

用 DRF

  • 代码极少

  • 自动 JSON

  • 自动增删改查

  • 标准 RESTful

  • 自带调试页面


5. 你之前学的 RESTful,DRF 就是落地工具

你前面记的规则:

  • URL 用名词

  • 动作由 GET/POST/PUT/DELETE 决定

  • user/ 获取列表

  • user/1 删除用户

这些规则,DRF 全部自动帮你实现!


总结

Django Rest Framework = Django 的 API 开发神器

专门帮你快速写出规范、好用、符合 RESTful 风格的后端接口

数据序列化

第一步 手动拼接
from django.views import View from django.http import JsonResponse from .models import Book, Press # 确保你的模型在 models.py 里定义好了 ​ class BookView(View): def get(self, request): # 1. 从 Book 表查询数据,指定字段 bookresult = list( Book.objects.values( "id", "bookname", "bookprice", "bookauthor", "pressdate", "category", "press" ) ) result = [] # 2. 遍历每一本书,把外键 press 对应的出版社信息替换成字典 for book in bookresult: book["press"] = { "id": book["press"], "pressname": Press.objects.get(id=book["press"]).pressname, "pressaddress": Press.objects.get(id=book["press"]).pressaddress } result.append(book) ​ # 3. 返回 JSON 数据给前端 return JsonResponse(result, safe=False)
第二步 DRF自动拼接 serializers
from django.core import serializers def get(self,request): book == .... data = serializers.serializer('json',book) return ... #这种方法还实现单一对象 如果有一对多和多对多 无法进行序列化
方式三:DRF序列化
第一步:安装DRF

pip install djangorestframework

第二步:创建DRF的序列化器
class PressSerializer(serializers.Serializer): id = serializers.IntegerField() pressname = serializers.CharField(max_length=30) pressaddress = serializers.CharField(max_length=50) class BookSerializer(serializers.Serializer): id = serializers.IntegerField() bookname = serializers.CharField(max_length=50) bookprice = serializers.FloatField bookauthor = serializers.CharField(max_length=50) pressdate = serializers.DateField() # 语言 category = serializers.IntegerField(choices=((1, "汉语"), (2, "英语"), (3, "法语"), (4, "日语"))) # 多对一 press = PressSerializer()
第三步:创建DRF的视图层View
from rest_framework.response import Response from rest_framework.views import APIView ​ from app01.models import * from app01.MySerializers import * ​ class BookView(APIView): def get(self,reqeust): bookresult = Book.objects.all() # 将查询到的结果进行DRF序列化 bookSerializer = BookSerializer(bookresult,many=True) # 返回 return Response(bookSerializer.data)
第四步:在settings.py文件中,对drf做配置
INSTALLED_APPS = [ "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", 'app01.apps.App01Config', "rest_framework" ]
第五步:启动服务,访问路由

数据反序列化

实现数据添加操作

第一步:考虑添加过程中的数据提交格式

{ "bookname": "鹿鼎记", "bookauthor": "金庸", "bookprice":50, "pressdate": "2026-04-28", "category2": 2, "press2": 1, "booktype2": [1,3] }

第二步:在序列化器中追加特殊的反序列化字段(标记好只读和只写)和ID字段

class BookSerializer(serializers.Serializer): id = serializers.IntegerField(required=False, read_only=True) bookname = serializers.CharField(max_length=50) bookprice = serializers.DecimalField(max_digits=5, decimal_places=2) bookauthor = serializers.CharField(max_length=50) pressdate = serializers.DateField() ​ # 只读:返回给前端看的 category = serializers.CharField(source='get_category_display', read_only=True) press = PressSerializers(read_only=True) booktype = BookTypeSerializers(many=True, read_only=True) ​ # 只写:前端传参用 category2 = serializers.CharField(write_only=True) press2 = serializers.IntegerField(write_only=True) booktype2 = serializers.ListField( write_only=True )

第三步:在视图逻辑层,接收提交数据与封装序列化器对象并进行验证

def post(self,request): # 接收参数 book_obj = request.data # 调用orm 封装序列化器 book_ser = BookSerializer(data=book_obj) # 需要对封装完成以后得序列化对象做验证 # 如果得到Ture,则验证通过;如果是False,则需要检查序列化字段与反序列化字段 print(book_ser.is_valid())

第四步:调用save方法执行添加操作

def post(self,request): # 接收参数 book_obj = request.data # 调用orm 序列化器 book_ser = BookSerializer(data=book_obj) # 需要对封装完成以后得序列化对象做验证 if book_ser.is_valid(): book_ser.save() return Response("添加成功") # 返回序列化数据 return Response("添加失败")

第五步:在序列化器中重写create方法,调用底层ORM,如果没有重写create,则运行报错

def create(self, validated_data): # 2. 创建书籍对象 book = Book.objects.create( bookname=validated_data.get('bookname'), bookprice=validated_data.get('bookprice'), bookauthor=validated_data.get('bookauthor'), pressdate=validated_data.get('pressdate'), category=validated_data["category2"], press_id=validated_data['press2'], ) # 3. 处理多对多:书籍类型 book.booktype.add(*validated_data['booktype2']) ​ return book

第六步:测试添加效果

实现数据修改操作

第一步 :配置URL

urlpatterns = [ path('book/',app.views.BookView.as_view()), path('book/<int:id>/', app.views.BookView2.as_view()), ​ ]

第二部 在视图逻辑层,接收提交数据与封装序列化器对象并进行验证

class BookView2(APIView): def get(self,request,id): book = Book.objects.get(id=id) book = BookSerializer(book) return Response(book.data) ​ def delete(self,request,id): Book.objects.get(id=id).delete() return Response({"msg":"删除成功"}) ​ def put(self,request,id): book = Book.objects.get(id=id) ser = BookSerializer(book,data=request.data,partial=True) # 局部修改 if ser.is_valid(): ser.save() return Response({"msg":"修改成功"}) return Response(ser.errors)

第三部 增加的代码实现修改操作 重写 updata

def update(self, instance, validated_data): instance.bookname = validated_data.get('bookname', instance.bookname) instance.bookprice = validated_data.get('bookprice', instance.bookprice) instance.bookauthor = validated_data.get('bookauthor', instance.bookauthor) instance.pressdate = validated_data.get('pressdate', instance.pressdate) instance.category = validated_data.get('category2', instance.get_category_display()) instance.press_id = validated_data.get('press2', instance.press_id) ​ if validated_data.get('booktype2'): instance.booktype.set(validated_data['booktype2']) instance.save() ​ return instance

第 四步 测试效果

补充

DRF 核心参数 / 方法通俗讲解

结合 ** 序列化器 (Serializer)** 日常使用场景,逐个讲清,附用法示例。

一、instance

作用

接收数据库已有模型对象,用于更新 / 修改数据

  • 不传 / 传None:代表新增数据

  • 传模型实例对象:代表更新已有数据


二、data&validated_data

1.data

原始传入的前端请求数据(字典),未校验,可能含非法字段、格式错误。

  • 来源:request.data、手动传入的字典

2.validated_data

校验通过后的干净数据(字典)。

  • 必须先执行is_valid()校验成功后才能使用

  • 自动过滤非法字段、完成类型转换、执行字段校验规则

  • create()/update()方法时*必用

ser = UserSerializer(data={"age": "20"}) ser.is_valid() # 先校验 print(ser.data) # 原始数据 print(ser.validated_data) # 校验后合规数据,age 已转为数字

三、is_valid()方法

作用

执行全字段校验(类型、长度、正则、自定义校验器等)。

  • 返回值:True= 校验通过;False= 校验失败

  • 校验失败可通过ser.errors查看错误信息

必写规则

使用validated_data前,必须先调用is_valid()

ser = UserSerializer(data={"age": 200}) if ser.is_valid(): ser.save() else: print(ser.errors) # 输出错误提示
补充参数:raise_exception=True

校验失败直接抛出异常,DRF 会自动返回 400 响应,接口常用:

ser.is_valid(raise_exception=True)

四、partial参数

作用

局部更新开关,布尔值,搭配instance更新使用。

  • partial=False(默认):全量更新,所有必填字段必须传,缺字段直接校验失败

  • partial=True局部更新,只传需要修改的字段,其他字段可不传

场景

只改用户名,不改其他必填字段:

user = User.objects.get(id=1) # partial=True 局部更新 ser = UserSerializer(instance=user, data={"name":"新名字"}, partial=True) ser.is_valid() ser.save()

总结:新增不用partial部分字段更新必须加 partial=True


五、add()set()(针对多对多字段

DRF/ORM 中专门操作ManyToManyField多对多关系的方法,作用是关联 / 解绑关联数据

前置说明

假设有模型:文章 (Article) 和 标签 (Tag),多对多关联。

1.set(可迭代对象)

覆盖式设置:清空原有所有关联,再重新绑定新数据。

  • []:直接清空所有关联

article = Article.objects.get(id=1) # 把文章标签 全部替换 为 tag1、tag2 article.tags.set([tag1, tag2])
2.add(对象/主键)

追加式添加不删除原有关联,只新增关联关系。

  • 可传模型对象、主键 id、多个参

# 在原有标签基础上,额外再加一个 tag3 article.tags.add(tag3) # 也可以传id article.tags.add(3)
补充对应解绑:remove()
# 移除指定关联,保留其他 article.tags.remove(tag3)

快速汇总(记忆版)
  1. instance:传模型对象 = 更新;不传 = 新增

  2. is_valid():执行数据校验,失败看errors

  3. validated_data:校验通过的安全数据,校验后才能用

  4. partialTrue= 局部更新(只改部分字段)

  5. set():多对多 → 覆盖原有关系

  6. add():多对多 → 追加新关系,保留原有

DRF 2.0 版本

class BookSerializer(serializers.ModelSerializer): class Meta: model = Book fields = '__all__' extra_kwargs = { "press": {"write_only": True}, "booktype": {"write_only": True}, "category": {"write_only": True}, } # depth = 1 # 深度查询. category_display = serializers.SerializerMethodField(read_only=True) press_display = serializers.SerializerMethodField(read_only=True) booktype_display = serializers.SerializerMethodField(read_only=True) ​ def get_category_display(self, obj): return obj.get_category_display() def get_press_display(self, obj): return {"id":obj.press.id,"pressname":obj.press.pressname} def get_booktype_display(self, obj): return [{"id":i.id,"typename":i.typename} for i in obj.booktype.all()]

什么是序列化什么是反序列化?

一、核心定义

1. 序列化(Serialize)

把内存中运行的对象 / 数据结构,转换成可存储、可网络传输的字节串 / 字符串格式。 内存中的对象不能直接存硬盘、发网络,序列化就是做「打包」。

2. 反序列化(Deserialize)

把字节串 / 字符串还原回程序内存里原生对象 / 数据结构。相当于「解包」,恢复成程序能直接调用的变量、对象。

DRF中的1.0 以及 2.0 的区别

特性APIViewModelViewSet
定位DRF 视图体系的基础父类,所有视图的 “根”基于ViewSet+ModelMixin封装类,专门给模型 CRUD 用
CRUD 方法完全手动写:get()/post()/put()/delete()自动提供:list()/retrieve()/create()/update()/destroy()
路由配置需手动为每个方法绑定 URL配合DefaultRouter自动生成 5 个标准接口路由
自定义程度极高,所有逻辑自己写中等,可通过重写方法、权限类、过滤类扩展
适用场景非模型接口、复杂业务逻辑、非标准请求模型的标准 CRUD 接口、快速开发后台接口

DRF2.0 高级

在DRF的基础上 进行封装

# # 封装通用模块 # #通用模块 # class GenericAPIView(APIView): 公共父类 # queryset = None # serializer_class = None # # def get_queryset(self): # return self.queryset.all() # # def get_serializer_class(self,*args,**kwargs): # return self.serializer_class(*args,**kwargs) # # #通用查询所有模块 # class ListModelMixin(object): # def list(self,request): # queryset = self.get_queryset() # ser_obj = self.get_serializer_class(queryset,many=True) # return Response(ser_obj.data) # # #通用添加模块 # class CreateModelMixin(object): # def create(self,request): # ser_obj = self.get_serializer_class(data=request.data) # if ser_obj.is_valid(): # ser_obj.save() # return Response(ser_obj.validated_data) # return Response(ser_obj.errors) # # #通用查询某一个模块 # class RetrieveModelMixin(object): # def retrieve(self,request,id): # queryset = self.get_queryset().get(id=id) # # 序列化 # ser_obj = self.get_serializer_class(queryset) # # return Response(ser_obj.data) # # #通用删除某一个模块 # class DestroyModelMixin(object): # def destroy(self,request,id): # queryset = self.get_queryset().get(id=id) # if not queryset: # return Response("对象不存在") # # 序列化 # queryset.delete() # return Response("删除成功") # # #通用修改某一个模块 # class UpdateModelMixin(object): # def update(self,request,id): # queryset = self.get_queryset().get(id=id) # ser_obj = self.get_serializer_class(instance=queryset, data=request.data, partial=True) # if ser_obj.is_valid(): # # 调用orm执行修改 # ser_obj.save() # return Response(ser_obj.validated_data) # return Response(ser_obj.errors) # # class ListCreateApiView(GenericAPIView,ListModelMixin,CreateModelMixin): # pass # # class RetrieveDestroyUpdateApiView(GenericAPIView,RetrieveModelMixin,DestroyModelMixin,UpdateModelMixin): # pass # # class BookView(ListCreateApiView): # #提前准备查询数据与序列化器 # queryset = Book.objects.all() # serializer_class = BookSerializer # # def get(self, request): # return self.list(request) # # def post(self, request): # return self.create(request) # # class BookEditView(RetrieveDestroyUpdateApiView): # queryset = Book.objects.all() # serializer_class = BookSerializer # def get(self, request, id): # return self.retrieve(request,id) # # def put(self, request, id): # return self.update(request,id) # # def delete(self, request, id): # return self.destory(request,id) # # class PressView(ListCreateApiView): # queryset = Press.objects.all() # serializer_class = PressSerializer # # def get(self,request): # return self.list(request) # # def post(self,request): # return self.create(request) # # class BookTypeView(ListCreateApiView): # queryset = BookType.objects.all() # serializer_class = BookTypeSerializer # def get(self,request): # return self.list(request) # # class PressView(APIView): # def get(self,request): # presslist = Press.objects.all() # serializer = PressSerializer(presslist, many=True) # return Response(serializer.data) # class BookView(APIView): # # def get(self,reqeust): # bookresult = Book.objects.all() # # 将查询到的结果进行DRF序列化 # bookSerializer = BookSerializer(bookresult,many=True) # # 返回 # return Response(bookSerializer.data) # # """ # { # "bookname": "鹿鼎记", # "bookauthor": "金庸", # "bookprice":50, # "pressdate": "2026-04-28", # "category2": 2, # "press2": 1, # "booktype2": [1,3] # } # """ # def post(self,request): # # 接收参数 # book_obj = request.data # # 调用orm 序列化器 # book_ser = BookSerializer(data=book_obj) # # 需要对封装完成以后得序列化对象做验证 # if book_ser.is_valid(): # book_ser.save() # return Response("添加成功") # # 返回序列化数据 # return Response("添加失败") # class BookEditView(APIView): # def get(self,request,id): # book = Book.objects.get(id=id) # # 将查询到的结果进行DRF序列化 # bookSerializer = BookSerializer(book) # # 返回 # return Response(bookSerializer.data) # # def delete(self,request,id): # book = Book.objects.get(id=id) # book.delete() # # 返回 # return Response("删除成功") # # def put(self, request, id): # # 得到要修改的数据库中的详细对象信息 # book = Book.objects.get(id=id) # # # instance:原始数据 data:要修改的数据 partial:局部修改 # book_ser = BookSerializer(instance=book,data=request.data,partial=True) # print(book_ser.is_valid()) # if book_ser.is_valid(): # book_ser.save() # return Response("修改成功")

用这种方式可以减少好多类的创建以及 减少代码冗余

一、整体思路总结

你手写的这套代码,完全复刻了 DRF 底层设计思想

  1. GenericAPIView= DRF 原生GenericAPIView(通用基础视图)

  2. ListModelMixin/CreateModelMixin...= DRF 五大混入类 (Mixin)

  3. 最后组合 = 拼装出具备增删改查能力的视图

本质:抽公共代码 → 封装基类 + 功能混入 → 子类直接复用,避免重复写get/post/序列化/查询逻辑。

DRF 3.0 版本

重写 URL 方法可以直接填充为 五中url 不用自己定义

from rest_framework.routers import DefaultRouter from app.views import BookView ​ router = DefaultRouter() # 格式:register(路由前缀, 视图类, basename=唯一标识) router.register('book', BookView, basename='book1') # router.register('book2', BookView2, basename='book2') ​ urlpatterns = [] urlpatterns += router.urls ​ from rest_framework.viewsets import ModelViewSet class BookView(ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializer

在 views 里面的实现 为直接继承类 不用自己在定义方法

DRF3.0版本-View

view层是继承APIView还是继承ModelViewSet?

结论先行: ​ 简单单接口、定制化逻辑、非标准 CRUD → 用 APIView(基础视图类,自由度最高) 标准列表 / 详情 / 增删改查全套 CRUD、配合路由自动生成 → 用 ModelViewSet(视图集,开发效率最高) ​ 一、两者核心区别 1. APIView(基础视图) DRF 最底层视图,基于 Django 原生 View 扩展,一个类对应一个接口地址。 ​ 按请求方法拆分:get() / post() / put() / delete() 等 手动写路由、手动控制逻辑,灵活度拉满 适合:单一场景接口、复杂业务逻辑、非标准 CRUD、自定义权限 / 限流 / 响应 2. ModelViewSet(视图集) 封装好的全套 CRUD 视图集,继承自 ViewSet,一个类对应一组接口。 默认自动实现 5 个标准动作: list(查列表)、retrieve(查单个)、create(新增)、update(全改)、destroy(删除) ​ 配合 DRF 路由器(DefaultRouter/SimpleRouter)自动生成路由,不用手写多条路由 代码极简,只需要配置 queryset 和 serializer_class 适合:标准数据表 CRUD 接口、后台管理、常规业务接口 二、如何选型(场景对照) 优先用 APIView ​ 接口不是标准 CRUD(如登录、验证码、文件上传、统计接口、复杂查询) 每个接口逻辑差异大,需要深度定制请求 / 响应 / 流程 只需要单个接口,不需要整套增删改查 习惯 Django 原生视图写法,追求细粒度控制 ​ 优先用 ModelViewSet ​ 对一张数据表做完整 增删改查 后台管理、常规业务接口,逻辑通用无特殊定制 想少写路由、少写重复代码,快速开发 ​ 三、补充衍生类(常用过渡选择) 如果介于两者之间,还有折中方案: ​ GenericAPIView + 混入类 比 APIView 少写重复代码,比 ViewSet 更灵活,适合部分 CRUD。 ReadOnlyModelViewSet 只提供查询(列表 + 详情),禁用增删改,纯只读接口首选。 ​ 四、一句话总结 ​ 追求灵活、定制、单接口 → APIView 追求高效、标准 CRUD、自动路由 → ModelViewSet ​ 日常开发中:后台管理 / 标准数据表优先 ModelViewSet;前端业务接口、特殊功能接口优先 APIView。
分页类型核心配置请求示例核心特点适用场景
页码分页PageNumberPaginationpage_size:默认每页条数page_query_param:页码参数名page_size_query_param:动态调整每页条数/list?page=2&size=3支持直接跳页,使用直观;数据量大时性能下降传统分页列表、需要跳页的场景
偏移分页LimitOffsetPaginationdefault_limit:默认每页条数limit_query_param:条数参数名offset_query_param:偏移量参数名/list?offset=3&limit=3无页码概念,实现简单;offset 越大查询越慢数据量较小的简单列表
游标分页CursorPaginationpage_size:每页条数cursor_query_param:游标参数名ordering:必填排序字段/list?cursor=xxx依靠游标定位,只能上下翻页、不能跳页;大数据量性能最优,不易漏 / 重数据海量数据、无限滚动、信息流、聊天记录

认证机制

前后端分离开发中,如何判断访问的路由是否合理?(是否正常登录)Token认证

from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed ​ from app01.models import User ​ ​ class MyAuth(BaseAuthentication): def authenticate(self, request): ​ print(request.headers) ​ # 判断是否有token url ?thoken=706c5ed1124b4191b6f8ec786b3499ae token = request.query_params.get('token') if not token: # 没有登录 raise AuthenticationFailed('您还没有登录!') # 用户携带token,但是真假不确定 userobj = User.objects.filter(token=token).first() if not userobj: raise AuthenticationFailed('登录信息有误,请重新登录!') return userobj,token

授权机制

授权机制

$.ajax({ url:"http://localhost:8000/app01/user/", type:"delete", data:{"id":1}, headers:{"Authorization":"Bearer "+loginuser.token}, success:funxxxxx }) class MyPer(): message = "您没有管理员权限!" def has_permission(self,request,view): print(request.user) userobj = request.user print(userobj) if userobj.type == 1: return True return False

限流机制

应用场景!!

from rest_framework.throttling import SimpleRateThrottle class MyThrottle(SimpleRateThrottle): ​ scope = "mytime" ​ def get_cache_key(self, request, view): return self.get_ident(request) # setting.py文件中配置 REST_FRAMEWORK = { "DEFAULT_THROTTLE_RATES":{"mytime":"3/m"} } class UserView(APIView): ​ # 认证组件 authentication_classes = [MyAuth] # 授权组件 permission_classes = [MyPer] # 限流组件 throttle_classes = [MyThrottle] ​ def get(self,request): print("用户删除") return Response("用户删除成功")

分页机制

from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination,CursorPagination class MyPage(PageNumberPagination): page_size = 3 #每页显示3条 page_query_param = "page" #下一页 user?page=2 page_size_query_param = "size" #下一页显示多少条 user?page=2&size=3 ​ class MyLimit(LimitOffsetPagination): default_limit = 3 ​ class MyCursor(CursorPagination): cursor_query_param = "cursor" page_size = 3 ordering = "-id" ​ class BookView(ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializer

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询