天天看点

Rest Framework第二天-序列化组件(下)、解析器                                          序列化组件(下)                                                     解析器

                                          序列化组件(下)

一、rest-framework序列化之ModelSerializer

class BookSerializers(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        # fields = "__all__"
        fields=['nid','title','authors','publish']
        # exclude=('nid',)   #不能跟fields同时用
        # depth = 1    #深度控制,写 几 往里拿几层,层数越多,响应越慢,官方建议0--10之间,个人建议最多3层
    publish=serializers.SerializerMethodField()
    def get_publish(self,obj):
        return obj.publish.name
    authors=serializers.SerializerMethodField()
    def get_authors(self,obj):
        ret=obj.authors.all()
        ss=AuthorSerializer(ret,many=True)
        return ss.data
           

二、生成hypermedialink(极少数)

class BookSerializers(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        fields = "__all__"
    # 生成连接,直接查看出版社详情
    publish = serializers.HyperlinkedIdentityField(view_name='ttt', lookup_field='publish_id', lookup_url_kwarg='pkk')
    authors=serializers.SerializerMethodField()
    def get_authors(self,obj):
        ret=obj.authors.all()
        ss=AuthorSerializer(ret,many=True)
        return ss.data
#--------------

res=BookSerializers(ret,many=True,context={'request': request})

#--------------

class Publish(APIView):
    def get(self,request,pkk):
        print(pkk)
        return HttpResponse('ok')
#----路由---
url(r'^publish/(?P<pkk>\d+)$', views.Publish.as_view(),name='ttt'),
           

三、序列化组件之请求数据校验和保存功能

class BookSerializers(serializers.ModelSerializer):
    class Meta:
        model=Book
        fields="__all__"

#————————
class BookView(APIView):

    def post(self, request):

        # 添加一条数据
        print(request.data)

        bs=BookSerializers(data=request.data)
        if bs.is_valid():
            bs.save()  # 生成记录
            return Response(bs.data)
        else:

            return Response(bs.errors)
           
class BookSerializer1(serializers.Serializer):
    title=serializers.CharField(error_messages={'required': '标题不能为空'})

#这种方式要保存,必须重写create方法

           

通过源码查看检验字段的钩子函数:

#is_valid---->self.run_validation-(执行Serializer的run_validation)-->self.to_internal_value(data)---(执行Serializer的run_validation:485行)
def validate_title(self, value):
        from rest_framework import exceptions
        raise exceptions.ValidationError('看你不顺眼')
        return value

#全局
def validate(self, attrs):
    from rest_framework import exceptions
    if attrs.get('title')== attrs.get('title2'):
        return attrs
    else:
        raise exceptions.ValidationError('不想等啊')
           

 四、序列化组件源码分析

序列化组件,先调用__new__方法,如果many=True,生成ListSerializer对象,如果为False,生成Serializer对象
序列化对象.data方法--调用父类data方法---调用对象自己的to_representation(自定义的序列化类无此方法,去父类找)
Aerializer类里有to_representation方法,for循环执行attribute = field.get_attribute(instance)
再去Field类里去找get_attribute方法,self.source_attrs就是被切分的source,然后执行get_attribute方法,source_attrs
当参数传过去,判断是方法就加括号执行,是属性就把值取出来      

                                                     解析器

一、解析器的作用

根据请求头 content-type 选择对应的解析器对请求体内容进行处理。

有application/json,x-www-form-urlencoded,form-data等格式

二、全局使用解析器

settings文件:

REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES':[
        'rest_framework.parsers.JSONParser'
        'rest_framework.parsers.FormParser'
        'rest_framework.parsers.MultiPartParser'
    ]

}
           

路由:

urlpatterns = [
    url(r'test/', TestView.as_view()),
]
           

视图函数:

from rest_framework.views import APIView
from rest_framework.response import Response

class TestView(APIView):
    def post(self, request, *args, **kwargs):
        print(request.content_type)

        # 获取请求的值,并使用对应的JSONParser进行处理
        print(request.data)
        # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
        print(request.POST)
        print(request.FILES)
        return Response('POST请求,响应内容')

    def put(self, request, *args, **kwargs):
        return Response('PUT请求,响应内容')
           

三、局部使用解析器

a. 仅处理请求头content-type为application/json的请求体

# urls.py文件
from django.conf.urls import url, include
from web.views.s5_parser import TestView

urlpatterns = [
    url(r'test/', TestView.as_view(), name='test'),
]
           
# views.py文件

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import JSONParser


class TestView(APIView):
    parser_classes = [JSONParser, ]

    def post(self, request, *args, **kwargs):
        print(request.content_type)

        # 获取请求的值,并使用对应的JSONParser进行处理
        print(request.data)

        # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
        print(request.POST)
        print(request.FILES)

        return Response('POST请求,响应内容')

    def put(self, request, *args, **kwargs):
        return Response('PUT请求,响应内容')
           

b. 仅处理请求头content-type为application/x-www-form-urlencoded 的请求体

# urls.py文件

from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'test/', TestView.as_view(), name='test'),
]
           
# views.py文件

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import FormParser


