ApiView/Request類原始碼分析/序列化器

2023-02-01 21:00:49

內容概要

  • ApiView+JsonResponse編寫介面
  • ApiView+Response編寫介面
  • ApiView原始碼解析
  • Request物件原始碼分析
  • 序列化器介紹和快速使用/反序列化
  • 反序列化的校驗

ApiView+JsonResponse編寫介面

我們還是在models模型層中建立一個book類
裡面包含 name/price欄位
我們就使用django自帶的sqlite資料庫即可
'modles.py'
from django.db import models
class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.CharField(max_length=32)
'views'
from django.shortcuts import render
from rest_framework.views import APIView # 匯入APiView
from django.http import JsonResponse
from app01 import models
class BookView(APIView): # 繼承APIView類
    def get(self, request):
        book_queryset = models.Book.objects.all()
        book_list = []
        for book_obj in book_queryset:           book_list.append({'name':book_obj.name,'price':book_obj.price})
        return JsonResponse(book_list,safe=False)
		# 因為在返回列表格式資料的時候會出現問題,需要將safe=False
'urls'
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
    path('admin/', admin.site.urls),
    path('book/api/v1/',views.BookView.as_view()),
]

ApiView+Response編寫介面

與JsonResponse基本一致,只是在返回資料時,不需要進行safe=False的操作
    def get(self,request):
        book_queryset = models.Book.objects.all()
        book_list = []
        for book_obj in book_queryset:
            book_list.append({'name':book_obj.name,'price':book_obj.price})
        return Response(book_list)

ApiView原始碼解析

我們接下來分析以下APIView和View到底有什麼區別?
首先是APIView的執行流程:
		路由層:path('book/api/v1/',views.BookView.as_view()),
		請求來了,執行views.BookView.as_view()()
		'as_view()是APIView中的方法,並不是View的了!'
		
		# 呼叫父類別的as_view()方法,APIView繼承的是View
		view = super().as_view(**initkwargs)
		# 拿到父類別中的閉包函數view
		as_view() 方法 返回了 >>>> return csrf_exempt(view)
		
		# csrf_exempt 排除所有csrf的認證
		# 相當於在所有的方法上面加了這個裝飾器
		
		路由匹配成功,執行 csrf_exempt(view)(requets)
		相當於回到了View類中,執行閉包函數view,返回的是
		return self.dispatch(request, *args, **kwargs)
		
		這個self就是我們檢視函數中編寫的BookView類產生的物件
		所以執行物件.dispatch,因為這個BookView繼承了APIView
		正好在APIView中也有dispatch方法
		
		def dispatch(self, request, *args, **kwargs):
		# request是django原生的request,老的request

		request = self.initialize_request(request, *args, **kwargs)
		self.request = request
		# 把老的request包裝成了新的request,這個是drf提供的Request類的物件
		try:
			# 執行了三大認證(認證,頻率,許可權)使用新的request
			self.initial(request, *args, **kwargs)
			接下來跟CBV原始碼都一樣了
			if request.method.lower() in self.http_method_names:
				handler = getattr(self, request.method.lower(),
								  self.http_method_not_allowed)
			else:
				handler = self.http_method_not_allowed
				# 將新的request傳入,檢視類中的get的request也是新的request
				response = handler(request, *args, **kwargs)
			except Exception as exc:
				# 在執行3大認證和檢視類中方法的過程中,如果出了異常,都能捕獲到---》全域性異常捕獲
				response = self.handle_exception(exc)
				self.response = self.finalize_response(request, response, *args, **kwargs)
				return self.response

# 總結
	1 去除了所有的csrf
    2 包裝了新的request,以後在檢視類中用的request是新的request  Request類的物件,不是原生的了
    	-原生的在:新的requets._request
    3 在執行檢視類的方法之前,執行了3大認證
    4 如果在3大認證或檢視函數方法執行過程中出了錯,會有異常捕獲>>>全域性異常捕獲
    5 以後檢視類方法中的request都是新的了

Request原始碼分析

在Request類中有__getattr__魔法方法
如果我們使用新的request.method方法就會觸發Request中的__getattr__方法
    def __getattr__(self, attr):
        try:
            return getattr(self._request, attr)  # 會從老的request中獲取method方法
        except AttributeError:
            return self.__getattribute__(attr)

		-request.data--->這是個方法,包裝成了資料屬性
		-以後無論post,put 在body中提交的資料,都從request.data中取,取出來就是字典
		-無論是那種編碼格式

		-request.query_params--->這是個方法,包裝成了資料屬性
		-get請求攜帶的引數,以後從這裡面取
		-query_params:查詢引數--->restful規範請求地址中帶查詢引數

		-request.FILES--->這是個方法,包裝成了資料屬性
		-前端提交過來的檔案,從這裡取

它們都攜帶了@property裝飾器,將方法偽裝成了屬性

 # Request類總結
	-1  新的request用起來,跟之前一模一樣,因為新的取不到,會取老的__getattr__
    -2 request.data  無論什麼編碼,什麼請求方式,只要是body中的資料,就從這裡取(字典格式)
    -3 request.query_params 就是原來的request._request.GET
    -4 上傳的檔案從request.FILES中取

序列化器介紹和快速使用

	我們在編寫介面時,經常需要使用到序列化和反序列化
	並且在反序列化過程中需要進行資料校驗
	drf中直接提供了固定的寫法,我們只需要按照固定寫法編寫即可完成上述需求
drf提供了兩個類 Serializer/ModelSerializer
	以後我們只需要編寫自己的類,繼承drf提供的序列化類
	使用其中的某些方法即可

