drf-APIView

发布时间 2023-08-30 21:30:22作者: Way*yy

CBV源码分析

基于类的视图---->使用类来编写---->在类中写跟请求同名的方法(get、post....)---->路由配置(类名.as_view())

执行流程:
	请求过来--->做路由匹配---->匹配上了路由就会执行第二个参数(),把实参request传入---->BoonView.as_view()(request)

    
研究BookView.as_view()(request)是如何执行的?
BookView中并没有as_view,说明在它的父类View中有一个as_view的方法

研究View.as_view源码

# View.as_view的源码
@classonlymethod
def as_view(cls, **initkwargs):
    # 闭包函数
    def view(request, *args, **kwargs):
        self = cls(**initkwargs) # 类实例化得到对象:BookView的对象
        # 调用对象的绑定方法 dispath--->去BookView中找dispatch-->找不到去父类--->View中的dispatch
        return self.dispatch(request, *args, **kwargs) # 对象的方法
    return view
    
执行:
	BookView.as_view()(request)--->本质是执行 View中as_view内部的view方法,传入了reuqest---->在执行View的dispatch方法,传入了request

研究dispatch源码

def dispatch(self, request, *args, **kwargs):
    if request.method.lower() in self.http_method_names:
        # http_method_names = [get、post、put.....]
        # 判断请求方式是否在那个固定的列表中[get, post...]
        # 反射:通过字符串 动态的  操作对象的属性或方法
        handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        # 假设是get请求--->去BookView中取出 get方法赋值给handler
        else:
            handler = self.http_method_not_allowed
        # 执行handler加括号,传入了request
        # 本质是执行 get(request)
        return handler(request, *args, **kwargs)

总结

请求过来---->执行BookView.as_view()(request)---->内部执行了---->
View的as_view的内层的view闭包函数---->这个闭包函数调用了self.dispatch---->去BookView找,它里面没有---->去父类的View中找,通过反射获取到了跟请求同名的方法---->然后执行,将request传入

APIView

基于drf的APIView写接口

1、使用drf,以后都要在view中写CBV
2、CBV需要继承一个drf提供的APIView,但是APIView是继承了Django的View,所以使用起来没有什么区别,但是APIView比Django的View多个几个功能
多出的功能如下:
	1、去除了csrf认证
    2、包装了新的request
        在新的request中多出了以下:
            2.1、request.data
            2.2、request.query_parm
            2.3、request._request(老的request)
            2.4、request使用方法还跟以前一样
	3、执行了认证、频率、权限这三大认证
    4、全局异常处理:在视图类的方法中执行报错、会被异常捕捉、做统一处理
    
3、写视图类
	from rest_framework.views import APIView
	# 先导入模块

    class UserView(APIView):
        def get(self, request):
            print(request.data)  # <QueryDict: {'username': ['kevin']}>
            print(request.POST)  # <QueryDict: {'username': ['kevin']}>
            return JsonResponse({"code": 100, "msg": "请求成功"})
   # 不管是使用request.data还是request.POST拿到的数据都是一样的 
4、写路由
	urlpatterns = [
    path("user/", views.UserView.as_view())
]

APIView的执行流程分析

执行流程分析:
    1、-请求来了---->执行UserView.as_view()(request)---->UserView这个类里面没有as_view的方法,去父类---->APIView的as_view---->父类的as_view最终是做了禁用csrf认证
    @classmethod
    def as_view(cls, **initkwargs):
        # 这个view就是Django中的View下的as_view下的view
        view = super().as_view(**initkwargs)
        
        return csrf_exempt(view)
    2、-UserView.as_view()(request)---->执行了禁用掉csrf的View的as_view的view---->self.dispatch---->先找APIView下的dispatch
    def dispatch(self, request, *args, **kwargs):
    1、改写request方法
        # 包装了新的request对象
        request = self.initialize_request(request, *args, **kwargs)
		# 将新的request对象赋值给self.request
        self.request = request
    2、执行了3大认证
        try:
            self.initial(request, *args, **kwargs)
            # 三大认证,在initial下
            self.perform_authentication(request)
            self.check_permissions(request)
            self.check_throttles(request)
            # 跟Django中的View是一样的,根据请求的方式执行类中同名的方法
            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

            response = handler(request, *args, **kwargs)
	3、执行三大认证和视图类的方法,如果出现异常,抛错,就会捕捉异常统一处理
        except Exception as exc:
            response = self.handle_exception(exc)

        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response

包装新的request

1、request.data:前端传入的数据(post、put...编码格式)---->写在请求体的数据,还都在
2、老版中的request.GET被替换成了request.query_params
3、其他的所有属性方法,和之前一样

序列化组件介绍

由于基于原生Django写接口---->序列化比较麻烦---->使用drf提供的序列化组件快速序列化

