1. 程式人生 > >rest-framework的APIview原始碼分析,Serializer及解析器原始碼分析

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