drf之序列化组件
drf之序列化组件
模型类准备
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.CharField(max_length=32)
publisher = models.CharField(max_length=32)
迁移后随便插入几条数据就可以简单的测试接口了。
序列化器介绍与快速使用
在写接口时,需要序列化,需要反序列化,而且反序列化的过程中要做数据校验
---》drf直接提供了固定的写法,只要按照固定写法使用,就能完成上面的三个需求。
drf提供了两个类,Serializer、ModelSerializer,使用这些类也能完成并进一步简化接口的编写。
序列化类基本使用--Serializer
序列化类的建立
编写一个serializer.py文件
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
# 序列化某些字段,这里写要序列化的字典
name = serializers.CharField() # serializers下大致跟models下的类是对应的
price = serializers.CharField()
publish = serializers.CharField()
我们在文件中建立继承序列化器的类,在内部添加需要序列化和反序列化的字段。
使用序列化类进行序列化
在views.py中,我们可以在函数中,可以调用序列化类BookSerializer产生序列化对象
查询数据
# 视图文件
class BookView(APIView):
def get(self, request):
books = Book.objects.all()
# 使用序列化类来完成---》得有个序列化类
# instance要序列化的数据books queryset对象
# many=True 只要是queryset对象要传many=True,如果是单个对象就不用传
ser = BookSerializer(instance=books, many=True)
return Response(ser.data) # 无论是列表还是字典都可以序列化
class BookDetailView(APIView):
# def get(self, request,pk):
def get(self, request, *args, **kwargs):
book = Book.objects.filter(pk=kwargs.get('pk')).first()
# 序列化
ser = BookSerializer(instance=book)
return Response(ser.data)
# url文件
urlpatterns = [
path('books/', views.BookView.as_view()),
path('books/<int:pk>/', views.BookDetailView.as_view()),
]
序列化对象中:
- 用于查询的序列化对象,需要初始化instance参数,接收查询结果
- 查询结果如果是queryset则many参数要设置为True,如果是数据对象则不需要
- 序列化对象初始化后,在data属性中会将数据对象或queryset序列化车成字典或列表
- 利用Response可以直接将data的列表或字典都能序列化成json格式
关于视图类:
- 我们通常将对单和对多操作分成两个视图类,这样我们就可以在路由层直接分路,不必在如get函数中再添加判断
路由层:
- 不需要后续添加pk参数的,让视图BookView处理
- 需要后续添加pk参数的,让视图BookDetailView处理
使用序列化类进行反序列化
serializer反序列化可以帮助我们直接校验数据是否合法,然后直接做出新增或修改的操作。
新增数据
# 序列化类
class BookSerializer(serializers.Serializer):
# 序列化某些字段,这里写要序列化的字典
name = serializers.CharField() # serializers下大致跟models下的类是对应的
price = serializers.CharField()
publish = serializers.CharField()
def create(self, validated_data):
# 保存的逻辑
# validated_data 校验过后的数据 {name,price,publish}
# 保存到数据库
book = Book.objects.create(**validated_data)
# 一定不要返回新增的对象
return book
# 视图类
class BookView(APIView):
def post(self, request):
# requset.data # 前端提交的要保存的数据----》校验数据---》存
ser = BookSerializer(data=request.data) # 把前端传入的要保存的数据,给data参数
# 校验数据
if ser.is_valid():
# 保存---->需要自己写,要在序列化类BookSerializer中写----》create方法
ser.save() # 调用ser.save,自动触发咱们写的create,保存起来
return Response({'code': 100, 'msg': '新增成功', 'result': ser.data})
else:
return Response({'code': 101, 'msg': ser.errors})
序列化类中写create,在校验后会自动执行新增。
- create函数会接收validated_data参数,保存的是校验成功的数据
- 在上述代码中,因为序列化的字段和模型类相对应,所以可以直接使用打散
视图类中写post函数表示执行新增:
- 初始化序列对象加入data参数接收request.data,它会在校验处理后传入validated_data
- 根据BookSerializer初始化传入的参数中没有instance,判断为新增,在ser.save()中自动执行序列化类中的create
修改数据
# 序列化类
class BookSerializer(serializers.Serializer):
# 序列化某些字段,这里写要序列化的字典
name = serializers.CharField() # serializers下大致跟models下的类是对应的
price = serializers.CharField()
publish = serializers.CharField()
def update(self, instance, validated_data):
# instance 要修改的对象
# validated_data 校验过后的数据
instance.name = validated_data.get('name')
instance.price = validated_data.get('price')
instance.publish = validated_data.get('publish')
instance.save() # orm的单个对象,修改了单个对象的属性,只要调用对象.save,就能把修改保存到数据库
return instance # 不要忘了吧修改后的对象,返回
# 视图类
class BookDetailView(APIView):
def put(self, request, pk):
book = Book.objects.filter(pk=pk).first()
# 反序列化保存 ---借助于序列化类
ser = BookSerializer(data=request.data, instance=book)
if ser.is_valid():
ser.save() # 由于没有重写update,所以这报错
return Response({'code': 100, 'msg': '修改成功', 'result': ser.data})
else:
return Response({'code': 101, 'msg': ser.errors})
序列化类使用update,在校验后自动执行:
- instance接收修改的实例数据对象
- validated_data接收校验后合法的数据
- instance注意用save保存,才能在数据库生效
- 将修改后的instance返回出去,最终会传给序列化对象的data属性
视图类使用put表示修改动作:
- put中先查询得到一个数据对象,再传入序列化类的instance
- data同新增,将body中的数据处理好传入
- 产生的序列化对象根据instance有值,save时自动执行了update
基于序列化编写5个接口
在上文已经编写了查多、查单、新增、修改,而关于删除是不需要序列化组件参与的,其总体的内容如下:
点击查看代码
# 序列化类
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
# 序列化某些字段,这里写要序列化的字典
name = serializers.CharField() # serializers下大致跟models下的类是对应的
price = serializers.CharField()
publish = serializers.CharField()
def create(self, validated_data):
# 保存的逻辑
# validated_data 校验过后的数据 {name,price,publish}
# 保存到数据库
book = Book.objects.create(**validated_data)
# 一定不要返回新增的对象
return book
def update(self, instance, validated_data):
# instance 要修改的对象
# validated_data 校验过后的数据
instance.name = validated_data.get('name')
instance.price = validated_data.get('price')
instance.publish = validated_data.get('publish')
instance.save() # orm的单个对象,修改了单个对象的属性,只要调用对象.save,就能把修改保存到数据库
return instance # 不要忘了吧修改后的对象,返回
# 视图类
class BookView(APIView):
def get(self, request):
books = Book.objects.all()
# 使用序列化类来完成---》得有个序列化类
# instance要序列化的数据books queryset对象
# many=True 只要是queryset对象要传many=True,如果是单个对象就不用传
ser = BookSerializer(instance=books, many=True)
return Response(ser.data) # 无论是列表还是字典都可以序列化
def post(self, request):
# requset.data # 前端提交的要保存的数据----》校验数据---》存
ser = BookSerializer(data=request.data) # 把前端传入的要保存的数据,给data参数
# 校验数据
if ser.is_valid():
# 保存---->需要自己写,要在序列化类BookSerializer中写----》create方法
ser.save() # 调用ser.save,自动触发咱们写的create,保存起来
return Response({'code': 100, 'msg': '新增成功', 'result': ser.data})
else:
return Response({'code': 101, 'msg': ser.errors})
class BookDetailView(APIView):
# def get(self, request,pk):
def get(self, request, *args, **kwargs):
book = Book.objects.filter(pk=kwargs.get('pk')).first()
# 序列化
ser = BookSerializer(instance=book)
return Response(ser.data)
def put(self, request, pk):
book = Book.objects.filter(pk=pk).first()
# 反序列化保存 ---借助于序列化类
ser = BookSerializer(data=request.data, instance=book)
if ser.is_valid():
ser.save() # 由于没有重写update,所以这报错
return Response({'code': 100, 'msg': '修改成功', 'result': ser.data})
else:
return Response({'code': 101, 'msg': ser.errors})
def delete(self, requset, pk):
Book.objects.filter(pk=pk).delete()
return Response({'code': 100, 'msg': '删除成功'})
反序列化类校验
在上文中,我们在post和put函数中对request.data中的数据进行了is_valid的校验。
ps:实际上由于有全局异常捕获,我们可以直接用is_valid(raise_exception=True)校验,那么校验失败的话会直接报错。
而反序列化时,是基于什么标准进行校验的呢?
- 先执行serializer字段自己的校验规则如max_length
- 再validators参数=[方法1,方法2,],方法可以是自定义的,也可以用django.core.validators.RegexValidator
- 局部钩子校验规则
- 全局钩子校验规则
钩子函数
# 序列化类
# 局部钩子
def validate_name(self, name):
# 校验name是否合法
if name.startswith('sb'):
# 校验不通过,抛异常
raise ValidationError('不能以sb开头')
else:
return name
# 全局钩子
def validate(self, attrs):
# 校验过后的数据,书名跟出版社名字不能一致
if attrs.get('name') == attrs.get('publish'):
raise ValidationError('书名跟出版社名字不能一致')
else:
return attrs
序列化类常用字段
1 BooleanField BooleanField()
2 NullBooleanField NullBooleanField()
3 CharField CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
4 EmailField EmailField(max_length=None, min_length=None, allow_blank=False)
5 RegexField RegexField(regex, max_length=None, min_length=None, allow_blank=False)
6 SlugField SlugField(max_length=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+
7 URLField URLField(max_length=200, min_length=None, allow_blank=False)
8 UUIDField UUIDField(format=’hex_verbose’) format: 1) 'hex_verbose' 如"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex' 如 "5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a"
9 IPAddressField IPAddressField(protocol=’both’, unpack_ipv4=False, **options)
10 IntegerField IntegerField(max_value=None, min_value=None)
11 FloatField FloatField(max_value=None, min_value=None)
12 DecimalField DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置
13 DateTimeField DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
14 DateField DateField(format=api_settings.DATE_FORMAT, input_formats=None)
15 TimeField TimeField(format=api_settings.TIME_FORMAT, input_formats=None)
16 DurationField DurationField()
17 ChoiceField ChoiceField(choices) choices与Django的用法相同
18 MultipleChoiceField MultipleChoiceField(choices)
19 FileField FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
20 ImageField ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ListField ListField(child=, min_length=None, max_length=None)
DictField DictField(child=)
重点:
CharField IntegerField DecimalField DateTimeField BooleanField
ListField DictField
序列化类字段常用参数
CharField及其子类的(EmailField) ---》反序列化的校验,字段自己的规则
参数 | 含义 |
---|---|
max_length/min_length | 最大长度/最小长度 |
allow_blank | 是否允许为空 |
trim_whitespace | 是否截断空白字符 |
IntegerField的常用参数:max_value/min_value
所有字段都有的参数:
参数 | 含义 |
---|---|
required | 表明该字段在反序列化时必须输入,默认True |
default | 反序列化时的默认值 |
allow_null | 表明该字段是否允许传入None,默认False |
validators | 该字段使用的验证器 |
---- | ---- |
error_messages | 包含错误编号与错误信息的字典 |
label | 用于HTML展示API页面时,显示的字段名称 |
help_text | 用于HTML展示API页面时,显示的字段帮助提示信息 |
**** | **** |
read_only | 表明该字段仅用于序列化输出,默认False |
write_only | 表明该字段仅用于反序列化输入,默认False |
序列化定制
所谓定制就是可以一定程度上改写序列化的规则,控制序列化数据的形式
数据准备
# 创建关联表
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.CharField(max_length=32)
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE) # 留住,还有很多
authors = models.ManyToManyField(to='Author')
class Publish(models.Model):
name = models.CharField(max_length=32)
addr = models.CharField(max_length=32)
class Author(models.Model):
name = models.CharField(max_length=32)
phone = models.CharField(max_length=11)
# 迁移,录入数据
source方法
配置序列化字段的参数source
-
自有字段,直接写字段名字
name_real = serializers.CharField(max_length=8, source='name') -
关联字段,一对多的关联,直接点
publish = serializers.CharField(source='publish.name') -
多对多,搞不了,source不能用
authors=serializers.CharField(source='authors.all')
# 序列化类
class BookSerializer(serializers.Serializer):
# 字段参数,通用的,所有字段都可以写 通过source指定哪个字段
# 自有字段,直接写字段名字
name_real = serializers.CharField(max_length=8, source='name')
real_price = serializers.CharField(source='price')
# 关联字段,一对多的关联,直接点
publish = serializers.CharField(source='publish.name')
#多对多,搞不了,source不能用
# authors = serializers.CharField(source='authors.all')
ps:source用到了反射的原理,当传入的实例(queryset或者orm对象)有source参数的字段属性,那么则以这个字段属性的数据做序列化。
SerializerMethodField定制
class BookSerializer(serializers.Serializer):
name = serializers.CharField(max_length=8)
price = serializers.CharField()
publish_detail = serializers.SerializerMethodField()
def get_publish_detail(self, obj):
return {'name': obj.publish.name, 'addr': obj.publish.addr}
author_list = serializers.SerializerMethodField()
def get_author_list(self, obj):
return [{'name': author.name, 'phone': author.phone} for author in obj.authors.all()]
- 上述代码中,将一对多的外键,序列化成字典,内容为外键表的详情
- 将多对多的外键,序列化成列表套字典,内容为多个外键数据详情字典的列表
- queryset的单个数据对象作为参数obj传入此函数,方便序列化取出数据
表模型中定制
#### 模型表中,写两个函数伪装成数据属性(可以不伪装)
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.CharField(max_length=32)
publish = models.ForeignKey(to='Publish', on_delete=models.SET_NULL)
authors = models.ManyToManyField(to='Author')
@property
def publish_detail(self):
return {'name': self.publish.name, 'addr': self.publish.addr}
@property
def author_list(self):
return [{'name': author.name, 'phone': author.phone} for author in obj.authors.all()]
# 序列化类
class BookSerializer(serializers.Serializer):
name = serializers.CharField(max_length=8)
price = serializers.CharField()
# publish_detail = serializers.CharField()
publish_detail = serializers.DictField()
author_list = serializers.ListField()
- 原理就是模型表中的属性,序列化表都会对其进行反射取资源,那么方法也一样是模型表的属性之一,严谨来说加装property更清晰
- 对于定制的序列化,我们如果想让其成为方便操作的字典和列表(自定义对象和数组),则使用serializers.DictField()和serializers.ListField()
多表联合反序列化
还是用上小节用的三张模型表,Book,Publisher,Author
需求:
查询图书时,我们进行序列化,是对外表递归查询,得到更详细的外键数据;
新增图书时,前端传入的数据格式为{name:红楼梦,price:19,publish_id:1,authors:[1,2]}
即publish和authors传入的是主键值,类似于选项的场景。
修改图书则与新增图示传入的数据格式一致。
# 视图类
class BookView(APIView):
def post(self, request):
ser = BookSerializer(data=request.data)
if ser.is_valid():
ser.save()
return Response({'code': 100, 'msg': '新增成功'})
else:
return Response({'code': 101, 'msg': ser.errors})
class BookDetailView(APIView):
def put(self, request, pk):
book = Book.objects.filter(pk=pk).first()
ser = BookSerializer(data=request.data, instance=book)
if ser.is_valid():
ser.save()
return Response({'code': 100, 'msg': '修改成功'})
else:
return Response({'code': 101, 'msg': ser.errors})
# 序列化类
class BookSerializer(serializers.Serializer):
# name和price 既用来序列化,又用来反序列化 即写又读 ,不用加read_only,write_only
name = serializers.CharField(max_length=8)
price = serializers.CharField()
# 只用来做序列化 只读 read_only
publish_detail = serializers.DictField(read_only=True)
author_list = serializers.ListField(read_only=True)
# 只用来做反序列化 只写 write_only
publish_id = serializers.CharField(write_only=True)
authors = serializers.ListField(write_only=True)
# 新增要重写create方法
def create(self, validated_data):
# validated_data 校验过后的数据,{name:红楼梦,price:19,publish:1,authors:[1,2]}
# 新增一本图书
book = Book.objects.create(name=validated_data.get('name'),
price=validated_data.get('price'),
publish_id=validated_data.get('publish_id'))
# 作者也要关联上
# book.authors add remove set clear....
book.authors.add(*validated_data.get('authors'))
# book.authors.add(1,2)
return book
def update(self, instance, validated_data):
# validated_data 校验过后的数据,{name:红楼梦,price:19,publish:1,authors:[1,2]}
instance.name = validated_data.get('name')
instance.price = validated_data.get('price')
instance.publish_id = validated_data.get('publish')
# 先清空,再add
authors = validated_data.get('authors')
instance.authors.set(authors)
instance.save()
return instance
ModelSerializer
ModelSerializer继承自Serializer,其特点是:
- 与模型表强关联
- 内部重写了create和update,大部分请求可以不用写create和update了
class BookSerializer(serializers.ModelSerializer):
# 跟表有关联
class Meta:
model = Book # 跟book表建立了关系 序列化类和表模型类
# fields = '__all__' # 序列化所有Book中的字段 id name price publish authors
fields = ['name', 'price', 'publish_detail', 'author_list', 'publish', 'authors'] # 序列化所有Book中的name和price字段字段
# 定制name反序列化时,最长不能超过8 给字段类加属性---方式一
extra_kwargs = {'name': {'max_length': 8},
'publish_detail': {'read_only': True},
'author_list': {'read_only': True},
'publish': {'write_only': True},
'authors': {'write_only': True},
}
# 我们也可以重写字段覆盖掉fields中的字段,以覆盖掉一些限制条件
# name = serializers.CharField(max_length=8),但extra_kwargs已经可以达到重写的目的
# 直接写方法也可以将其作为序列化类的字段,拿到一些数据
def get_publish_detail(self, obj):
return {'name': obj.publish.name, 'addr': obj.publish.addr}
用法汇总:
- 固定写法Meta类,内部含model参数关联模型表,fields确定字段,也可以注册没有的字段
- extra_kwargs保存以字典形式存储的字段参数
- 可以在序列化类体中重写字段覆盖fields中的字段,以清除一些限制条件
- 在这个序列化类中,继承了定制化字段和钩子函数方法,用法与之前保持一致
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异