Django Rest Framework之序列化
一:安装DRF
pip install django==2.1.7
pip install djangorestframework
settings配置文件中注册rest_framework
INSTALLED_APPS = [
...............
'rest_framework',
]
二:APIView

APIView与View的区别:
- APIView继承了View
- APIView中重写了as_view()方法以及dispatch()方法
- 在dispatch()里重新封装了request对象,request = Request(),而旧的request对象变成了_request
- 获取get请求参数使用:request.query_params()
- 获取post请求参数使用:request.data()
不管是View还是APIView最开始调用的都是as_view()方法

APIView继承了View, 并且执行了View中的as_view()方法,最后把view返回了,用csrf_exempt()方法包裹后去掉了csrf的认证
那我们看看View中的as_view()方法做了什么~

在View中的as_view方法返回了view函数,而view函数执行了self.dispatch()方法,而这里的dispatch方法是APIView中的方法

我们去initialize_request中看下把什么赋值给了request,并且赋值给了self.request, 也就是我们在视图中用的request.xxx到底是什么

我们看到,这个方法返回的是Request这个类的实例对象~~我们注意我们看下这个Request类中的第一个参数request,这是django中旧的request

这个Request类把原来的request赋值给了self._request, 也就是说以后_request是我们老的request,新的request是我们这个Request类。那我们继承APIView之后请求来的数据都在哪呢

