3 APIView执行流程分析
3.1 基于drf的APIView写接口
3.2 APIView的执行流程分析
4 序列化组件介绍
5 序列化组件快速使用之序列化
6 常用字段类和参数
7序列化组件之校验
8字典推导式回顾
1 函数和方法回顾
函数和方法
-函数:使用def关键字定义的函数,有几个参数,就要传几个参数,不能多,不能少-----》按位置传,按关键字传
-方法:定义在类内部,可以自动传值的函数称之为方法---》绑定给对象的方法(对象方法)---》绑定给类的方法(类 方法)
-对象方法:对象来调用,自动把对象传入,
-类也可以调用,但是类来调用,就变成了普通函数,有几个值,就要传几个值
-类方法:类来调用,自动把类传入
-对象也可以调用,内部会取到对象的类,自动传入,它也是方法
-函数和方法区别:能否自动传值
########使用案例############3
# 函数:使用def关键字定义的函数
# 函数:有几个参数,就要传几个参数,不能多,也不能少---------->按位置传和按关键字传
def add():
pass
# 方法:定义在类内部,可以自动传值的函数称之为方法
# 绑定给对象的方法(对象方法)------------>绑定给类的方法(类 方法)
class Person:
def printName(self):
print(self.name)
# 绑定给类的方法-----》类来调用-------》自动把类传入
@classmethod
def test(cls):
print(cls.__name__)
# 注意:这个时候不能直接在实例化类的时候传入参数值
# 由于Person类中没有 __init__() 方法,并且Person的父类Object类中也没有 __init__()方法
# p = Person(name='lin')
p = Person()
# 若是给实例化的对象中放入属性,用对象调用方法就可以了
p.name = 'lin'
p.printName()
# 类是否可以调用方法?
# 可以------->怎么调用呢?
# 类名.方法
# 若是用类来调用就变成了普通函数,所以有几个值就得传几个值
Person.printName(p)
# 绑定给类的方法,会自动把类传入
Person.test()
# 对象也可以调用类的方法----》直接调用,内部会把对象的类拿到,然后自动传入
p.test()
案例2:
from types import MethodType, FunctionType
# 函数和方法
# 普通函数,调用时候,有几个值,就要传几个值
# 方法:定义在类内部:对象的绑定方法,类的绑定方法----》绑定给谁,谁来调用---》调用的时候,会自动传值
# 类调用对象绑定方法,就变成了普通函数,有几个值就要传几个值
# 对象调用类的绑定方法,就是方法,会自动传值
# 一切皆对象,函数也是个对象, 由某个类产生 FunctionType
def add():
pass
print(isinstance(add, FunctionType)) # True 判断一个对象是不是这个类的对象
print(isinstance(add, MethodType)) # False 判断一个对象是不是这个类的对象
class Foo:
def run(self):
pass
@classmethod
def xx(cls):
pass
@staticmethod
def zz():
pass
# 对象调用 run ,run就是方法 会自动传值
f = Foo()
# print(isinstance(f.run, FunctionType)) # False
# print(isinstance(f.run, MethodType)) # Ture
#
# # 类来调用run,run就是函数,有几个值就要传几个值
# print(isinstance(Foo.run, FunctionType)) # True
# print(isinstance(Foo.run, MethodType)) # False
# 类调用类的绑定方法---》就是方法
print(isinstance(Foo.xx, FunctionType)) # False
print(isinstance(Foo.xx, MethodType)) # True
# 对象调用类的绑定方法---》 也是方法
print(isinstance(f.xx, FunctionType)) # False
print(isinstance(f.xx, MethodType)) # True
# 对象来调用
print(isinstance(f.zz, FunctionType)) # True
print(isinstance(f.zz, MethodType)) # False
# 类来调用
print(isinstance(Foo.zz, FunctionType)) # True
print(isinstance(Foo.zz, MethodType)) # False
2 cbv源码分析
# 基于类的视图---》使用类编写---》在类中写跟请求方式同名的方法---》路由配置 类名.as_view()
# 请求过来,什么请求,就会执行跟请求方式同名的方法
# 执行流程
-请求来了---》做路由匹配---》如果是books/匹配上了
path('books/', BookView.as_view())---》内部就会执行 第二个参数(),
把实参request传入--->BookView.as_view()(request)--->
-研究:BookView.as_view()(request) 如何执行的
-BookView 中没有as_view,说明在父类中有as_view---》BookView的父类是View
# View.as_view的源码
@classonlymethod
def as_view(cls, **initkwargs):
# 闭包函数
def view(request, *args, **kwargs):
self = cls(**initkwargs) # 类实例化得到对象:BookView的对象
# 调用对象的绑定方法 dispatch---》去BookView中找dispatch--》找不到去父类---》View中的dispatch
return self.dispatch(request, *args, **kwargs) # 对象的方法
return view
# 执行:BookView.as_view()(request)---》本质是执行 View中as_view内部的view方法,
# 传入了reuqest----》在执行View的dispatch方法,传入了request
# 看 :View.dispatch---》源码
def dispatch(self, request, *args, **kwargs):
if request.method.lower() in self.http_method_names: # 判断请求方式是否在那个固定的列表中[get, post...]--->
# 反射:通过字符串 动态的 操作对象的属性或方法
# 假设是get请求---》去BookView中取出 get方法赋值给handler
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
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传入
3 APIView执行流程分析
3.1 基于drf的APIView写接口
#1 使用drf,以后都写cbv---》继承一个视图类---》以后都继承drf提供 的APIView
#2 APIView 继承了djagno的View
#3 补充:这三个都是一个
from django.views import View
from django.views.generic import View
from django.views.generic.base import View
# 4 继承APIView写cbv---》执行起来跟之前效果一样---》内部发生了非常大的变化
1 写视图类
from rest_framework.views import APIView
class PublishView(APIView):
def get(self, request):
return JsonResponse({'code': 999})
2 配置路由
path('publish/', PublishView.as_view()),
3.2 APIView的执行流程分析
# 比之前继承django的View多了如下
-0 去除了csrf认证
-1 包装了新的request
-2 执行了认证,频率,权限 这三大认证
-3 全局异常处理:在视图类的方法中执行报错,会被异常捕获,做统一处理
# 执行流程分析
-请求来了---》执行PublishView.as_view()(request)--->PublishView类没有as_view---》找父类---》APIView的as_view
-APIView的as_view---》本质在禁用csrf认证
@classmethod
def as_view(cls, **initkwargs):
# 调用父类的as_view得到返回值赋值给view---》django的View的as_view【看过】---》返回View的as_view的闭包函数 view
view = super().as_view(**initkwargs) # 现在这个view就是原来看的View的as_view的view
# 去除了csrf认证----》局部禁用csrf---》在视图函数上加 装饰器csrf_exempt
# 加了装饰器的本质: 被装饰的函数=装饰器(被装饰的函数)
return csrf_exempt(view)
-PublishView.as_view()(request)--》执行了禁用掉csrf的View的as_view的view---》self.dispath--->APIView的dispath
def dispatch(self, request, *args, **kwargs):
# 1 包装了新的request对象
request = self.initialize_request(request, *args, **kwargs)
# 把新的request,赋值给了 self.request
# self 是 PublishView的对象
self.request = request
try:
# 2 执行了3大认证
self.initial(request, *args, **kwargs)
######开始### 跟之前View的dispatch是一样的---》根据请求方式执行类中同名方法
###执行视图类的方法
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)
######结束###
# 执行三大认证和视图类的方法,如果出了异常,抛了错,会被捕获,统一处理
except Exception as exc:
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
# 装饰器的本质
@auth # add=auth(add)-->以后调用add 实际上调用 auth(add)()
def add():
pass
add=auth(add)
### 包装新的request
# 1 request.data:前端传入的数据(post,put,编码格式)--->写到在请求体中的数据,都用
# 2 老request中有 request.GET 新的使用request.query_params 充当
# 3 其他的所有属性方法,用起来跟之前一样
### 三大认证
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
### 处理了全局异常,统一处理了
#### 总结:
视图类继承APIView后多了
-0 去除了csrf认证
-1 新的request
-request.data
-request.query_params
-request.其他跟之前一样
-request._request 是老的
-2 三大认证
-3 全局异常
4 序列化组件介绍
# 基于原生djagno写接口---》序列化---》自己用for循环做-->比较麻烦
# 借助于drf提供的序列化组件来完成快速序列化
## 使用步骤
1 先在配置文件中注册 :
INSTALLED_APPS = [
'rest_framework',
]
2 写一个序列化类--》新建一个py文件---》serializer.py
-继承drf提供的serializers.Serializer
-在类中写要序列化的字段:字段类---》跟之前学过的models.py中的字段类完全对应,
但是比models多
3 在视图类中,使用序列化类
多条:serializer=UserSerializer(instance=users,many=True)
单条:serializer=UserSerializer(instance=user)
4 拿到序列化后的数据
serializer.data 可能是列表,可能是字典
5 使用drf提供的Resposne 返回
from rest_framework.response import Response
5 序列化组件快速使用之序列化
1 路由
urlpatterns = [
path('users/', UserView.as_view()),
path('users/<int:pk>', UserDetailView.as_view()),
]
2 视图类
from .models import User
from .serializer import UserSerializer
from rest_framework.response import Response
class UserView(APIView):
def get(self,request):
users=User.objects.all()
# 之前用for循环,现在用序列化类
# 传了两个参数:instance 要序列化的对象(qs,单个对象) many=True表示序列化多条,如果不写就是序列化一条
ser=UserSerializer(instance=users,many=True)
# 拿到序列化后的数据 ser.data--->多条就是列表 单条字典
return Response(ser.data)
class UserDetailView(APIView):
def get(self,request,pk):
user=User.objects.all().filter(pk=pk).first()
# 传了两个参数:instance 要序列化的对象 many=True表示序列化多条
ser=UserSerializer(instance=user)
return Response(ser.data)
3 序列化类
# 写序列化类
from rest_framework import serializers
class UserSerializer(serializers.Serializer):
# 写要序列化的字段
name = serializers.CharField()
# hobby = serializers.CharField()
# password=serializers.CharField()
age=serializers.IntegerField()
6 常用字段类和参数
1 常用字段类
# 写序列化类的时候,写了CharField,IntegerField 跟django中models中的类似
# 序列化类中的和models中的一一对应,但是序列化类中多一些
# 多的--->暂时有个印象,后面会详细讲
ListField
DictField
# 使用场景
{name:金鹏没,price:99,publish:{name:xx出版社,addr:南京},authors:[{},{}]}
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=) |
2 常用字段参数
# 字段类上,可以传参数,是做反序列化校验用的
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页面时,显示的字段帮助提示信息 |
7 序列化组件之校验
# 序列化组件
- 序列化
- 反序列化
- 反序列化校验
# 反序列化之校验: ser.is_valid()
1 字段自己的校验规则(字段类的属性上)
2 局部钩子(给某个字段加校验规则)
3 全局钩子
# 反序列化保存
ser.save()---》必须序列化类中重写 create--》自己定保存到哪个表
def create(self, validated_data): # validated_data:前端传入,校验过后的数据
user = User.objects.create(**validated_data)
return user
案例展示
视图类
from .models import User
from .serializer import UserSerializer
from rest_framework.response import Response
class UserView(APIView):
def get(self, request):
users = User.objects.all()
# 之前用for循环,现在用序列化类
# 传了两个参数:instance 要序列化的对象(qs,单个对象) many=True表示序列化多条,如果不写就是序列化一条
ser = UserSerializer(instance=users, many=True)
# 拿到序列化后的数据 ser.data--->多条就是列表 单条字典
return Response(ser.data)
def post(self, request):
# 前端提交过来的数据 request.data
ser = UserSerializer(data=request.data)
# 校验数据--》3层: 1 字段自己的校验规则(字段类的属性上) 2 局部钩子(给某个字段加校验规则) 3 全局钩子
if ser.is_valid():
# 保存
ser.save() # 会报错,序列化类中重写create方法
return Response({'code': 100, 'msg': '保存成功'})
else: # ser.errors 校验失败错误的数据
return Response({'code': 101, 'msg': ser.errors})
class UserDetailView(APIView):
def get(self, request, pk):
user = User.objects.all().filter(pk=pk).first()
# 传了两个参数:instance 要序列化的对象 many=True表示序列化多条
ser = UserSerializer(instance=user)
return Response(ser.data)
序列化类
# 写序列化类
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from .models import User
class UserSerializer(serializers.Serializer):
# 写要序列化的字段
# 字段自己
name = serializers.CharField(max_length=8, min_length=3, required=True)
hobby = serializers.CharField()
password = serializers.CharField()
age = serializers.IntegerField()
# 局部钩子
# 写一个方法 validate_字段名,传入要校验的数据--》前端传入的
def validate_name(self, value):
if value.startswith('sb'):
raise ValidationError('不能以sb开头') # 如果校验失败,抛ValidationError
return value # 如果校验通过,返回 value,后续继续用
# 全局钩子
# 名字和hobby不能一致 多个字段的同时校验
def validate(self, attrs): # 前端传入的所有数据,校验过后attrs 字典
name = attrs.get('name')
hobby = attrs.get('hobby')
if name == hobby:
raise ValidationError('名字和爱好不能一样')
else:
return attrs
# 重写create方法
def create(self, validated_data): # validated_data:前端传入,校验过后的数据
user = User.objects.create(**validated_data)
return user
路由
urlpatterns = [
path('users/', UserView.as_view()),
path('users/<int:pk>', UserDetailView.as_view()),
]
8 字典推导式回顾
'''
username=lin&password=123 这种格式转成字典
-两层for循环
-字典推导式
'''
res = 'username=lin&password=123'
# 定义一个空字典用于接收键值
res_dict = {}
# 将字符串分割成键值对并添加到字典中
for item in res.split('&'):
print(item) # username=lqz password=123
key, value = item.split('=')
res_dict[key] = value
print(res_dict)
# 使用字典推导式将字符串分割成键值对并创建字典
res_dict = {key: value for key, value in (item.split('=') for item in res.split('&'))}
print(res_dict)