APIView执行流程、Request对象源码分析、序列化器介绍和快速使用、反序列化、反序列化的校验、练习

APIView执行流程、Request对象源码分析、序列化器介绍和快速使用、反序列化、反序列化的校验、练习

一、APIView执行流程

1.1基于APIView+JsonResponse编写接口

# def提供了一个APIView类,方便我们使用drf写视图类,我们使用drf写视图类,都是继承这个类及其子类,APIView自身是继承了Django原生的view类

1.1.1 models部分

from django.db import models


# Create your models here.
class Book(models.Model):
    name = models.CharField(max_length=32, verbose_name='书籍名')
    price = models.CharField(max_length=32, verbose_name='价格')
    publish = models.CharField(max_length=32, verbose_name='出版社')

1.1.2 urls部分

from django.contrib import admin
from django.urls import path
from books import views
urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/', views.Books.as_view()),
]

1.1.3views部分

from django.http import JsonResponse
from django.shortcuts import render
from rest_framework.views import APIView
from .models import Book


# Create your views here.


class Books(APIView):
    def get(self, request):
        books = Book.objects.all()
        book_list = []
        for book in books:
            book_list.append({'name': book.name,
                              'price': book.price,
                              'publish': book.publish
                              })
        return JsonResponse(book_list, safe=False)

1.2 基于APIView+Response写接口

class BookView(APIView):
    def get(self, request):
        books = Book.objects.all()
        book_list = []
        for book in books:
            book_list.append({'name': book.name,
                              'price': book.price,
                              'publish': book.publish
                              })
        return Response(book_list)
# 使用该方法时,遇到问题,Response在浏览器上使用,报错,postman没问题,原因是因为rest_framework是个应用,需要注册下app

image-20230201164702422

1.3 APIView的执行流程

# urls中的path('books/', views.BookView.as_view()),请求来了之后,执行views.BookView.as_view()()--->现在as_view是APIView的as_view

# APIView的as_view方法:
	# view还是view,但是以后没有csrf认证了
    @classmethod
   	def as_view(cls, **initkwargs):
        # 调用父类的as_view,父类是django原生的View
        # 把djagno原生View的as_view方法中的闭包函数view拿出来了
        view = super().as_view(**initkwargs)
        # csrf_exempt 排除所有csrf的认证
        # 相当于在所有的方法上面加了这个装饰器
        return csrf_exempt(view)
    
# 路由匹配成功,执行 csrf_exempt(view)(requets)--->View的as_view中的闭包函数view---》self.dispatch---->self是视图类的对象---》BookiView---》APIView的dispatch,找到了
    def dispatch(self, request, *args, **kwargs):
        # request是django原生的request,老的request
        # 把老的request包装成了新的request,这个是drf提供的Request类的对象
        request = self.initialize_request(request, *args, **kwargs)
        # 到此以后,这个request就是新的了,老的request在哪?
        # request._request 这是老的
        
        # 把新的request放到了self对象【BookView的对象】
        self.request = request
        try:
            # 执行了三大认证【认证,频率,权限】,使用新的request,不读
            self.initial(request, *args, **kwargs)
            
            # 跟之前一毛一样
            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也是新的
            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.3.1 APIView的执行流程总结

1、去除了所有的csrf认证
2、包装了新的request,以后在视图类中用的request是新产生的request,Request类的对象,不再是原生的,原生的是:request._request
3、在执行视图类的方法之前,执行了三大认证(认证、校验、频率)
4、如果在三大认证或者视图函数方法执行过程中出现了错误,会有全局捕获--->全局异常捕获
5、以后视图类方法中的request就都是新的request

1.3.2 补充:装饰器的基本原理

def auth()  # 装饰器

def add()   # 函数

# 使用auth装饰add函数
@auth   #  本质是  add=auth(add)
def add()

# 以后再使用add,其实就是在使用 auth(add)  的返回结果

二、 Request对象源码分析

2.1 新的Request和老的Request的区别:

新的Request和老的Request的区别:
    老的Request来自:django.core.handlers.wsgi.WSGIRequest
    老的是:request._request
    新的Request来自:from rest_framework.request import Request
 