我们用了rest_framework框架以后,我们的request是重新封装的Request类,request.query_params 存放的是我们get请求的参数,request.data 存放的是我们所有的数据,包括post请求的以及put,patch请求,相比原来的django的request,我们现在的request更加精简,清晰了。
三:serializers.Serializer序列化
3.1 数据模型
from django.db import models
__all__ = ['Book', 'Publisher', 'Author']
class Book(models.Model):
title = models.CharField(max_length=32)
CHOICES = (
(1, 'Python'),
(2, 'Linux'),
(3, 'Go')
)
category = models.IntegerField(choices=CHOICES)
pub_time = models.DateField()
publisher = models.ForeignKey(to = 'Publisher', on_delete=models.CASCADE)
authors = models.ManyToManyField(to = 'Author')
class Publisher(models.Model):
title = models.CharField(max_length=32)
class Author(models.Model):
name = models.CharField(max_length=32)
3.2 创建序列化器
在app目录下创建serializers.py文件
from rest_framework import serializers
from .models import Book
class PublisherSerializer(serializers.Serializer):
id = serializers.IntegerField()
title = serializers.CharField(max_length=32)
class AutherSerializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField(max_length=32)
class BookSerializer(serializers.Serializer):
id = serializers.IntegerField(required=False) # required=False 指明反序时(前端提交数据过来)不做校验
title = serializers.CharField(max_length=32)
pub_time = serializers.DateField()
category = serializers.CharField(source='get_category_display', read_only=True)
# 外键关系的序列化是嵌套的序列化对象;内部通过外键关系的id找到publisher对象,再使用PublisherSerializer(publisher对象)
publisher = PublisherSerializer(read_only=True) # 嵌套序列化器
authors = AutherSerializer(many=True, read_only=True) # 多对多关系需要加上参数:many = True
# read_only:指明该字段只做序列化,反序列化时不走该字段;那么反序列化时就必须重新指定字段名
# write_only:指明该字段只做反序列化
post_category = serializers.IntegerField(write_only=True)
publisher_id = serializers.IntegerField(write_only=True)
author_list = serializers.ListField(write_only=True)
def create(self, validated_data):
# validated_data 校验通过的数据
# 通过ORM操作给Book表添加数据
# print(validated_data)
# {'title': 'Die Hard', 'pub_time': datetime.date(2020, 1, 12), 'post_category': 1, 'publisher_id': 1, 'author_list': [1, 2]}
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):
# instance为需要更新的对象
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'):
instance.authors.set(validated_data['author_list'])
instance.save()
return instance
# 参数校验,与Django中的form校验一样
def validate_title(self, value):
# 单个字段校验;value就是title的值
if 'python' not in value.lower():
raise serializers.ValidationError('标题必须包含python')
return value
def validate(self, attrs):
# 多个字段校验;attrs是个字典,包含前端传递过来的所有字段
if 'python' in attrs['title'].lower() and attrs['post_category'] == 1:
return attrs
raise serializers.ValidationError('参数错误')
自定义数据校验:
def my_validate(value):
if '敏感信息' in value.lower():
raise serializers.ValidationError('some message')
return value
class BookSerializer(serializers.Serializer):
id = serializers.IntegerField(required=False) # required=False 指明反序时(前端提交数据过来)不做校验
title = serializers.CharField(max_length=32, validators = [my_validate,])
............................
validate做数据校验错误返回的key问题:
def validate(self, attrs):
password = attrs.get('password')
re_password = attrs.pop('re_password')
print(password, re_password)
if password != re_password:
raise serializers.ValidationError('两次密码不一致')
return attrs
校验如有错误,报错的格式如下:
{
"status": 1,
"message": {
"non_field_errors": [
"两次密码不一致"
]
}
}
message的key为non_field_errors,传递到前端不方便处理,可以自定义一个key
raise serializers.ValidationError({'password': '两次密码不一致'})
报错格式如下:
{
"status": 1,
"message": {
"password": [
"两次密码不一致"
]
}
}
3.3 类视图
class BookView(APIView):
def get(self, request):
book_queryset = Book.objects.all()
ser_obj = BookSerializer(book_queryset, many = True)
return Response(ser_obj.data)
def post(self, request):
book_obj = request.data
# print(book_obj, type(book_obj))
ser_obj = BookSerializer(data = book_obj)
if ser_obj.is_valid():
# ser_obj.save()使用该方法时,序列化器中必须实例create()方法
ser_obj.save()
return Response(ser_obj.validated_data)
return Response(ser_obj.errors)
class BookEditView(APIView):
def get(self, request, id):
book_obj = Book.objects.filter(id = 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()
# partial=True 指明为部分字段做校验,即传入什么字段就校验什么字段,其他字段不做校验
ser_obj = BookSerializer(instance = book_obj, data = request.data, partial = True)
if ser_obj.is_valid():
ser_obj.save()
return Response(ser_obj.validated_data)
return Response(ser_obj.errors)
3.4 serializers.Serializer序列总结
使用serializers.Serializer方法序列化时注意以下几点:
1:正序与反序字段不一致
2:字段中的参数 required=False 指明反序时(前端提交数据过来)不做数据校验
3:read_only=True 参数指明该字段只做序列化,反序列化时不走该字段;那么反序列化时就必须重新指定字段名
4:write_only 参数指明该字段只用做反序列化
5:序列化器中必须实现create()与update()方法,分别对应创建与更新
6:前端提交的参数校验与Django中的form表单校验数据一样
四:seriarizers.ModelSerializer序列化
4.1 depth
# 只修改了book表的序列化器,其他文件不动
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
序列化后的数据如下:

外键关联的字段只显示了id,添加一个depth参数
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
depth = 1
序列化后的数据如下:

depth=1,意为找到一层关联的数据,即找到publisher关联表,如果publisher中有还有关联的表,不会再往下去找。
注意:
- depth最多不要超过四层
- depth会让所有的外键关系字段变成read_only=True
- 生产环境depth不常用
4.2 反序列化
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
# exclude = ['id']
# depth = 1 # 注意: depth会让所有的外键关系字段变成read_only=True
extra_kwargs = {
'publisher': {'write_only': True},
'authors': {'write_only': True},
}
# 重写正序字段
publisher_info = serializers.SerializerMethodField(read_only=True)
# SerializerMethodField是一个方法字段,即需要一个方法的返回值再赋给该字段
authors_info = serializers.SerializerMethodField(read_only=True)
def get_publisher_info(self, obj):
publisher_obj = obj.publisher
print(publisher_obj)
return {'id': publisher_obj.id, 'title': publisher_obj.title}
def get_authors_info(self, obj):
authors_queryset = obj.authors.all()
return [{'id': author.id, 'name': author.name} for author in authors_queryset]
外键关联的字段正序使用的是publisher_info与authors_info,那么反序还是使用的publisher与authors字段。字段的校验与上面的一样。
4.3 自定义序列字段的两种写法
这里所说的自定义序列字段可以另一张表中的字段,也可以自定要序列化的字段,也可以是外键关联的字段。
这种字段可以使用serializers.SerializerMethodField方法写在序列化器中
也可以在模型类中定义方法
1:写在序列化器中
class BookSerializer(serializers.ModelSerializer):
publisher = serializers.SerializerMethodField()
def get_publisher(self, obj):
return obj.publisher.name
class Meta:
model = Book
fields = ['name', 'price', 'publisher']
注意:使用serializers.SerializerMethodField方法时,在fields列表中必须写上该字段,如上面代码中的publisher。如果fields列表中没有指定publiser字段,会报如下异常:
The field 'publisher' was declared on serializer BookSerializer, but has not been included in the 'fields' option.
2:写在模型类中
class Book(BaseModel):
name = models.CharField(max_length=64)
price = models.DecimalField(max_digits=5, decimal_places=2)
img = models.ImageField(upload_to='img', default='img/default.jpg')
publisher = models.ForeignKey(to='Publisher', on_delete=models.CASCADE)
authors = models.ManyToManyField(to='Author')
class Meta:
db_table = 'gin_book'
def __str__(self):
return self.name
@property
def publisher_name(self): # 定义成一个方法属性,方法名与属性名不能重复
return self.publisher.name
序列化类中的fields列表,可以指定publisher_name;不指定也不报异常
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['name', 'price', 'publisher_name'] # 不指定publisher_name字段不会抛异常
使用该方法时反序列化如下:
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['name', 'price', 'publisher_name', 'img', 'author_list', 'publisher', 'authors']
extra_kwargs = {
'publisher': {'write_only': True},
'authors': {'write_only': True},
'name': {
'required': True,
'min_length': 3,
'error_messages': {
'required': '必填项',
'min_length': '至少3个字符'
}
}
}
视图类:
class BooksView(APIView):
def get(self, request):
queryset = Book.objects.filter(is_delete = False)
serializer = BookSerializer(queryset, many = True)
return Response(serializer.data)
def post(self, request):
ser_obj = BookSerializer(data = request.data)
ser_obj.is_valid(raise_exception=True) # 当校验失败会马上终止当前视图方法,抛出异常给前台
book_obj = ser_obj.save()
return Response({'status': 0, 'message': 'OK', 'result': BookSerializer(book_obj).data})
4.4 自定义联表深度(嵌套)
模型类如下:
class Book(BaseModel):
name = models.CharField(max_length=64)
price = models.DecimalField(max_digits=5, decimal_places=2)
img = models.ImageField(upload_to='img', default='img/default.jpg')
publisher = models.ForeignKey(to='Publisher', on_delete=models.CASCADE)
authors = models.ManyToManyField(to='Author')
class Meta:
db_table = 'gin_book'
def __str__(self):
return self.name
class Publisher(BaseModel):
name = models.CharField(max_length=64)
address = models.CharField(max_length=64)
class Meta:
db_table = 'gin_publisher'
def __str__(self):
return self.name
需求:Book表序列化时,也要序列化Pulisher表中指定的字段
class PublisherSerializer(serializers.ModelSerializer):
class Meta:
model = Publisher
fields = ['name', 'address']
class BookSerializer(serializers.ModelSerializer):
# 自定义连表深度 - 子序列化方式
publisher = PublisherSerializer()
class Meta:
model = Book
fields = ['name', 'price', 'publisher']

4.5 单增群增
序列化器如下:
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['name', 'price', 'publisher_name', 'img', 'author_list', 'publisher', 'authors']
extra_kwargs = {
'publisher': {'write_only': True},
'authors': {'write_only': True},
'name': {
'required': True,
'min_length': 3,
'error_messages': {
'required': '必填项',
'min_length': '至少3个字符'
}
}
}
def validate_name(self, value):
if Book.objects.filter(name = value).exists():
raise serializers.ValidationError('书籍名已存在')
return value
def validate(self, attrs):
# todo: do something
return attrs
单增群增,序列化器不用改。但需要判断前台数据为字典时为单增,为列表时是群增
class BooksView(APIView):
def get(self, request):
queryset = Book.objects.filter(is_delete=False)
serializer = BookSerializer(queryset, many=True)
return Response(serializer.data)
def post(self, request):
if isinstance(request.data, dict):
many = False
elif isinstance(request.data, list):
many = True
else:
return Response({'status': 1, 'message': 'invalid data'})
ser_obj = BookSerializer(data=request.data, many=many)
ser_obj.is_valid(raise_exception=True) # 当校验失败会马上终止当前视图方法,抛出异常给前台
results = ser_obj.save()
return Response({'status': 0, 'message': 'OK', 'result': BookSerializer(results, many=many).data})
前台传递数据格式如下:
[
{
"name": "better man",
"price": 96.66,
"authors": [1,3],
"publisher": 1
},
{
"name": "tomorrow will be better",
"price": 100,
"authors": [2],
"publisher": 2
}
]
4.6 单删群删
def delete(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
pks = [pk] # 单删在URL中传递主键id
else:
pks = request.data.get('pks') # 群删传递JSON数据:{'pks': [2,4,5,6]}
if Book.objects.filter(pk__in = pks, is_delete = False).update(is_delete = True):
return Response({'status': 0, 'message': 'success'})
return Response({'status': 0, 'message': 'failed'})
4.7 修改数据
1:单整体修改
即修改数据时所传递的数据字段不能少
def put(self, request, pk):
book_obj = Book.objects.filter(pk = pk).first()
serializer = BookSerializer(instance = book_obj, data = request.data)
# 有 instance = book_obj 参数,当serializer.save()时会走update方法;如果没有instance = book_obj 参数会走create方法
# data = request.data 必须要有,因为下面的is_valid方法校验的就是data中的数据
serializer.is_valid(raise_exception=True)
book = serializer.save()
return Response(BookSerializer(book).data, status = status.HTTP_201_CREATED)
前端传递数据如下:
{
"name": "take me to you heart",
"price": 68.3,
"publisher": 1,
"authors": [1]
}
2:单局部修改
即前端只传递部分字段,传递过来的字段做数据校验,校验通过后更新;没传递过来的字段不做数据校验
def patch(self, request, pk):
book_obj = Book.objects.filter(pk=pk).first()
# 方法同上,只是多了个 partial=True 参数;即传递过来的字段做校验,没有传递过来的字段不做校验
serializer = BookSerializer(instance=book_obj, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
book = serializer.save()
return Response(BookSerializer(book).data, status=status.HTTP_201_CREATED)
4.8 批量修改数据
class BooksAPIView(APIView):
........................
# 请求数据格式:[{pk:1, name: 123}, {}, {}]
def patch(self, request, *args, **kwargs):
request_data = request.data
pk = kwargs.get('pk')
if pk and isinstance(request_data, dict):
# 单改
pks = [pk]
request_data = [request.data]
elif not pk and isinstance(request_data, list):
# 群改
pks = []
for dic in request_data:
pk = dic.pop('pk', None)
pks.append(pk)
if not pks:
return Response({'status': 1, 'message': 'invalid data', 'result': ''})
else:
return Response({'status': 1, 'message': 'invalid data', 'result': ''})
objs = []
new_request_data = []
for index, pk in enumerate(pks):
try:
obj = Book.objects.get(pk = pk, is_delete = False)
objs.append(obj)
new_request_data.append(request_data[index])
except Exception as e:
continue
print(objs)
print(new_request_data)
book_ser = BookSerializer(instance = objs, data = new_request_data, partial = True, many = True)
book_ser.is_valid(raise_exception=True)
book_obj = book_ser.save()
return Response({'status': 1, 'message': 'success', 'result': BookSerializer(book_obj, many = True).data})
注意:批量修改数据,序列化器中必须要有ListSerializer,如果没有会抛出异常:
NotImplementedError: Serializers with many=True do not support multiple update by default, only multiple create. For updates it is unclear how to deal with insertions and deletions. If you need to support multiple update, use a `ListSerializer` class and override `.update()` so you can specify the behavior exactly.
序列化类:
# ListSerializer与ModelSerializer建立关联的是:ModelSerializer的Meta类的 list_serializer_class
class BookListSerializer(serializers.ListSerializer):
def update(self, instance, validated_data):
print(instance) # 要更新的对象们
print(self.child) # 服务的模型序列化类(BookSerializer)
for index, obj in enumerate(instance):
self.child.update(obj, validated_data[index])
return instance
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['name', 'price', 'publisher', 'img', 'author_list', 'publisher_name', 'authors']
extra_kwargs = {
'name': {'required': True, 'min_length': 1, 'error_messages': {'required': '必填项', 'min_length': '至少3个字符'}},
'publisher': {'write_only': True},
'authors': {'write_only': True}
}
list_serializer_class = BookListSerializer
4.9 视图类与序列化类传参
在视图类中,可以通过request得到登陆用户request.user
在序列化类中,要完成数据库数据的校验与入库操作,可能会需要知道当前的登录用户,但序列化类无法访问request对象
在视图类中实例化序列化对象时,可以通过context参数将request对象传递进去
book_ser = BookSerializer(context = {'request': request}, instance = objs, data = new_request_data, partial = True, many = True)
序列化类中可以在钩子函数中接受该参数:
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['name', 'price', 'publisher', 'img', 'author_list', 'publisher_name', 'authors']
extra_kwargs = {
'name': {'required': True, 'min_length': 1, 'error_messages': {'required': '必填项', 'min_length': '至少3个字符'}},
'publisher': {'write_only': True},
'authors': {'write_only': True}
}
list_serializer_class = BookListSerializer
def validate(self, attrs):
print(self.context.get('request').method)
return attrs
本文详细介绍了Django Rest Framework(DRF)中的序列化,包括安装DRF,使用APIView,serializers.Serializer序列化数据模型,以及serializers.ModelSerializer序列化,深度探讨了序列化过程中的参数设置、数据校验、反序列化、增删改查操作等关键点。
659

被折叠的 条评论
为什么被折叠?