使用步骤:
	1、在配置文件中APP配置中配置"rest_framework"
    2、写一个序列化类--->新建一个py文件---->serializer.py
    	2.1、继承drf提供的serializers.Serializer
        2.2、在类中写序列化的字段:字段类---->跟models里面的字段类一一对应---->这里的字段要比models里的字段多
    3、在视图函数中使用序列化类
    	查询多条数据:serializer=UserSerializer(instance=users,many=True)
        查询单条数据:serializer=UserSerializer(instance=user)
        "不同点是多条有:many=True,单条则没有"
    4、拿到序列化后的数据
    	serializer.data:多条是列表,单条是字典
    5、使用drf提供的Resposne返回
    	from rest_framework.response import Response

序列化组件的快速使用之序列化

序列化

from rest_framework import serializers


class BookSerializers(serializers.Serializer):
    # 写的是要序列化的字段
    book_name = serializers.CharField(# 写的是参数)
    book_price = serializers.IntegerField()

视图层

# 多条数据
class MybookViews(APIView):
    def get(self, request):
        # 查询数据库中的所有书籍
        book_obj = models.Books.objects.filter(is_delete=0).all()
        book = BookSerializers(instance=book_obj, many=True)
        print(book.data)  # [OrderedDict([('book_name', '京阿尼自传1'), ('book_price', 10000)])]
        return Response(book.data)

    
# 单条数据
class MybookDetail(APIView):
    def get(self, request, pk):
        book_obj = models.Books.objects.filter(pk=pk).first()
        book = BookSerializers(instance=book_obj)
        print(book.data)  # {'book_name': '京阿尼自传1', 'book_price': 10000}
        return Response(book.data)

路由层

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

常用字段类和参数

常用字段类

# 序列化类中的和models中的一一对应,但是序列化类中多一些

多出来的有:ListField、DictField
使用场景:{"a":"b",{"b":"c"},[{"c":"d"}]}
字段 字段构造方式
BooleanField BooleanField()
NullBooleanField NullBooleanField()
CharField CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
EmailField EmailField(max_length=None, min_length=None, allow_blank=False)
RegexField RegexField(regex, max_length=None, min_length=None, allow_blank=False)
SlugField SlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+
URLField URLField(max_length=200, min_length=None, allow_blank=False)
UUIDField UUIDField(format=’hex_verbose’) format: 1) 'hex_verbose'"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex'"5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a"
IPAddressField IPAddressField(protocol=’both’, unpack_ipv4=False, **options)
IntegerField IntegerField(max_value=None, min_value=None)
FloatField FloatField(max_value=None, min_value=None)
DecimalField DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置
DateTimeField DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
DateField DateField(format=api_settings.DATE_FORMAT, input_formats=None)
TimeField TimeField(format=api_settings.TIME_FORMAT, input_formats=None)
DurationField DurationField()
ChoiceField ChoiceField(choices) choices与Django的用法相同
MultipleChoiceField MultipleChoiceField(choices)
FileField FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ImageField ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ListField ListField(child=, min_length=None, max_length=None)
DictField DictField(child=)

常用字段参数

 # 字段类上,可以传参数,是做反序列化校验用的
	CharField:max_length,min_lenght,allow_blank: 可以不传
    IntegerField:max_value,min_value
# 所有字段都是可以通用的
	-重要的:read_only,write_only
    -default,required,allow_null

选项参数:

参数名称 作用
max_length 最大长度
min_lenght 最小长度
allow_blank 是否允许为空
trim_whitespace 是否截断空白字符
max_value 最小值
min_value 最大值

通用参数:

参数名称 说明
read_only 表明该字段仅用于序列化输出,默认False
write_only 表明该字段仅用于反序列化输入,默认False
required 表明该字段在反序列化时必须输入,默认True
default 反序列化时使用的默认值
allow_null 表明该字段是否允许传入None,默认False
validators 该字段使用的验证器
error_messages 包含错误编号与错误信息的字典
label 用于HTML展示API页面时,显示的字段名称
help_text 用于HTML展示API页面时,显示的字段帮助提示信息

序列化组件之校验

反序列化之校验:book.is_vaild()
	1、字段自己的校验(在字段类的属性上)
    2、局部钩子(给某个字段加校验规则)
    3、全局钩子()
    
反序列化保存:
	book.save()---->必须在序列化类中重写creat---->自己决定将数据保存到哪个数据库中

局部钩子和全局钩子

视图层

class MybookViews(APIView):
    def post(self, request):
        # 获取前端提交过来的数据在request.data里面
        book = BookSerializers(data=request.data)
      	# 校验数据----3层
        1、字段自己的校验
        2、局部钩子
        3、全局钩子
        if book.is_valid():
            book.save() # 如果不重写creat就会报错
            return Response({"code": 100, "msg": "添加成功"})
        else:
            # book.errors获取到错误信息
            return Response({"code": 101, "msg": book.errors})

序列化

from rest_framework import serializers
from rest_framework.exceptions import ValidationError