序列化基本使用>>>序列化多條資料

**serializer.py >>> BookSerializer類

from rest_framework import serializers
class BookSerializer(serializers.Serializer):
    name = serializers.CharField() # 只需要填寫需要序列化的欄位即可
    price = serializers.CharField()

views檢視 >>> BookView類

    def get(self,request):
        book_queryset = models.Book.objects.all()
        serializer_obj = serializer.BookSerializer(instance=book_queryset,many=True)
		# instance 引數是指定需要序列化的物件,
		# many 引數是如果有多個物件就需要將many引數改為True,預設為False
        return Response(serializer_obj.data)

urls路由層 >>> urlpatterns

	from django.contrib import admin
	from django.urls import path
	from app01 import views
	urlpatterns = [
		path('admin/', admin.site.urls),
		path('book/api/v1/',views.BookView.as_view()),
	]

序列化基本使用>>> 反序列化單條資料(新增)

**serializer.py >>> BookSerializer類

from rest_framework import serializers
class BookSerializer(serializers.Serializer):
    name = serializers.CharField() # 只需要填寫需要序列化的欄位即可
    price = serializers.CharField()
	
# 新增資料需要我們自己編寫create方法來進行儲存
    def create(self, validated_data):
        # validated_data >> 校驗完成的資料
        book_obj = models.Book.objects.create(**validated_data)
        return book_obj

views檢視 >>> BookView類

    def post(self,request):
        # 因為是新增資料我們需要將前端傳遞來的資料反序列化
        serializer_obj = serializer.BookSerializer(data=request.data)
        if serializer_obj.is_valid(): # 校驗資料是否合法
            # 合法的資料通過ser_obj點save方法自動呼叫序列化檔案中的create方法
            serializer_obj.save()
            return Response({"code":100,"msg":'新增成功','result':serializer_obj.data})
        else:
            return Response({"code":101,'msg':serializer_obj.errors})

urls路由層 >>> urlpatterns

	from django.contrib import admin
	from django.urls import path
	from app01 import views
	urlpatterns = [
		path('admin/', admin.site.urls),
		path('book/api/v1/',views.BookView.as_view()),
	]

序列化基本使用>>> 序列化單條資料(查詢)

**serializer.py >>> BookSerializer類

from rest_framework import serializers
class BookSerializer(serializers.Serializer):
    name = serializers.CharField() # 只需要填寫需要序列化的欄位即可
    price = serializers.CharField()

views檢視 >>> BookDetailView類

class BookDetailView(APIView):
    def get(self, request, pk):
        book_obj = models.Book.objects.filter(pk=pk).first()
        serializer_obj = serializer.BookSerializer(instance=book_obj)
        return Response(serializer_obj.data)

urls路由層 >>> urlpatterns

from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
    path('admin/', admin.site.urls),
    path('book/api/v1/',views.BookView.as_view()),
    path('book/api/v1/<int:pk>/',views.BookDetailView.as_view()),
]

序列化基本使用>>> 反序列化資料(修改單條資料)

**serializer.py >>> BookSerializer類

class BookSerializer(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.CharField()

    def create(self, validated_data):
        # validated_data >> 校驗完成的資料
        # name = validated_data.get('name')
        # price = validated_data.get('price')
        book_obj = models.Book.objects.create(**validated_data)
        return book_obj

    def update(self, instance, validated_data):
        # instance就是需要序列化的資料物件
        instance.name = validated_data.get('name')
        instance.price = validated_data.get('price')
        instance.save()
        return instance # 返回序列化資料物件

views檢視 >>> BookDetailView類

    def put(self,request,pk):
        book_obj = models.Book.objects.filter(pk=pk).first()
        serializer_obj = serializer.BookSerializer(instance=book_obj,data=request.data)
        if serializer_obj.is_valid():
            serializer_obj.save()  # 會自動呼叫update方法進行修改操作
            return Response({"code": 103, "msg": '修改成功', 'result': serializer_obj.data})
        else:
            return Response({"code": 104, 'msg': serializer_obj.errors})

urls路由層 >>> urlpatterns

from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
    path('admin/', admin.site.urls),
    path('book/api/v1/',views.BookView.as_view()),
    path('book/api/v1/<int:pk>/',views.BookDetailView.as_view()),
]

刪除單條資料 (不需要序列化/反序列化)

views檢視 >>> BookDetailView類

class BookDetailView(APIView):

    def delete(self, requset, pk):
        Book.objects.filter(pk=pk).delete()
        return Response({'code': 100, 'msg': '刪除成功'})

反序列化資料校驗

在我們的serializer.py檔案中編寫反序列化的校驗
    # 全域性勾點 # 校驗過後的資料,價格不能超過90
    def validate(self, attrs):
        if int(attrs.get('price')) >= 90:
            raise ValidationError('書價格不能超過90')
        else:
            return attrs

    # 區域性勾點 校驗名字不能以1開頭
    def validate_name(self, name):
        if name.startswith('1'):
            raise ValidationError('不能以1開頭')
        else:
            return name
如果滿足上述函數報錯條件,serializer_obj.is_valid() 不成立!

練習

原生的request  沒有data 屬性  實現一個原生的 request.data
拿出無論什麼編碼格式提交的資料(編寫FBV即可)
views.py
def method(func):
    def inner(request,*args,**kwargs):
        try:
            # 如果報錯說明資料不是Json格式
            request.data = request.body
        except:
            request.data = request.POST
        res = func(request)
        return res
    return inner


@method
def login(request):
    print(request.data)
    print(request.content_type)
    return render(request,'login.html',locals())