class TestView(APIView):
    parser_classes = [FormParser, ]

    def post(self, request, *args, **kwargs):
        print(request.content_type)

        # 获取请求的值,并使用对应的JSONParser进行处理
        print(request.data)

        # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
        print(request.POST)
        print(request.FILES)

        return Response('POST请求,响应内容')

    def put(self, request, *args, **kwargs):
        return Response('PUT请求,响应内容')
           

c. 仅处理请求头content-type为multipart/form-data的请求体

# urls.py文件

from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'test/', TestView.as_view(), name='test'),
]
           
# views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import MultiPartParser


class TestView(APIView):
    parser_classes = [MultiPartParser, ]

    def post(self, request, *args, **kwargs):
        print(request.content_type)

        # 获取请求的值,并使用对应的JSONParser进行处理
        print(request.data)
        # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
        print(request.POST)
        print(request.FILES)
        return Response('POST请求,响应内容')

    def put(self, request, *args, **kwargs):
        return Response('PUT请求,响应内容')
           
<!DOCTYPE html>
<html >
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="http://127.0.0.1:8000/test/" method="post" enctype="multipart/form-data">
    <input type="text" name="user" />
    <input type="file" name="img">

    <input type="submit" value="提交">

</form>
</body>
</html>
           

d. 仅上传文件

# urls.py文件

from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'test/(?P<filename>[^/]+)', TestView.as_view(), name='test'),
]
           
# views.py文件

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import FileUploadParser


class TestView(APIView):
    parser_classes = [FileUploadParser, ]

    def post(self, request, filename, *args, **kwargs):
        print(filename)
        print(request.content_type)

        # 获取请求的值,并使用对应的JSONParser进行处理
        print(request.data)
        # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
        print(request.POST)
        print(request.FILES)
        return Response('POST请求,响应内容')

    def put(self, request, *args, **kwargs):
        return Response('PUT请求,响应内容')
           
<!DOCTYPE html>
<html >
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="http://127.0.0.1:8000/test/f1.numbers" method="post" enctype="multipart/form-data">
    <input type="text" name="user" />
    <input type="file" name="img">

    <input type="submit" value="提交">

</form>
</body>
</html>
           

e. 同时多个Parser

当同时使用多个parser时,rest framework会根据请求头content-type自动进行比对,并使用对应parser

# urls.py

from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'test/', TestView.as_view(), name='test'),
]
           
# views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import JSONParser, FormParser, MultiPartParser


class TestView(APIView):
    parser_classes = [JSONParser, FormParser, MultiPartParser, ]

    def post(self, request, *args, **kwargs):
        print(request.content_type)

        # 获取请求的值,并使用对应的JSONParser进行处理
        print(request.data)
        # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
        print(request.POST)
        print(request.FILES)
        return Response('POST请求,响应内容')

    def put(self, request, *args, **kwargs):
        return Response('PUT请求,响应内容')
           

四、源码分析

在调用request.data时,才进行解析,由此入手

    @property

    def data(self):

        if not _hasattr(self, '_full_data'):

            self._load_data_and_files()

        return self._full_data

查看self._load_data_and_files()方法---->self._data, self._files = self._parse()

        def _parse(self):

            #用户请求头里content_type的值

            media_type = self.content_type

            #self.parsers 就是用户配置的parser_classes = [FileUploadParser,FormParser ]

            #self里就有content_type,传入此函数

            parser = self.negotiator.select_parser(self, self.parsers)

查看self.negotiator.select_parser(self, self.parsers)

     def select_parser(self, request, parsers):

        #同过media_type和request.content_type比较,来返回解析器,然后调用解析器的解析方法

        #每个解析器都有media_type = 'multipart/form-data'属性

        for parser in parsers:

            if media_type_matches(parser.media_type, request.content_type):

                return parser

        return None

最终调用parser的解析方法来解析parsed = parser.parse(stream, media_type, self.parser_context)

Request实例化,parsers=self.get_parsers()

    Request(

                request,

                parsers=self.get_parsers(),

                authenticators=self.get_authenticators(),

                negotiator=self.get_content_negotiator(),

                parser_context=parser_context

            )

get_parsers方法,循环实例化出self.parser_classes中类对象

    def get_parsers(self):

        return [parser() for parser in self.parser_classes]            

self.parser_classes 先从类本身找,找不到去父类找即APIVIew 中的

    parser_classes = api_settings.DEFAULT_PARSER_CLASSES

api_settings是一个对象,对象里找DEFAULT_PARSER_CLASSES属性,找不到,会到getattr方法

        def __getattr__(self, attr):

            if attr not in self.defaults:

                raise AttributeError("Invalid API setting: '%s'" % attr)

            try:

                #调用self.user_settings方法,返回一个字典,字典再取attr属性

                val = self.user_settings[attr]

            except KeyError:

                # Fall back to defaults

                val = self.defaults[attr]

            # Coerce import strings into classes

            if attr in self.import_strings:

                val = perform_import(val, attr)

            # Cache the result

            self._cached_attrs.add(attr)

            setattr(self, attr, val)

            return val

user_settings方法 ,通过反射去setting配置文件里找REST_FRAMEWORK属性,找不到,返回空字典

    @property

    def user_settings(self):

        if not hasattr(self, '_user_settings'):

            self._user_settings = getattr(settings, 'REST_FRAMEWORK', {})

        return self._user_settings

继续阅读