# Request源码
	方法:__geyattr__
    	在视图类的方法中,执行request.method,新的request是没有method的,会触发新的Request的__getattr__方法执行
        def __getattr__(self, attr):
            try:
        		 # 从老的request中反射出 要取得属性
                return getattr(self._request, attr)
            except AttributeError:
                return self.__getattribute__(attr)

2.2 Request类总结

	request.data--->这个方法,封装了数据属性,有了它之后,无论是post、put或是其他的放在请求体body中提交的数据,都可以从request.data中获取,获取到的数据就是字典,无论哪种编码格式
    
    request.query_params--->这个方法,封装了数据的属性,get请求携带的参数可以从这里获取
    query_params:查询参数--->restful规范请求地址中带有查询参数
    
    request.FILES--->这个方法,封装了数据属性,前端提交过来的文件,可以从这里获取
    
    总结:
    	1、新的request用起来,和老的一样,因为新的取不到,或获取到__getattr__
        2、request.data  无论什么编码,什么请求方式,只要是body中的数据,就从这里取,获取到的就是字典
        3、request.query_params 就是之前的request._request.GET
        4、上传的文件可以从request.FILES中获取

2.3 python中的魔法方法

__str__ :打印对象会调用
__init__:	 类()会调用
__call__:	 对象()会调用
__new__:	 在内存中为对象分配空间 返回对象的引用
__getattr__: 对象.属性,如果属性不存在,会触发它的执行

三、序列化器介绍和快速使用

3.1 序列化器简介

	# 我们在写接口的时候, 需要序列化、反序列化,而且反序列化的过程中需要做数据的校验--->drf封装了固定的写法,只要按照固定的写法使用,就可以完成响应的需求

	# 提供了 Serializer和ModelSerializer
    
    # 我们只需要写自己的类,继承drf的序列化类,使用其中的某些方法,即可完成需求
    
   	# 使用APIView+序列化类+Response 完成接口的编写

3.2 序列化类的基本使用,序列化单条

3.2.1 serializer.py--BookSerializer类(序列化类)

# coding:utf-8
from rest_framework import serializers


class BookSerializer(serializers.Serializer):
    # 序列化某些字段,这里写需要序列化的字典
    # serializer下大致跟models下的类是对应的
    name = serializers.CharField()
    price = serializers.CharField()
    publish = serializers.CharField()
    

3.2.2 views.py-->BookView类 (视图类)

from .serializer import BookSerializer


class BookView(APIView):
    def get(self, request):
        # 获取book数据
        books =Book.objects.all()
        # 使用序列化类来完成---> 需要有个序列化类
        # instance要序列化的数据books获取的queryset对象
        # many = Ture 只要是queryset对象需要传many = True
        # 单个对象不需要传
        ser = BookSerializer(instance=books, many=True)
        # Response()无论是列表还是字典都可以直接序列化
        return Response(ser.data)

3.3 序列化单条

3.3.1 serializer.py--BookSerializer类(序列化类)

# 和多条一样,没有改变
# coding:utf-8
from rest_framework import serializers


class BookSerializer(serializers.Serializer):
    # 序列化某些字段,这里写需要序列化的字典
    # serializer下大致跟models下的类是对应的
    name = serializers.CharField()
    price = serializers.CharField()
    publish = serializers.CharField()
    

3.3.2 views.py-->BookDetailView类 (视图类)

class BookDetailView(APIView):
    # def get(self, request, pk):
    # 也可以使用可变长形参
    def get(self, request, *args, **kwargs):
        book = Book.objects.filter(pk=kwargs.get('pk')).first()
        # 序列化
        ser = BookSerializer(instance=book,)
        return Response(ser.data)

3.3.3 url (路由)

urlpatterns = [
    path('books/<int:pk>', views.BookDetailView.as_view()),
]

四、反序列化

4.1 反序列化的新增

4.1.1 serializer.py--BookSerializer类(序列化类)

# coding:utf-8
from rest_framework import serializers

from books.models import Book


class BookSerializer(serializers.Serializer):
    # 序列化某些字段,这里写需要序列化的字典
    # serializer下大致跟models下的类是对应的
    name = serializers.CharField()
    price = serializers.CharField()
    publish = serializers.CharField()

    # 编写create方法,保存的逻辑
    def create(self, validated_data):
        # validated_data 校验过后的数据{name, price, publish}
        # 保存到数据库中
        book = Book.objects.create(**validated_data)
        return book

4.1.2 views.py-->BookView类 (视图类)

class BookView(APIView):
    def post(self, request):
        # request.data 存储着前端提交的要保存的数据--->校验数据
        # 把前端传入的要保存的数据给data参数
        ser = BookSerializer(data=request.data)
        # 校验数据
        if ser.is_valid():
            # 保存数据,需要在序列化类BookSerializer中写
            # 调用ser.save()方法,直接触发我们编写的create方法,保存起来
            ser.save()
            return Response({'code': 100, 
                             'msg': '新增成功',
                             'result': ser.data
                             })
        else:
            return Response({'code': 101,
                             'msg': ser.errors,
                             })

4.2 反序列化的修改

4.2.1 serializer.py--BookSerializer类(序列化类)

class BookSerializer(serializers.Serializer):
    # 序列化某些字段,这里写需要序列化的字典
    # serializer下大致跟models下的类是对应的
    name = serializers.CharField()
    price = serializers.CharField()
    publish = serializers.CharField()
	def update(self, instance, validated_data):
        # instance 需要修改的对象,validated_data校验过后前端传来的数据
        instance.name = validated_data.get('name')
        instance.price = validated_data.get('price')
        instance.publish = validated_data.get('publish')
        # orm的单个对象,修改了单个对象的属性,只要调用对象.save,就能把修改保存到数据库
        instance.save()
        return instance

4.2.2 views.py-->BookDetailView类 (视图类)

class BookDetailView(APIView):    
    def put(self, request, pk):
        book = Book.objects.filter(pk=pk).first()
        # 反序列化保存  ---借助于序列化类
        ser = BookSerializer(data=request.data, instance=book)
        if ser.is_valid():
            ser.save()
            return Response({'code': 100,
                             'msg': '修改成功',
                             'result': ser.data
                             })
        else:
            return Response({'code': 101,
                             'msg': ser.errors
                             })

4.3 删除单条

# views.py-->BookDetailView类  (视图类)
class BookDetailView(APIView):      
    def delete(self, request, pk):
        Book.objects.filter(pk=pk).delete()
        return Response({'code': 100,
                         'msg': '删除成功'
                         })

五、反序列化的校验

# 序列化类反序列化,数据校验功能--->类比forms组件
	# 局部钩子
        def validate_name(self, name):
        # 校验name是否合法
        import re
        # 第一个是表达式,第二个是需要校验数据
        res = re.findall('^[0-9].*$', name)
        if res:
            # 校验name是否合法
                # 校验不通过,抛异常
            raise ValidationError('不能以数字开头')
        else:
            return name
    # 全局钩子
        def validate(self, attrs):
            # 校验之后的数据,书名和出版社不能一致
            if attrs.get('name') == attrs.get('publish'):
                raise ValidationError('书名跟出版社名字不能一致')
            else:
                return attrs

六、练习

	原生的request  没有data 属性  实现一个原生的 request.data 拿出无论什么编码格式提交的数

6.1 models

from django.db import models

# Create your models here.


class Publish(models.Model):
    publish_name = models.CharField(max_length=32, verbose_name='出版社')

6.2 views

from django.shortcuts import render, HttpResponse
from django.views import View
from .models import Publish
import json
from urllib.parse import unquote


def outer(func):
    def inner(request, *args, **kwargs):
        try:
            request.data = json.loads(request.body)
        except:
            request.data = request.POST
            if request.method == 'PUT':
                res = request.body.decode(encoding='utf-8').split('&')
                request.data = {}
                for res1 in res:
                    res2 = res1.split('=')
                    request.data[res2[0]] = unquote(res2[1])

        res = func(request, *args, **kwargs)
        return res
    return inner


@outer
def publish_view(request):
    print(request.data)
    return HttpResponse('www.baidu.com')

6.3 views

from django.contrib import admin
from django.urls import path
from viewWorker import views


urlpatterns = [
    path('publish/', views.publish_view)
]
posted @ 2023-02-02 08:53  负剑远游行归来仍少年  阅读(22)  评论(0编辑  收藏  举报