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()),
]