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,])
............................
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字段。
字段的校验与上面的一样。