drf APIView执行流程,Request对象源码分析,序列化器介绍与使用

昨日内容回顾

restful 规范

1接口使用https协议传输数据保证数据的安全性

2接口带api标识

3接口带版本信息

4接口地址即资源,尽量使用名字,或复数,特殊情况可以使用动词

5接口使用请求方式操作资源

6返回数据响应状态码:http响应状态码,自定义响应状态码

7返回数据应该改有错误信息{code:100,msg:'成功'}

8返回数据规范

查所有返回数组

查单个返回对象

新增返回新增对象

修改返回修改对象

删除返回空文档

9返回接口带链接

10接口带过滤添加(查询条件)

序列化反序列化

核心就是数据的格式转换

前后端分离:

​ 序列化:查出的模型数据--json数据字符串--给前端 read

​ 反序列化:前端传入json格式字符串--转换成python字典--模型对象--存入数据库 write

使用django原生编写5个接口

前后端分离,写的都是接口,接口基本上都是基于这5种的变形

查询所有 :Jsonresponse

新增:request.POST取出前端传来body的数据,只能取出form-data urlencode编码格式的数据 不能取出json格式的数据 需要先反序列化字典在使用

查单个:根据pk查询到单个对象,直接序列化对象返回给前端

修改单个:根据pk查到对象,put请求数据放到body中,request.POST取不到数据,只能自己手动取,然后转为字典对象

删除:pk直接删除

drf介绍

django的一个app组件,最新版为3.x支持django从3.x 到最新

框架,模板不一致可能会导致运行不起来

方便我们快速的在django框架上编写符合restful规范的接口

快速使用drf写5个接口

路由:自动生成路由

视图:

系列化类:serializer

CBV源码分析

路由中:path('api/v1/books/', views.BookView.as_view())--》匹配成功执行--》view.BookView.as_view()(request)--》由于当前类没有,找父类有,执行父类的as_view方法,返回闭包函数的view内存地址然后再加括号调用--》return sefl.dispatch(request,*args, **kwargs)-->这个self就是BookView产生的对象-->发现BookView没有dispatch方法--》View类中有

def dispatch(self, request, *args, **kwargs):
    if request.method.lower() in self.http_method_names:
        # self是BookView的对象,通过字符串get找方法或属性,找到了get方法
        #handler就是BookView类的get方法
        handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed
            # 执行 BookView的get(request)---->get请求就会执行BookView的get方法,post请求就会执行BookView的post方法
            return handler(request, *args, **kwargs)

使用pycharm打开sqlite的问题

sqlite文件--》文件(xx.mp4,xx.txt)--->是一样的都是存储的二进制

我们选择什么软件打开,看到的东西是不一样的

解决:

干脆不用pycharm打开使用nvaicate打开

使用pycharm正常连接

今日内容概要

drf整体内容

APIView执行流程(难,了解)

Request对象原阿门分析(难,了解)

序列化器介绍和快速使用

反序列化

基于序列化器编写的5个接口(重要)

反序列化的校验

今日内容详细

drf整体内容

1 入门规范

-web开发模式
-api接口
-接口测试工具
-restful规范
-序列化反序列化
-drf快速使用
-APIView,Request类
-drf把django的好多东西都重写了

2 序列化组件(重要)

-基本序列化类
-模型序列化类
-如何序列化
-如何反序列化
-反序列化的数据校验功能

3 请求与相应

drf的request已经不是原来的django的request

drf的reponse已经不是原来的django的reponse

指定可以接收请求的编码方式

指定响应格式

4 视图组件(重点)

两个视图基类

5个视图扩展类

9个视图子类

视图集

5 路由

自动生成路由

6 认证(登录认证),权限,频率(重点)

7 过滤,排序,分页(次重点)

8 自动生成接口文档


9 JWT登录认证的,区别于cookie和session,主流

10 后台管理美化(django原生的很丑,simpleui二次开发)

