Python高级day03
前情回顾
# 1 前端编码格式---》数据如何获取 -urlencoded: body体中 :username=lqz&password=123 django 的request.POST取出值 -json: body体中 :{"username":"lqz","password":"123"} django 的request.POST取不出值,从request.body-->自己做反序列化 -form-data:body中格式固定:数据部分和文件部分--》request.POST能取出数据,取不出文件,文件都是从request.FILES '----------------------------789048583058585187025897\r\nContent-Disposition: form-data; name="username"\r\n\r\nlqz\r\n文件二进制 # 2 varchar和char char是定长,规定了几位就会申请几位内存空间,输入不满位数会自动补齐位数 varchar是可变长,输入了几位就会存几位 # 3 命令创建django项目 pip3 install djagno----》释放出一个可执行文件 djagno-admin startproject 项目名 # 4 文件对象:django中的文件对象,原生文件对象 -django:from django.core.files.uploadedfile import InMemoryUploadedFile -原生文件:_io.BufferedWriter -django中文件类没有继承原生文件类,但是有原生文件类所有方法 # 5 装饰器模板 def warpper_request(func): def inner( *args, **kwargs): # 在执行被装饰函数前执行 res = func(*args, **kwargs) # 在执行被装饰函数后执行 return res return inner # 6 restful规范 -表征状态转移:在做前后的分离后端写api接口时的一个规范,总共有10条 - # 7 序列化反序列化 -序列化:把我们识别的数据转换成指定的格式提供给别人 -反序列化:把别人提供的数据转换/还原成我们需要的格式 # 8 drf,第三方模块,只能用在django,可以帮助我们快速编写符合restful规范的接口 -drf:djangorestframework
cbv源码分析
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的对象 # 调用对象的绑定方法 dispath---》去BookView中找dispatch--》找不到去父类---》View中的dispatch return self.dispatch(request, *args, **kwargs) # 对象的方法 return view
执行 BookView.as_view()(request)===》本质是执行 View中 as_view内部的view方法,传入了request===》再执行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传入
APIView执行流程分析
基于drf的APIView写接口
# 1. 使用drf,以后都写cbv===》继承一个视图类===》以后都继承drf提供的APIView # 2. APIView继承了Django的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.view import APIView class PublishView(APIView): def get(self, reuqest): return JsonResponse({'code': 999}) # 2. 配置路由 path('publish/', PublishView.as_view())
APIView的执行流程分析
比之前继承Django的View多了如下功能
1. 去除了csrf认证
2. 包装了新的request
3. 执行了认证、频率、权限 这三大认证
4. 全局异常处理:在视图类的方法中执行报错,会被异常捕捉,做统一处理
执行流程分析
请求来了===》执行 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的dispatch
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的dispath是一样的---》根据请求方式执行类中同名方法 ###执行视图类的方法 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 全局异常 '''
序列化组件介绍
定义:是drf提供的一个可以做序列化和反序列化的 一个类(东西或组件)
作用(能干的事):
1 序列化
2 反序列化
3 数据校验
使用步骤
1. 先在配置文件中注册:
INSTALLED_APPS = [
'rest_framework',
]
2. 写一个序列化类===》新建一个py文件===》serealizer.py
继承drf提供的serializers.Serializer
在类中写要序列化的字段:字段类===》跟之前学过的models.py中的字段类完全对应,但是比models多
3. 在视图类中,使用序列化类
多条:serializer = UserSerializer(instance=users, many=True)
单条:serializer = UserSerializer(instance=users)
4. 拿到序列化之后的数据
serializer.data ===》可能是列表,可能是字典
5. 使用drf提供的 Response 返回
from rest_framework.response import Response
序列化组件快速使用之序列化
路由:
urlpatterns = [ path('users/', UserView.as_view()), path('users/<int:pk>', UserDetailView.as_view()), ]
视图类:
from .models import User from .serializer import UserSerializer from rest_framework.response import Response from rest_framework.views import APIView class UserView(APIView): # 查询所有数据 def get(self, request): users = models.User.objects.all() # 使用序列化类 # instance:选择要序列化的对象;many=True:表示要序列化多条信息,不写默认序列化一条 ser = serialize.UserSerializer(instance=users, many=True) # 拿到序列化后的数据 ser.data--->多条就是列表 单条字典 return Response(ser.data) # 添加一条数据 def post(self, request): # 前端提交过来的数据 request.data ser = serialize.UserSerializer(data=request.data) ''' 校验数据(三层): 1. 字段自己的校验(字段类的属性上) 2. 局部钩子:给某个字段加校验规则 3. 全局钩子:给多个字段加校验规则 ''' if ser.is_valid(): # 保存 # 序列化类中重写create方法 ser.save() return Response({'code': 100, 'msg': '添加成功'}) else: # ser.errors:校验失败错误的数据 return Response({'code': 101, 'msg': ser.errors}) class UserDetailView(APIView): # 查询一条数据 def get(self, request, pk): users = models.User.objects.filter(pk=pk).first() # 使用序列化类 # instance:选择要序列化的对象;many=True:表示要序列化多条信息,不写默认序列化一条 ser = serialize.UserSerializer(instance=users) # print(ser.data) return Response(ser.data) # 修改一条数据 def put(self, request, pk): # name = request.data.get('name') # hobby = request.data.get('hobby') # age = request.data.get('age') # addr = request.data.get('addr') # models.User.objects.filter(pk=pk).update(name=name, hobby=hobby, age=age, addr=addr) # 拿到前端传过来的数据 ser = serialize.UserSerializer(data=request.data) print(ser) if ser.is_valid(): # 修改数据 ser.update(pk, request.data) return Response({'code': 100, 'msg': '修改成功'}) else: # ser.errors:校验失败错误的数据 return Response({'code': 101, 'msg': ser.errors}) # 删除一条数据 def delete(self, request, pk): models.User.objects.filter(pk=pk).delete() return Response({'删除成功'})
序列化类:
# 写序列化类 from rest_framework import serializers class UserSerializer(serializers.Serializer): # 写要序列化的字段 name = serializers.CharField() # hobby = serializers.CharField() # password=serializers.CharField() age=serializers.IntegerField() # 重写create方法 def create(self, validated_data): # validated_data:前端传入,序列化之后的数据 user = models.User.objects.create(**validated_data) return user # 重写update方法 def update(self, instance, validated_data): # instance=传过来的pk,validated_data:前端传入,序列化之后的数据 user = models.User.objects.filter(pk=instance).update(**validated_data) return user
常用字段类和参数
写序列化类的时候,写了CharField,IntegerField 跟django中models中的类似
序列化类中的和modles中的一一对应,但是序列化类中的多一些
多的:
ListField
DictField
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
最大长度 | |
---|---|
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页面时,显示的字段帮助提示信息 |
序列化组件之校验
序列化
反序列化
反序列化校验
反序列化之校验:
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()), ]
补充复习:
函数和方法
函数:使用def关键字定义的函数,有几个参数就传几个参数,不能多也不能少==》按位置传或者按关键字传参
方法:定义在类内部,可以自动传值的函数称之为方法==》绑定给对象的方法(对象方法)/绑定给类的方法(类方法)
对象方法:对象来调用,自动把对象传入
类也可以调用,但是类来调用就变成了普通函数,有几个值,就要传几个值
类方法:类来调用,自动把类传入
对象也可以调用,内部会取到对象的类,自动传入,他也是方法
函数和方法的区别:能否自动传值