rest-framework的APIview源码分析,Serializer及解析器源码分析

rest-framework

1.安装

方式一:pip3 install djangorestframework

方式二:pycharm图形化界面安装

方式三:pycharm命令行下安装(装在当前工程所用的解释器下)

2.djangorestframework的APIView分析(详见源码)

在视图函数中定义一个类(Book)继承了APIview,然后在url中,view.Book.as_view(),进入这个as_view(),在140行可以看到view = super(APIView, cls).as_view(**initkwargs),然后执行了父类也就是View的as_view()方法,点进去看,然后在68行有个return self.dispatch(request, *args, **kwargs),记住此时的self指的是实例化的对象,对象的方法应该从头开始找,会找到APIview里的dispatch方法,而不是直接找View里的dispatch方法。然后在478行执行了request = self.initialize_request(request, *args, **kwargs),然后继续找initialize_request方法,还是在APIview里面。然后在377行返回了一个Request对象。这里的Request类是rest_framework自己封装的类,通过这个类实例化出一个对象再一步步返回回去。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.django的rest_framework的Request对象简单介绍

详见上图及源码

4.Django自带序列化组件

from django.core import serializers
def test(request):
    book_list = Book.objects.all()    
    ret = serializers.serialize("json", book_list)
    return HttpResponse(ret)

5. rest-framework序列化之Serializer,ModelSerializer,请求数据校验和保存功能

from django.db import models


# Create your models here.
class Book(models.Model):
    title = models.CharField(max_length=32,null=True)
    price = models.IntegerField()
    pub_date = models.DateField(auto_now_add=True)
    publish = models.ForeignKey(to='Publish',null=True)
    author = models.ManyToManyField(to='Author')

    def __str__(self):
        return self.title

    def test(self):
        return '555'


class Publish(models.Model):
    name = models.CharField(max_length=32)
    email = models.EmailField()

    # def __str__(self):
    #     return self.name


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()

from django.shortcuts import render,HttpResponse,redirect
import json
from django.views import View
from django.core import serializers
from app01 import models
from django.http import JsonResponse

# Create your views here.
# def book(request):
#     if request.method == 'GET':
#         dic = {
#             'status':1,
#             'title':'金瓶梅',
#             'price':100
#         }
#         return HttpResponse(json.dumps(dic))


# class Book(View):
#     def get(self,request):
#         # dic = {
#         #     'status': 1,
#         #     'title': '金瓶梅',
#         #     'price': 100
#         # }
#         # return HttpResponse(json.dumps(dic))
#         # 方式一
#         # res = models.Book.objects.all()
#         # l = []
#         # for book in res:
#         #     l.append({'title':book.title})
#         # return HttpResponse(json.dumps(l))
#         # 方式二
#         # res = models.Book.objects.all().values('title')
#         # return HttpResponse(json.dumps(list(res)))
#         # 方式三
#         book_list = models.Book.objects.all()
#         res = serializers.serialize('json',book_list)
#         return HttpResponse(res)
#
#     def post(self,request):
#         return HttpResponse('ok')


# 用APIView
from rest_framework import serializers
from rest_framework.response import Response

'''
方法一
class AuthorSerializer(serializers.Serializer):
    name = serializers.CharField()
    age = serializers.IntegerField()


class BookSerializer(serializers.Serializer):
    title = serializers.CharField()
    # 如果想重命名,指定source
    # xx = serializers.CharField(source='title')
    price = serializers.IntegerField()
    pub_date = serializers.DateField()
    # publish = serializers.CharField()
    publish = serializers.CharField(source='publish.email')
    # author = serializers.CharField(source='author.all')
    author = serializers.SerializerMethodField()  # 指向一个字段的方法,该方法的返回值指向该字段

    # 这里的函数名必须是"get_" 加字段名,
    def get_author(self,obj):
        # 方式一:
        # print(obj)  # 这里的obj指的是每一本书这个对象
        # l = []
        # for author in obj.author.all():
        #     l.append(author)
        # return l

        # 方式二:
        res = obj.author.all()
        author_ser = AuthorSerializer(res,many=True)
        return author_ser.data
        
        
from rest_framework.views import APIView
class Book(APIView):
    # 这里的这个request指的就是rest_framework自己封装的request
    # 从源码可以找出request._request是原来django封装的那个
    def get(self,request):
        # print(request)  ===><rest_framework.request.Request object at 0x00000256C223B9E8>
        # print(request._request) ===><WSGIRequest: GET '/book/'>
        res = models.Book.objects.all()
        # ret是queryset对象,many=True代表序列化多条,many=False代表序列化一条
        book_ser = BookSerializer(res,many=True)
        print(book_ser.data)  # 是对象转成的字典
        return Response(book_ser.data)
        # return HttpResponse(json.dumps(list(res)))

    def post(self,request):
        # 无论通过urlencoded还是json格式编码传来的数据都是被rest_framework处理好了放在data里,是一个字典
        print(request.data)  # {'title': 'aaaa'}
        return HttpResponse('ok')
'''