11 基于角色的访问控制,权限的控制

APIView执行流程(难,了解)

基于APIView+JsonResponse编写接口

# 原来基于django原生的View编写接口
# drf提供给咱们的一个类,以后使用drf写视图类,都是继承这个类及其子类,APIView本身就是继承了Django原生的View

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 JsonResponse(book_list, safe=False)

基于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)  # 无论是列表还是字典都可以序列化

APIView的执行流程

# 路由中写的: 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

总结:APIView的执行流程

  1. 去除了所有的csrf认证
    image

  2. 包装了django原生的request成了新的request,以后再视图类中用的request是新的request Request类的对象,不是原生的了
    原生的request再request._request
    image

    image

  3. 在执行视图类之前,执行了3大认证
    image

    image

  4. 如果再3大认证或视图函数方法执行过程出错,会有异常捕获—》全局异常捕获
    image

  5. 以后视图类中的request都是新的了
    image

补充:装饰器的基本原理

def auth(func)
	def inner(*args,**kwargs)
    	res = func(*args,**kwargs)
    	return res
    return inner

@auth   #本质是 add = auth(add)
def add():
    pass
以后使用add(),其实就是在使用 auth(add)的返回的结果

Request对象源码分析(难,了解)

新的Request—》区别于老的

老的:django.core.handlers.wsgi.WSGIRequest

新的:from rest_framework.request import Request

新的 request._request 是老的request

Request源码

_getattr_

在视图类的方法中,执行request.method,新的request是没有method的,就触发了新的request的__getattr__方法的执行

def __getattr__(self, attr):
    try:
        # 从老的request中反射出 要取得属性
        return getattr(self._request, attr)
    except AttributeError:
        return self.__getattribute__(attr)
request.data—>这个方法,包装成了数据属性

以后无论post,put….放在body中提交的数据,都从request.data中取,取出来就是字典

无论哪种编码格式都从这里面取

@property
def data(self):
    if not _hasattr(self, '_full_data'):
        self._load_data_and_files()
        return self._full_data
requset.query_parms—》这个方法,包装成了数据属性

地址栏中携带的参数,以后从这里面取

也就是?xxx=xxx&yyy=yyy这中类型的

@property
def query_params(self):
    """
        More semantically correct name for request.GET.
        """
    return self._request.GET
request.FILES—>这是个方法,包装成了数据属性

前端提交过来的文件,从这里面取

@property
def FILES(self):
    # Leave this one alone for backwards compat with Django's request.FILES
    # Different from the other two cases, which are not valid property
    # names on the WSGIRequest class.
    if not _hasattr(self, '_files'):
        self._load_data_and_files()
        return self._files

Request类总结

  1. 新的request用去来,跟之前一模一样,因为新的取不到,会取老的_getattr_
  2. request.data 无论什么编码,什么请求方式,只要是body中的数据,就从这里面取,取出的是字典类型
  3. request.query_parms就是原来的request._request.GET
  4. 上传的文件从request.FILES

python中有很多的魔法方法,在类中,某种情况下会触发,会自动执行

_str_:打印对象会调用

_init_:类()会调用

_call_:对象()会调用

_new_:

_getattr_:对象.属性,如果属性不存在,会触发它的执行

序列化器介绍和快速使用

因为在写接口是需要序列化,需要反序列化,而反序列化的过程中要做数据校验—》drf直接提供了固定的写法,只要按照固定写法使用,就能完成上面的三个需求

提供了两个类 Serializer ModelSerializer

以后咱们只需要写自己的类,继承drf提供的序列化类,使用其中的某些方法,就能完成上面的操作

序列化类基本使用,序列化多条

serializer.py–>BookSerializer类
from django.db.models.query_utils import DeferredAttribute
from rest_framework import serializers
from .models import Book
from collections import OrderedDict