class BookSerializers(serializers.Serializer):
    # 写的是要序列化的字段
    book_name = serializers.CharField(max_length=8)
    book_price = serializers.IntegerField()

    # 局部钩子
    def validate_book_price(self, value):
        # value是book_price的值
        if value > 999:
            raise ValidationError("最高金额不得超过1000")
        return value

    # 全局钩子
    def validate(self, attrs):
        # 前端传入的所有数据,校验过后attrs 字典类型
        book_name = attrs.get("book_name")
        book_price = attrs.get("book_price")
        book_price = str(book_price)
        if book_name == book_price:
            raise ValidationError("书名和价格不能相同")
        return attrs
	# 重写create方法
    def create(self, validated_data):# validated_data:前端传入,校验过后的数据
        book_data = Books.objects.create(**validated_data)
        return book_data

路由

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

补充

函数和方法

# 函数和方法
	-函数:使用def关键字定义的函数,有几个参数,就要传几个参数,不能多,不能少-----》按位置传,按关键字传
    -方法:定义在类内部,可以自动传值的函数称之为方法---》绑定给对象的方法(对象方法)---》绑定给类的方法(类 方法)
    
    -对象方法:对象来调用,自动把对象传入,
    	-类也可以调用,但是类来调用,就变成了普通函数,有几个值,就要传几个值
        
    -类方法:类来调用,自动把类传入
    	-对象也可以调用,内部会取到对象的类,自动传入,它也是方法
        
        
    -函数和方法区别:能否自动传值

作业

# 1 username=lqz&password=123 这种格式转成字典
	-两层for循环
	-字典推导式
    
    
# 2 继承APIView使用Response 和序列化类,完成对book表4个接口的编写
	-序列化
    -反序列化校验
    	-图书名不能以 sb开头
        -图书名最大8位,最小3位
        -图书价格不能超过100,最低不能低于10
        
        
# 3 修改接口-->尝试写一下试试()

1、username=yfh&password=123 这种格式转成字

class MybookViews1(View):
    def post(self, request):
        book_obj = request.body
        book = book_obj.decode("utf8")
        book_list = book.split("&")
        dic = {}
        for i in book_list:
            lis = i.split("=")
            dic[lis[0]] = lis[1]
        print(dic)  # {'username': 'yfh', 'password': '123'}
        return JsonResponse(
            {"code": 200, "msg": "添加成功", })

2 继承APIView使用Response 和序列化类,完成对book表4个接口的编写

要求:
    -序列化
    -反序列化校验
    -图书名不能以 sb开头
    -图书名最大8位,最小3位
    -图书价格不能超过100,最低不能低于10

视图层

from rest_framework.views import APIView
from .serializers import BookSerializers
from rest_framework.response import Response

class MybookViews(APIView):
    def get(self, request):
        # 查询数据库中的所有书籍
        book_obj = models.Books.objects.filter(is_delete=0).all()
        book = BookSerializers(instance=book_obj, many=True)
        print(book.data)  # [OrderedDict([('book_name', '京阿尼自传1'), ('book_price', 10000)])]
        return Response(book.data)

    def post(self, request):
        # 获取前端提交过来的数据在request.data里面
        book = BookSerializers(data=request.data)
        # 当数据走到这里就会开始进行数据校验
        if book.is_valid():
            book.save()  # 如果不重写creat就会报错
            return Response({"code": 100, "msg": "添加成功"})
        else:
            # book.errors
            return Response({"code": 101, "msg": book.errors})


# 单条数据
class MybookDetail(APIView):
    def get(self, request, pk):
        book_obj = models.Books.objects.filter(pk=pk, is_delete=0).first()
        book = BookSerializers(instance=book_obj)
        print(book.data)  # {'book_name': '京阿尼自传1', 'book_price': 10000}
        if book.book_name == "":
            return Response({"code": 101, "msg": "该书籍不存在"})
        return Response(book.data)

    def put(self, request, pk):
        # 获取到用户要修改的信息
        book_name = request.data.get("book_name")
        book_price = int(request.data.get("book_price"))
        book_obj = models.Books.objects.filter(pk=pk).first()
        book = BookSerializers(book_obj, data={"book_name": book_name, "book_price": book_price})
        if book.is_valid():
            book.save()
        else:
            return Response({"code": 101, "msg": book.errors})
        return Response({"code": 100, "msg": {"book_name": book_name, "book_price": book_price}})

    def delete(self, request, pk):
        models.Books.objects.filter(pk=pk).update(is_delete=1)
        return Response({"code": 100, "msg": {}})

序列化

from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from .models import Books


class BookSerializers(serializers.Serializer):
    # 写的是要序列化的字段
    book_name = serializers.CharField(max_length=8, min_length=3, write_only=True)
    book_price = serializers.IntegerField(write_only=True)

    # 全局钩子
    def validate(self, attrs):
        # 前端传入的所有数据,校验过后attrs 字典类型
        print(attrs)
        book_name = attrs.get("book_name")
        book_price = attrs.get("book_price")
        if book_name.startswith("sb"):
            raise ValidationError("图书名称不能以sb开头")
        if book_price < 10 or book_price > 100:
            raise ValidationError("最高金额不得超过100")
        return attrs

    def update(self, instance, validated_data):
        book_data = Books.objects.filter(pk=instance.pk).update(**validated_data)
        return book_data

    def create(self, validated_data):
        book_data = Books.objects.create(**validated_data)
        return book_data

路由

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