'''
# 方法二
class AuthorSerializer(serializers.Serializer):
    name = serializers.CharField()
    age = serializers.IntegerField()


class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book  # 指定哪张表
        # 注意,如果写__all__代表全取,跟exclude不能同时用
        fields = '__all__'   # 指向那张表哪几个字段
        # fields = ('id','publish')
        # exclude=('id','publish')  # 去除哪个字段

    # 可以重写某个字段
    # id = serializers.CharField(source='test')

    title = serializers.CharField(error_messages={'required':'标题必填'})

    # author = serializers.SerializerMethodField()
    # def get_author(self,obj):
    #     res = obj.author.all()
    #     author_ser = AuthorSerializer(res,many=True)
    #     return author_ser.data


from rest_framework.views import APIView
class Book(APIView):
    # 这里的这个request指的就是rest_framework自己封装的request
    # 从源码可以找出request._request是原来django封装的那个
    def get(self,request):
        # print(request)  ===><rest_framework.request.Request object at 0x00000256C223B9E8>
        # print(request._request) ===><WSGIRequest: GET '/book/'>
        res = models.Book.objects.all()
        # ret是queryset对象,many=True代表序列化多条,many=False代表序列化一条
        book_ser = BookSerializer(res,many=True)
        # print(book_ser.data)  # 是对象转成的字典
        return Response(book_ser.data)
        # return HttpResponse(json.dumps(list(res)))

    def post(self,request):
        # 无论通过urlencoded还是json格式编码传来的数据都是被rest_framework处理好了放在data里,是一个字典
        # print(request.data)  # {'title': 'aaaa'}
        # print(request.POST)

        # Serializer的反序列化功能
        ser = BookSerializer(data=request.data)
        # 字段校验功能
        if ser.is_valid():
            # 保存到数据库
            ser.save()
            return HttpResponse('ok')
        else:
            print(ser.errors)
            return JsonResponse(ser.errors)
'''

6.局部钩子及全局钩子分析

# 局部钩子函数源码分析
#is_valid---->self.run_validation-(执行Serializer的run_validation)-->self.to_internal_value(data)-->(执行Serializer的run_validation:485行)
from rest_framework.exceptions import ValidationError
class AuthorSerializer(serializers.Serializer):
    name = serializers.CharField()
    age = serializers.IntegerField()


class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book  # 指定哪张表
        # 注意,如果写__all__代表全取,跟exclude不能同时用
        fields = '__all__'   # 指向那张表哪几个字段
    title = serializers.CharField(error_messages={'required':'标题必填'})

    # 局部钩子使用
    def validate_title(self, value):
        print(value)
        # raise ValidationError('就是要报错')
        return value

    # 全局钩子使用
    # def validate(self, attrs):
    #     print('>>>>>',attrs)  # 是个字典
    #     if attrs.get('title') == attrs.get('price'):
    #         return attrs
    #     else:
    #         raise ValidationError('书的名字和价格不一样')


from rest_framework.views import APIView
class Book(APIView):

    def get(self,request):
        res = models.Book.objects.all()
        book_ser = BookSerializer(res,many=True)
        return Response(book_ser.data)


    def post(self,request):
        ser = BookSerializer(data=request.data)
        # 字段校验功能
        if ser.is_valid():
            # 保存到数据库
            ser.save()
            return HttpResponse('ok')
        else:
            print(ser.errors)
            return JsonResponse(ser.errors)

7.解析器

# 解析器的作用
# 限制前端向后端传的数据的格式
from rest_framework.parsers import JSONParser,FormParser,FileUploadParser
# JSONParser ==>解析json格式
# FormParser ==>解析urlencoded格式
class Book(APIView):
    # 局部使用解析器
    parser_classes = [JSONParser,FormParser]
    def get(self,request):
        res = models.Book.objects.all()
        book_ser = BookSerializer(res,many=True)
        return Response(book_ser.data)

    def post(self,request):
        print(request.POST)
        print(request.data)
        return HttpResponse('OK')
settings.py配置文件
# 全局配置解析器
REST_FRAMEWORK ={
    'DEFAULT_PARSER_CLASSES':['rest_framework.parsers.JSONParser',
                              'rest_framework.parsers.FormParser''rest_framework.parsersFileUploadParser']

}
7.1 解释器源码分析
在调用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

8.补充

   __getattr__ 拦截点号运算。当对未定义的属性名称和实例进行点号运算时,就会用属性名作为字符串调用这个方法。如果继承树可以找到该属性,则不调用此方法。
   
    __setattr__会拦截所有属性的的赋值语句。如果定义了这个方法,self.arrt = value 就会变成self,__setattr__("attr", value).这个需要注意。当在__setattr__方法内对属性进行赋值是,不可使用self.attr = value,因为他会再次调用self,__setattr__("attr", value),则会形成无穷递归循环,最后导致堆栈溢出异常。应该通过对属性字典做索引运算来赋值任何实例属性,也就是使用self.__dict__['name'] = value

猜你喜欢

转载自blog.csdn.net/qq_42721964/article/details/84857128