class BookSerializer(serializers.Serializer):
    #自己一个一个写
    # name = serializers.CharField()
    # price = serializers.CharField()
    # publish = serializers.CharField()
    # pass
    def create(self, validated_data):
        books = Book.objects.create(**validated_data)
        return books

    def update(self, instance, validated_data):
        for key in validated_data:
            setattr(instance, key, validated_data.get(key))
        instance.save()
        return validated_data

#序列化除id的其他字段
#自动写
fields = [field for field, obj in Book.__dict__.items() if isinstance(obj, DeferredAttribute)]
fields.remove('id')
# print(fields)


fields_list = []
for name in fields:
    fields_list.append((name, serializers.CharField()))
setattr(BookSerializer, '_declared_fields', OrderedDict(fields_list))
views.py–>BookView类
class BookView(APIView):
    def get(self, request):
        """查所有"""
        # 只是为了验证之前讲过的
        print(request.method)
        print(request._request)
        print(type(self.request))
        
        books = Book.objects.all()
        # 使用序列化类来完成---》得有个序列化类
        # instance要序列化的数据books queryset对象
        # many=True 只要是queryset对象要传 many=True 如果是单个对象就不用传默认为False
        ser = BookSerializer(instance=books, many=True)
        return Response(ser.data) # 无论是列表还是字典都可以序列化

序列化单条

from rest_framework import serializers

class BookSerializer(serializers.Serializer):
    # 序列化某些字段,这里写要序列化的字典
    name = serializers.CharField()  # serializers下大致跟models下的类是对应的
    # price = serializers.CharField()
    publish = serializers.CharField()
views.py–>BookViewBody
class BookViewBody(APIView):
    def get(self, request, *args, **kwargs):
        """查询单个信息"""
        book = Book.objects.filter(pk=kwargs.get('pk')).first()
        ser = BookSerializer(instance=book)
        return Response(ser.data)
urls.py—>添加新的路由
urlpatterns = [
    path('books/<int:pk>/', views.BookViewBody.as_view()),
]

反序列化

反序列化的新增

序列化类
class BookSerializer(serializers.Serializer):
    #自己一个一个写
    name = serializers.CharField()
    price = serializers.CharField()
    publish = serializers.CharField()

    def create(self, validated_data):
        # 保存的逻辑
        # validated_data 校验过后的数据 {name,price,publish}
        books = Book.objects.create(**validated_data)
        # 一定要返回新增的对象
        return books
veiws.py
class BookView(APIView):
    def post(self, request):
        # requset.data  # 前端提交的要保存的数据----》校验数据---》存
        ser = BookSerializer(data=request.data)  # 把前端传入的要保存的数据,给data参数
        # 校验数据
        if ser.is_valid():
            # 保存---->需要自己写,要在序列化类BookSerializer中写----》create方法
            ser.save()  # 调用ser.save,自动触发咱们写的create,保存起来
            return Response({'code': 100, 'msg': '新增成功', 'result': ser.data})
        else:
            return Response({'code': 101, 'msg': ser.errors})

反序列化的修改

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

    def create(self, validated_data):
        # 保存的逻辑
        # validated_data 校验过后的数据 {name,price,publish}
        # 保存到数据库
        book = Book.objects.create(**validated_data)
        # 一定不要返回新增的对象
        return book

    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')
        instance.save()  # orm的单个对象,修改了单个对象的属性,只要调用对象.save,就能把修改保存到数据库

        return instance  # 不要忘了把修改后的对象,返回
    #也可以使用下main这种
    def update(self, instance, validated_data):
        for key in validated_data:
            setattr(instance, key, validated_data.get(key))
            instance.save()
        return validated_data
views.py
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()  # 由于没有重写update,所以这报错
            return Response({'code': 100, 'msg': '修改成功', 'result': ser.data})
        else:
            return Response({'code': 101, 'msg': ser.errors})

删除单条

views.py
def delete(self, request, pk):
    """删除单条"""
    Book.objects.filter(pk=pk).delete()
    return Response()

