DRF初识与序列化
一、Django的序列化方法
1、为什么要用序列化组件
做前后端分离的项目,我们前后端数据交互一般都选择JSON,JSON是一个轻量级的数据交互格式。
那么我们给前端数据的时候都要转成json格式,那就需要对我们从数据库拿到的数据进行序列化。
2、表的构建

CHOICES = ((1, "python"), (2, "linux"), (3, "go")) # 书籍表 class Book(models.Model): title = models.CharField(max_length=64) category = models.IntegerField(choices=CHOICES) # 书籍分类 pub_time = models.DateField() publisher = models.ForeignKey(to="Publisher") authors = models.ManyToManyField(to="Author") class Meta: verbose_name = '书籍' verbose_name_plural = verbose_name def __str__(self): return self.title # 出版社表 class Publisher(models.Model): title = models.CharField(max_length=64) class Meta: verbose_name = '出版社' verbose_name_plural = verbose_name def __str__(self): return self.title # 作者表 class Author(models.Model): name = models.CharField(max_length=32) class Meta: verbose_name = '作者' verbose_name_plural = verbose_name def __str__(self): return self.name
3、希望构建的字典格式
book_list = [ { "id": 1, "title": "", "publisher": { # 外键 "id": 1 "title": "" }, "authors": [{}, {}] # 多对多 }, { "id": 2, "title": "", "publisher": { # 外键 "id": 1 "title": "" }, "authors": [{}, {}] # 多对多 }, ]
4、方法一

在Django中使用Json模块序列化 class BooksView(views.View): def get(self, request): book_queryset = Book.objects.values("id", "title", "pub_time", "publisher") book_list = list(book_queryset) # 把queryset类型转换成列表 # 如果我们需要取外键关联的字段信息 需要循环获取外键 再去数据库查然后拼接成我们想要的 ret = [] for book in book_list: publisher_obj = Publisher.objects.filter(id=book["publisher"]).first() # 修改原字典中的publisher对应的值 book["publisher"] = { "id": publisher_obj.id, "title": publisher_obj.title } # 把新的字典追加到一个空列表中 ret.append(book) ret = json.dumps(ret, ensure_ascii=False, cls=MyJson) return HttpResponse(ret) # json不能序列化时间字段,重写JSONEncoder里面的default方法解决 class MyJson(json.JSONEncoder): def default(self, field): if isinstance(field, datetime.datetime): return field.strftime('%Y-%m-%d %H:%M:%S') elif isinstance(field, datetime.date): return field.strftime('%Y-%m-%d') else: return json.JSONEncoder.default(self, field)
5、方法二

使用JsonResponse,自动帮我们重写了JSONEncoder里面的default方法,解决时间字段的问题 class BooksView(views.View): def get(self, request): book_queryset = Book.objects.values("id", "title", "pub_time", "publisher") book_list = list(book_queryset) # 把queryset类型转换成列表 # 如果我们需要取外键关联的字段信息 需要循环获取外键 再去数据库查然后拼接成我们想要的 ret = [] for book in book_list: publisher_obj = Publisher.objects.filter(id=book["publisher"]).first() # 修改原字典中的publisher对应的值 book["publisher"] = { "id": publisher_obj.id, "title": publisher_obj.title } # 把新的字典追加到一个空列表中 ret.append(book) return JsonResponse(ret, safe=False, json_dumps_params={'ensure_ascii': False})
6、方法三

使用Django自带的序列化模块 from django.core import serializers # 能够得到我们要的效果 结构有点复杂 class BooksView(views.View): def get(self, request): book_queryset = Book.objects.all() ret = serializers.serialize("json", book_queryset, ensure_ascii=False) return HttpResponse(ret)
二、DRF序列化的介绍
1、介绍
下载DRF模块:pip install djangorestframework 导入: from rest_framework.views import APIView from rest_framework.response import Response
使用DRF默认的页面,需要在settings的APP注册rest_framework

INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', ]
首先,我们要用DRF的序列化,就要遵循DRF框架的一些标准, -- Django我们CBV继承类是View,现在DRF我们要用APIView -- Django中返回的时候我们用HTTPResponse,JsonResponse,render ,DRF我们用Response
2、APIView跟View区别
-- APIView继承了View -- APIView的as_view方法实现了csrf中间件的豁免(用DRF不需要再把settings的csrf中间件注释掉了) -- 重新封装了request request._request可以拿到旧的request request.query_params 旧的request.GET即_request.GET request.data 除了GET请求外的所有的数据,_request.POST、_request.FILES等
-- 序列化器对象.data
存放序列化好的数据
校验通过的数据存放到validated_data里,最后也会序列化封装到序列化对象.data里面返回给前端
校验不通过,错误信息存到序列化对象.errors里面
-- DRF有自己的序列化模块
from rest_framework import serializers -- Response 继承了HttpResponse 携带HTTP标准状态码 做模板的渲染
3、使用方法
-- 序列化(传数据到前端) -- 声明一个序列化器 class BookSerializer(serializers.Serializer): id = serializers.IntegerField(required=False) title = serializers.CharField(max_length=32) pub_time = serializers.DateField() -- 视图里序列化我们的queryset或者某个对象 # queryset需要声明many=True ser_obj = BookSerializer(queryset, many=True) # 具体的某个的对象则不需要声明 ser_obj = BookSerializer(book_obj) return Response(ser_obj.data) -- 实现流程 -- 如果指定了many=True -- 把queryset当成可迭代对象去循环 得到每个模型对象 -- 把每个模型对象放入序列号器进行序列化 -- 进行字段匹配 匹配上的字段进行序列化 匹配不上丢弃 -- 必须满足序列化的所有字段要求 -- 反序列化(从前端获取数据) -- 获取前端传过来的数据 -- 用序列化器进行校验 # 新增一条数据 ser_obj = BookSerializer(data=request.data) # 编辑某条数据 # instance编辑哪个对象,data前端传过来要跟新的某些字段数据,partial表示允许部分跟新 ser_obj = BookSerializer(instance=book_obj, data=request.data, partial=True) if ser_obj.is_valid(): ser_obj.save()# 调用create/update方法 return Response(ser_obj.data) else: return Response(ser_obj.errors) -- 写create方法 在create方法里用ORM操作创建新对象 -- 写update方法 在update方法里用ORM操作创编辑对象
4、注意事项
1. 外键和多对多字段的序列化需要额外再设置序列化器 2. 序列化 序列化器的参数是queryset和many 3. 反序列化 序列化器的参数是data=提交上来的数据 4. 序列化器字段类型不统一的情况 反序列化要用的一些字段通过一些参数跟序列化区分开 -- required=False # 可以不传的字段 -- read_only=True # 只用于序列化的字段 -- write_only=True # 只用于反序列化的字段 5. 反序列化的验证 is_valid() --> 校验数据 post请求中的save() --> 调用序列化器的create方法
put请求中的save() --> 调用序列化器的update方法
三、DRF序列化示例
1、声明序列化器
# serializers.py文件 from rest_framework import serializers from libsys.models import Book CHOICES = ((1, "python"), (2, "linux"), (3, "go")) # 继承serializers.Serializer class PublisherSerializer(serializers.Serializer): id = serializers.IntegerField() title = serializers.CharField(max_length=64) class AuthorSerializer(serializers.Serializer): id = serializers.IntegerField() name = serializers.CharField(max_length=32) # 字段的声明和models相类似 class BookSerializer(serializers.Serializer): # POST校验的时候required=False声明不需要校验这个字段 id = serializers.IntegerField(required=False) title = serializers.CharField(max_length=64) pub_time = serializers.DateField() # 选择字段,显示的是数字 # category = serializers.ChoiceField(choices=CHOICES) # 把选择字段显示成字符类型,source参数后面跟的是ORM操作 # read_only=True表示这个字段只在渲染前端的时候使用 category = serializers.CharField(source='get_category_display', read_only=True) # write_only=True表示这个字段只在POST提交数据,做校验的时候使用 post_category = serializers.IntegerField(write_only=True) # 外键需要设置额外的序列化器对它进行序列化 publisher = PublisherSerializer(read_only=True) publisher_id = serializers.IntegerField(write_only=True) # 多对多字段需要设置额外的序列化器对它进行序列化,且声明many=True authors = AuthorSerializer(many=True, read_only=True) author_list = serializers.ListField(write_only=True) def create(self, validated_data): # validated_data是通过校验的数据,最后也会封装到data里面 #通过ORM操作给Book表增加数据 book_obj = Book.objects.create(title=validated_data["title"], pub_time=validated_data["pub_time"], category=validated_data["post_category"], publisher_id=validated_data["publisher_id"]) book_obj.authors.add(*validated_data["author_list"]) return book_obj def update(self, instance, validated_data): # 通过ORM操作给Book表编辑数据 # instance就是book_obj instance.title = validated_data.get("title", instance.title) instance.pub_time = validated_data.get("pub_time", instance.pub_time) instance.category = validated_data.get("post_category", instance.category) instance.publisher_id = validated_data.get("publisher_id", instance.publisher_id) if validated_data.get("author_list", False): instance.authors.set(validated_data["author_list"]) instance.save() return instance
2、在视图函数中调用
from libsys.models import Book, Publisher, Author from rest_framework.views import APIView from rest_framework.response import Response from .serializers import BookSerializer # Create your views here. # 书籍列表 class BookView(APIView): def get(self, request): """前端获取到的数据格式如下 [ { "id": 4, "title": "go入门", "pub_time": "2017-07-18", "category": "go", "publisher": { "id": 1, "title": "南山出版社" }, "authors": [ { "id": 1, "name": "小明" }, { "id": 2, "name": "小红" } ] }, { ... }, { ... } ] """ book_queryset = Book.objects.all() # 声明一个序列化器 # 用序列化器去序列化queryset(queryset有多个对象的时候,需要声明many=True) # 把数据提交到序列化器,跟序列化器的字段进行匹配,匹配成功就进行序列化 ser_obj = BookSerializer(book_queryset, many=True) return Response(ser_obj.data) # 新增书籍 def post(self, request): """前端传过来的数据应该这样的 { "title": "python工程师", "pub_time": "2015-08-09", "post_category": 1, "publisher_id": 2, "author_list": [1, 2] } """ # 获取前端传过来的数据 book_obj = request.data # 用序列化器做校验 ser_obj = BookSerializer(data=book_obj) if ser_obj.is_valid(): # 校验通过,新增书籍 ser_obj.save() # 这里的save方法会去调用序列化器的create方法 print(ser_obj.validated_data) # validated_data是通过校验的数据,也会封装到data里面 return Response(ser_obj.data) # 校验不通过返回错误信息 return Response(ser_obj.errors) # 编辑书籍 class BookEditView(APIView): def get(self, request, id): book_obj = Book.objects.filter(pk=id).first() ser_obj = BookSerializer(book_obj) return Response(ser_obj.data) def put(self, request, id): book_obj = Book.objects.filter(id=id).first() ser_obj = BookSerializer(instance=book_obj, data=request.data, partial=True) if ser_obj.is_valid(): ser_obj.save() # 这里的save方法会去调用序列化器的update方法 return Response(ser_obj.data) return Response(ser_obj.errors)
3、反向查询的序列化
直接看例子
1.urls
from django.conf.urls import url from my_app import views urlpatterns = [ url(r'^books_reverse/(\d)', views.RevBookView.as_view()), ]
2.models
from django.db import models CHOICES = ((1, "python"), (2, "linux"), (3, "go")) class Publisher(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=20) def __str__(self): return self.name class Book(models.Model): title = models.CharField(max_length=64) publisher = models.ForeignKey(to='Publisher', on_delete=models.CASCADE, null=True) category = models.IntegerField(choices=CHOICES, default=1) def __str__(self): return self.title
3.my_serializer
from rest_framework import serializers ################################ 外键正向查询 ################################## class PublisherSerializer(serializers.Serializer): id = serializers.IntegerField() name = serializers.CharField(max_length=20) class BookSerializer(serializers.Serializer): """外键正向查询""" id = serializers.IntegerField(required=False) title = serializers.CharField(max_length=64) category = serializers.CharField(source='get_category_display', read_only=True) # 正向外键查询:从Book查询Publisher publisher = PublisherSerializer(read_only=True) ################################ 外键反向查询 ################################## class ReverseBookSerializer(serializers.Serializer): id = serializers.IntegerField(required=False) title = serializers.CharField(max_length=64) category = serializers.CharField(source='get_category_display', read_only=True) class ReversePublisherSerializer(serializers.Serializer): """外键反向查询""" id = serializers.IntegerField() name = serializers.CharField(max_length=20) # 反向外键查询:从Publisher查询Book,且关联多个数据的时候,要设置many=True # 跟ORM的反向查询一致,小写的表名_set,或者使用related_name book_set = ReverseBookSerializer(read_only=True,many=True)
4.views
from rest_framework.views import APIView from rest_framework.response import Response from my_app.models import * from my_app.my_serializer import * class RevBookView(APIView): def get(self, request, type): if int(type) == 1: # 正向外键查询:从Book查询Publisher book_queryset = Book.objects.all() ser_obj = BookSerializer(book_queryset, many=True) else: # 反向外键查询:从Publisher查询Book publish_queryset = Publisher.objects.all() ser_obj = ReversePublisherSerializer(publish_queryset, many=True) return Response(ser_obj.data)
5.结果
正向查询
反向查询
四、验证
1、 单个字段的验证(局部钩子)
class BookSerializer(serializers.Serializer): id = serializers.IntegerField(required=False) title = serializers.CharField(max_length=64) 其他字段... # 局部钩子方法:validate_字段名,value 是提交过来的这个字段的数据 def validate_title(self, value): # 定义校验规则:标题必须含有python、linux、go course_list = ['python', 'linux', 'go'] for course in course_list: if course in value.lower(): return value else: raise serializers.ValidationError('输入的书籍名不合法')
2、 多个字段的验证(全局钩子)
class BookSerializer(serializers.Serializer): post_category = serializers.IntegerField(write_only=True) publisher_id = serializers.IntegerField(write_only=True) 其他字段... # 全局钩子方法:validate,attrs 是前端传过来的所有的数据组成的字典 def validate(self, attrs): # 定义校验规则:书籍分类和作者id不能超过3 if attrs['post_category'] > 3 or attrs['publisher_id'] > 3: raise serializers.ValidationError('输入的图书分类或作者不存在') return attrs
3、 自定义校验规则
def my_validate(value): if '周星星' in value: raise serializers.ValidationError('输入的书籍太帅,不合法') return value class BookSerializer(serializers.Serializer): id = serializers.IntegerField(required=False) # validators声明校验的规则 title = serializers.CharField(max_length=64, validators=[my_validate,])
4、校验的权重
自定义校验 > 局部钩子 > 全局钩子
五、ModelSerializer
1、介绍
跟Django的Form组件类似,我们使用DRF进行前后端数据交互,有很多需要序列化和反序列化的字段都跟models模型相关,
那么,DRF也给我们提供了跟模型紧密相关的序列化器:ModelSerializer
-- 继承serializers.ModelSerializer
-- 它和Form有点类似
-- 它会根据模型自动生成一组字段
-- 它默认实现了.update()以及.create()方法
2、定义ModelSerializer序列化器
class BookSerializer(serializers.ModelSerializer): class Meta: model = Book fields = "__all__" # fields = ["id", "title", "pub_time"] # exclude = ["user"] # 分别是所有字段 包含某些字段 排除某些字段
3、外键关系的字段
当序列化类META中定义了depth时,这个序列化类中引用字段(外键)则自动变为只读 class BookSerializer(serializers.ModelSerializer): class Meta: model = Book fields = "__all__" # fields = ["id", "title", "pub_time"] # exclude = ["user"] # 分别是所有字段 包含某些字段 排除某些字段 # depth 代表找嵌套关系的第几层 depth = 1
4、META中的其他参数
class BookSerializer(serializers.ModelSerializer): class Meta: model = Book fields = "__all__" # fields = ["id", "title", "pub_time"] # exclude = ["user"] # 分别是所有字段 包含某些字段 排除某些字段 # depth 代表找嵌套关系的第几层 depth = 1 # 只读字段 read_only_fields = ["id"] # 给某些字段设置额外参数 extra_kwargs = {"title": {"validators": [my_validate,]}}
5、SerializerMethodField方法字段
外键关联的对象有很多字段我们是用不到的,如果都传给前端会有数据冗余, 就需要我们自己去定制序列化外键对象的哪些字段。 使用了方法字段,它会自动去找对应的钩子函数(get_字段名),这个方法字段展示的值就是钩子函数的返回值 钩子函数的参数obj:就是序列化的每个模型对象 book_obj class BookSerializer(serializers.ModelSerializer): # 方法字段 # SerializerMethodField 会去找钩子方法 钩子方法的返回值给这个字段 # 钩子函数:get_字段名称 category_text = serializers.SerializerMethodField(read_only=True) publisher_info = serializers.SerializerMethodField(read_only=True) author_info = serializers.SerializerMethodField(read_only=True) def get_category_text(self, obj): # obj就是序列化的每个模型对象 book_obj return obj.get_category_display() def get_publisher_info(self, obj): return {"id": obj.publisher_id, "title": obj.publisher.title} def get_author_info(self, obj): return [{"id": author.id, "name": author.name} for author in obj.authors.all()] class Meta: model = Book fields = "__all__"
6、完整的ModelSerializer思路
因为depth会让我们外键变成只读,所以一般来说我们不用它, 因为如果是前端发数据过来(post,put等),我们对外键、多对多等字段进行验证是验证其在数据库的中真实值, 因此,把ModelSerializer序列化器fields代表的字段应该用于反序列化,而对序列化到前端的外键、多对多等字段的数据, 我们可以使用SerializerMethodField对其进行处理后展示到前端。 class BookSerializer(serializers.ModelSerializer): # 方法字段 category_text = serializers.SerializerMethodField(read_only=True) publisher_info = serializers.SerializerMethodField(read_only=True) author_info = serializers.SerializerMethodField(read_only=True) # 方法字段的值,取决于它对应的方法字段的钩子函数的返回值 def get_category_text(self, obj): # obj就是序列化的每个模型对象 book_obj return obj.get_category_display() def get_publisher_info(self, obj): return {"id": obj.publisher_id, "title": obj.publisher.title} def get_author_info(self, obj): return [{"id": author.id, "name": author.name} for author in obj.authors.all()] class Meta: model = Book fields = "__all__" # fields = ["id", "title", "pub_time"] # exclude = ["user"] # 分别是所有字段 包含某些字段 排除某些字段 # depth 代表找嵌套关系的第几层 # depth = 1 extra_kwargs = { "category": {'write_only': True}, "publisher": {'write_only': True}, "authors": {'write_only': True}, }
7、ModelSerializer的数据格式
class BookView(APIView): def get(self, request): """序列化的数据如下 [ { "id": 2, "category_text": "python", "publisher_info": { "id": 1, "title": "南山出版社" }, "author_info": [ { "id": 1, "name": "小明" } ], "title": "python开发", "pub_time": "2019-06-03" }, {...}, {...}, ] """ book_queryset = Book.objects.all() # 声明一个序列化器 # 用序列化器去序列化queryset(queryset有多个对象的时候,需要声明many=True) # 把数据提交到序列化器,跟序列化器的字段进行匹配,匹配成功就进行序列化 ser_obj = BookSerializer(book_queryset, many=True) return Response(ser_obj.data) # 新增书籍 def post(self, request): """前端传过来的数据应该这样的 { "title": "python大师", "pub_time": "2013-03-09", "category": 1, "publisher": 2, "authors": [1] } """ # 获取前端传过来的数据 book_obj = request.data # 用序列化器做校验 ser_obj = BookSerializer(data=book_obj) if ser_obj.is_valid(): # 校验通过,新增书籍 ser_obj.save() # 这里的save方法会去调用序列化器的create方法 print(ser_obj.validated_data) # validated_data是通过校验的数据,也会封装到data里面 return Response(ser_obj.data) # 校验不通过返回错误信息 return Response(ser_obj.errors)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix