【DRF框架】序列化组件
DRF框架的序列化组件
在前后端分离的应用模式中,后端仅返回前端所需的数据,返回的数据类似是JSON,因此需要使用序列化组件进行序列化再将数据返回
使用JsonResponse做序列化
# 使用JsonResponse做序列化
class SearchBook(View):
def get(self, request):
book_queryset = Book.objects.values('id', 'title', 'category', 'pub_time', 'publisher')
book_list = list(book_queryset)
for book in book_list:
# 将publisher转化为字典格式
publisher_obj = Publisher.objects.filter(id=book['publisher']).first()
book['publisher'] = {
'id': publisher_obj.id,
'title': publisher_obj.title
}
# JsonResponse序列化列表,需要加safe=False,json_dumps_params={'ensure_ascii':False}处理编码问题
ret = JsonResponse(book_list, safe=False, json_dumps_params={'ensure_ascii': False})
return HttpResponse(ret)
使用django进行序列化
# 使用Django自带的serializers做序列化
from django.core.serializers import serialize
class SearchBook1(View):
def get(self, request):
book_queryset = Book.objects.all()
# 使用serialize进行序列化
ret = serialize('json', book_queryset, ensure_ascii=False) # ensure_ascii=False处理编码问题
return HttpResponse(ret)
使用DRF框架的准备
1.导入模块
from rest_framework.response import Response
from rest_framework.views import APIView
2.注册rest_framework
# settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'SerDemo.apps.SerdemoConfig',
'rest_framework' # 注册rest_framework
]
DRF框架的APIView
所有的类都继承了APIView
1.APIView继承了View
2.豁免了csrf
3.重新封装了request
4.request新的属性
request._request == View.request
request.query_params == View.request.GET
request.data == request.POST/PUT (除了GET请求的其他请求)
使用序列化组件前的准备
# models.py
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')
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=32)
class Meta:
verbose_name = '出版社'
verbose_name_plural = verbose_name
class Author(models.Model):
name = models.CharField(max_length=32)
class Meta:
verbose_name = '作者'
verbose_name_plural = verbose_name
序列化组件
'''
根据模型构建序列化器
'''
# serializers.py
from rest_framework import serializers # 导入框架的序列化器
# 外键字段的序列化器
class PublisherSerializer(serializers.Serializer):
# 根据外键模型的字段定义字段进行匹配
id = serializers.IntegerField()
title = serializers.CharField(max_length=32)
# 多对多字段的序列化器
class AuthorSerializer(serializers.Serializer):
# 根据多对多字段模型的字段定义字段进行匹配
id = serializers.IntegerField()
name = serializers.CharField(max_length=32)
class BookSerializer(serializers.Serializer):
# 定义模型中需要序列化的字段,匹配的上的进行序列化,匹配不上的丢弃
id = serializers.IntegerField()
title = serializers.CharField(max_length=32)
category = serializers.CharField(source='get_category_display') # source通过ORM操作获得CHOICES的值
pub_time = serializers.DateField()
# 外键字段,先声明外键字段的序列化器再实例化
publisher = PublisherSerializer()
# 多对多字段,先声明多对多字段的序列化器再实例化
authors = AuthorSerializer(many=True) # 多个模型对象使用many=True
正序列化(GET请求)
1.声明序列化器
2.将每个模型对象放到序列化器进行序列化化,多个对象必须指定many=True
3.序列化器进行字段匹配,匹配上的进行序列化,匹配不上的丢弃,且必须满足序列化的所有字段要求
4.序列化后返回序列化对象
5.数据都在序列化对象.data
6.通过Response返回
class Search_book(APIView):
# 查询数据,GET请求
def get(self,request):
book_queryset = Book.objects.all()
# 使用序列化器进行序列化,返回序列化对象
ser_obj = BookSerializer(book_queryset,many=True) # 多个queryset对象使用many=True
# 返回数据
return Response(ser_obj.data) # 序列化好的数据都在 序列化对象.data
序列化组件的方法
反序列化不验证:required=False
仅在正序列化验证:read_only=True
仅在反序列化验证:write_only=True
序列化多个对象:many=True
反序列化(POST请求)
1.序列化器对校验字段进行过滤
2.获取前端的数据,传入到序列化器,返回序列化器对象
3.对序列化器进行校验,不通过返回错误信息
4.校验通过调用save()方法,其内部会调用create()方法
5.序列化器自定义create()方法新增模型对象并返回
6.通过Response返回数据
'''
根据模型构建序列化器
'''
# serializers.py
from rest_framework import serializers # 导入框架的序列化器
from SerDemo.models import Book
# 外键字段的序列化器
class PublisherSerializer(serializers.Serializer):
# 根据外键模型的字段定义字段进行匹配
id = serializers.IntegerField()
title = serializers.CharField(max_length=32)
# 多对多字段的序列化器
class AuthorSerializer(serializers.Serializer):
# 根据多对多字段模型的字段定义字段进行匹配
id = serializers.IntegerField()
name = serializers.CharField(max_length=32)
class BookSerializer(serializers.Serializer):
# 定义模型中需要序列化的字段,匹配的上的进行序列化,匹配不上的丢弃
id = serializers.IntegerField(required=False) # 反序列化不匹配该字段
title = serializers.CharField(max_length=32)
category = serializers.CharField(source='get_category_display',read_only=True) # source通过ORM操作获得CHOICES的值
category_post = serializers.IntegerField(write_only=True) # 用于反序列化的字段
pub_time = serializers.DateField()
# 外键字段,先声明外键字段的序列化器再实例化
publisher = PublisherSerializer(read_only=True) # 仅在正序列化匹配该字段
publisher_id = serializers.IntegerField(write_only=True) # 仅在反序列化匹配该字段
# 多对多字段,先声明多对多字段的序列化器再实例化
authors = AuthorSerializer(read_only=True,many=True) # 多个模型对象使用many=True,正序列化匹配该字段
authors_list = serializers.ListField(write_only=True) # 反序列化匹配该字段
# 新增对象调用create方法
def create(self, validated_data):
# validated_data 是数据的字典
# 创建对象
book_obj = Book.objects.create(
title=validated_data.get('title'),
category=validated_data.get('category_post'),
pub_time = validated_data.get('pub_time'),
publisher_id = validated_data.get('publisher_id'),
)
book_obj.authors.add(*validated_data['authors_list']) # 多对多字段使用add
book_obj.save()
return book_obj
# 新增数据,POST请求
def post(self,request):
# 对提交的数据进行反序列化,返回一个序列化对象
ser_obj = BookSerializer(data=request.data)
# 对序列化对象进行校验
if ser_obj.is_valid():
# 校验通过,新增数据
ser_obj.save() # 调用序列化器的create()方法
return Response(ser_obj.data) # 返回新增的数据
else:
# 校验不通过
return Response(ser_obj.errors)
源码流程
# 2.1、开始初始化,执行PubliserView.as_view()
class APIView(View):
@classmethod
def as_view(cls, **initkwargs):
# 1.2、开始调用父类的as_view方法
view = super(APIView, cls).as_view(**initkwargs) # view
view.cls = cls
view.initkwargs = initkwargs
return csrf_exempt(view)
class View(object):
# 2.2、返回View.view方法名
@classonlymethod
def as_view(cls, **initkwargs):
def view(request, *args, **kwargs):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
return self.dispatch(request, *args, **kwargs)
return view
# 3、url初始化后:
url(r'^publishers/$',View.view)
# 4、用户开始url,则开始执行View.view()方法,传入原始的request对象,并调用PubliserView的dispatch方法
def view(request, *args, **kwargs):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
return self.dispatch(request, *args, **kwargs)
# step 5:PubliserView的父类有dispatch方法
class PubliserView(APIView):
pass
class ApiView(View):
def dispatch(self, request, *args, **kwargs):
pass
# step 6:构建新的request对象
class ApiView(View):
def dispatch(self, request, *args, **kwargs):
self.args = args
self.kwargs = kwargs
# 6.1
request = self.initialize_request(request, *args, **kwargs)
# 6.2
def initialize_request(self, request, *args, **kwargs):
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
# 6.3
class Request(object):
def __init__(...):
self._request = request
self._data = Empty
self._files = Empty
self._full_data = Empty
self._content_type = Empty
@property
def data(self):
if not _hasattr(self, '_full_data'):
# 开始封装self._request._post、self._request._file、self._full_data
self._load_data_and_files()
return self._full_data
@property
def query_params(self):
return self._request.GET
@property
def POST(self):
# Ensure that request.POST uses our request parsing.
if not _hasattr(self, '_data'):
self._load_data_and_files()
if is_form_media_type(self.content_type):
return self._data
return QueryDict('', encoding=self._request._encoding)
@property
def FILES(self):
if not _hasattr(self, '_files'):
self._load_data_and_files()
return self._files
# 6.3.1
def _load_data_and_files(self):
if not _hasattr(self, '_data'):
self._data, self._files = self._parse()
if self._files:
self._full_data = self._data.copy()
self._full_data.update(self._files)
else:
self._full_data = self._data
if is_form_media_type(self.content_type):
self._request._post = self.POST
self._request._files = self.FILES
# 6.3.2
def is_form_media_type(media_type):
"""
Return True if the media type is a valid form media type.
"""
base_media_type, params = parse_header(media_type.encode(HTTP_HEADER_ENCODING))
return (base_media_type == 'application/x-www-form-urlencoded' or
base_media_type == 'multipart/form-data')