基于序列化器编写的5个接口(重要)

反序列化的校验

序列化与反序列化,数据校验功能—》类比form组件

局部钩子

全局钩子

from django.db.models.query_utils import DeferredAttribute
from rest_framework import serializers
from .models import Book
from collections import OrderedDict
from rest_framework.exceptions import ValidationError


class BookSerializer(serializers.Serializer):
    #自己一个一个写
    # name = serializers.CharField()
    # price = serializers.CharField()
    # publish = serializers.CharField()
    # pass
    def create(self, validated_data):
        books = Book.objects.create(**validated_data)
        return books

    def update(self, instance, validated_data):
        for key in validated_data:
            setattr(instance, key, validated_data.get(key))
        instance.save()
        return validated_data

    def validate_name(self,name):
        """局部钩子"""
        if name.startswith('sb'):
            raise ValidationError('不能以namesb开头')
        return name

    def validate(self, attrs):
        #全局钩子
        if attrs.get('name')==attrs.get('publish'):
            raise ValidationError('书名与出版社名不能一样')
        return attrs

作业

APIView和Request源码分析,自己分析一遍

基于序列化器编写5个接口 Publish

使用局部钩子和全局钩子校验 5个接口

view.py

from django.shortcuts import render

# Create your views here.
from rest_framework.views import APIView
from .models import Book
from .serializer import BookSerializer
from rest_framework.response import Response


class BookView(APIView):
    def get(self, request):
        books = Book.objects.all()
        ser = BookSerializer(instance=books, many=True)
        return Response(ser.data)

    def post(self, request):
        ser = BookSerializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        return Response(ser.errors)


class BookDetailView(APIView):
    def get(self, request, publish):
        book = Book.objects.filter(publish=publish).first()
        ser = BookSerializer(instance=book)
        return Response(ser.data)

    def put(self, request, publish):
        book = Book.objects.filter(publish=publish).first()
        ser = BookSerializer(instance=book, data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        return Response(ser.errors)

    def delete(self, request, publish):
        Book.objects.filter(publish=publish).delete()
        return Response()

urls.py


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

serializer.py

from rest_framework import serializers

from .models import Book
from rest_framework.exceptions import ValidationError


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

    def create(self, validated_data):
        book = Book.objects.create(**validated_data)
        return book

    def update(self, instance, validated_data):
        for k, v in validated_data.items():
            setattr(instance, k, v)
        instance.save()
        return validated_data

    def validate_name(self, name):
        if len(name) < 5:
            raise ValidationError('名字长度小于5')
        return name

    def validate(self, attrs):
        if not attrs:
            raise ValidationError('不能传空数据')
        return attrs

models.py

from django.db import models


# Create your models here.
class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.CharField(max_length=32)
    publish = models.CharField(max_length=32)

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

from django.views import View

import json


def dictdata(func):
    def inner(self, request, *args, **kwargs):
        if request.POST:   # 判断有没有值,有值说明是form-data urlencode这两种下面都可以用
            # data_dict = {}
            # for k, v in request.POST.items():
            #     data_dict[k] = v  # 过滤列表

            request.data = {k: v for k, v in request.POST.items()}
        else:
            request.data = json.loads(request.body)  # 这种是json格式携带参数的,因为django不会帮我们解析
        res = func(self, request, *args, **kwargs)
        return res

    return inner


from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt, csrf_protect


class BookRawView(View):
    @method_decorator(csrf_exempt)  # 取消csrf校验只能加载dispatch上
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)

    @dictdata
    def post(self, request):
        ser = BookSerializer(data=request.data)
        if ser.is_valid():
            Book.objects.create(**ser.data)
            return JsonResponse(request.data)
        return JsonResponse(ser.errors)
posted @ 2023-02-01 22:41  clever-cat  阅读(45)  评论(0编辑  收藏  举报