rest-framework的APIview原始碼分析,Serializer及解析器原始碼分析
阿新 • • 發佈:2018-12-07
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)
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