day72:drf:
目录
5.Field字段参数:read_only和write_only
1.续:反序列化功能(5-8)
用户post类型提交数据,反序列化功能的步骤
class Student(View): def get(self,request): ... def post(self,request): '''1.获取用户在前端输入的数据''' recv_data = { 'name':request.POST.get('name'), 'age':request.POST.get('age'), 'class_null':request.POST.get('class_null'), 'description':request.POST.get('description'), } '''2.实例化序列化器类,生成序列化器类对象,并将我们获取的数据传进去完成对数据的反序列化''' ser = StudentSerizlizer(data=recv_data) '''3.校验,全部通过得到True,一个字段错了都是得到False''' print(ser.is_valid()) '''4.所有字段的错误信息''' print(ser.errors) '''---------------------------------------------------------------''' '''5.反序列化完成之后的数据''' print(ser.validated_data) '''6.将反序列化后的数据添加到数据库中''' ret = models.Student.objects.create( **ser.validated_data ) '''7.反序列化的数据已经存储到数据库中,但是现在需要前端显示出我们新添加的数据''' '''按照接口规范:如果post请求提交保存了一条记录,那么要将新添加的数据再响应回去''' serializer = StudentSerializer(instance=ret) print(serializer.data) # 序列化完成后的数据 '''8.将序列化完成后的数据返回给前端,并设置safe和ensure_ascii参数''' return JsonResponse(serializer.data,safe=False,json_dumps_params={"ensure_ascii":False})
反序列化功能的局部钩子和全局钩子
1.局部钩子和全局钩子在序列化器中的使用
class StudentSerizlizer(serializers.Serializer): name = serializers.CharField(max_length=4,validators=[check666,]) age = serializers.IntegerField(max_value=18) class_null = serializers.CharField() '''required=False,allow_null=True允许字段为空,也就是不用传递过来这个data''' description = serializers.CharField(required=False,allow_null=True) '''allow_blank=True 允许只为空字符串''' description = serializers.CharField(allow_blank=True) '''局部钩子:针对单个属性对应的数据进行校验''' def validate_name(self,val):# val就是你对应字段的内容,函数名固定写法:validate_字段名 if '777' in val: raise serializers.ValidationError('不能有777') return val # 如果没有错误,需要return这个属性数据 '''全局钩子: 主要是针对多个属性数据进行校验''' def validate(self,data): '''data是反序列化后得到的字典''' print(data) # OrderedDict([('name', 'c778'), ('age', 6), ('class_null', '1'), ('description', '123')]) age = data.get('age') class_null = data.get('age') if age == class_null: raise serializers.ValidationError('age和class——null不能相等') return data #如果没有错误,全局钩子要return所有数据
2.反序列化相关校验的执行顺序
举个例子,代码如下所示
class StudentSerizlizer(serializers.Serializer): # 1.字段限制 name = serializers.CharField(max_length=4,validators=[check666,]) age = serializers.IntegerField(max_value=18) # 2.局部钩子 def validate_name(self,val): pass def validate_age(self,val): pass # 3.全局钩子 def validate(self,data): pass
执行顺序:
1.先执行name中CharField参数中的规则
2.执行name的局部钩子函数
3.执行age的IntegerField参数中的规则
4.执行age的局部钩子函数
5.执行全局钩子函数
Tip:is_valid中的raise_exception参数
serializer.is_valid(raise_exception=True) # 等于True会主动抛出异常
反序列化的数据保存功能
反序列化的数据保存,一共有两种方式:
方式一:直接在视图中保存
# 方式1:直接在视图中保存
class StudentView(View): def post(self,request): if ser.is_valid(): ret = models.Student.objects.create( **ser.validated_data ) serializer = StudentSerizlizer(instance=ret) return JsonResponse(serializer.data,safe=False,json_dumps_params={'ensure_ascii':False}) else: print(ser.errors) return JsonResponse({'error':'有字段错误'})
方式二:视图中通过save方法来触发序列化器类中的create方法
# 方式2 save+create
# 在序列化器中定义create方法来数据的保存 class StudentSerizlizer(serializers.Serializer): name = serializers.CharField(max_length=4,validators=[check666,]) age = serializers.IntegerField(max_value=18) ...... def create(self, validated_data): # 定义create方法 '''self.validated_data和validated_data结果是相同的,所以直接用validated_data即可''' print(validated_data) ret = models.Student.objects.create( **validated_data ) return ret # 视图中通过save方法来触发,序列化器类中的create方法 class StudentView(View): def post(self,request): if ser.is_valid(): '''通过save()方法去触发序列化器中的create方法''' instance = ser.save() # 得到create方法的返回值 serializer = StudentSerizlizer(instance=instance) return JsonResponse(serializer.data,safe=False,json_dumps_params={'ensure_ascii':False})
反序列化的数据更新功能
# 在序列化器中定义update方法来数据的更新 class StudentSerizlizer(serializers.Serializer): name = serializers.CharField(max_length=4,validators=[check666,]) age = serializers.IntegerField(max_value=18) ... # 做更新动作 def update(self, instance, validated_data): '''instance,validated_data?????''' print(instance) # id=2 的模型类对象 print(validated_data) # 反序列化后的数据 instance.name = validated_data['name'] instance.age = validated_data['age'] instance.class_null = validated_data['class_null'] instance.description = validated_data['description'] instance.save() return instance # 视图部分 class StudentView(View): def put(self,request): '''要更新的数据''' data = { 'id': 2, 'name':'xx', 'age':8, 'sex':0, 'class_null':'9', 'description':'xx', } obj = models.Student.objects.get(id=data.get('id')) '''instance=obj用来指定这个save()触发的是序列化器中的update方法,而不是create方法,data就是你要更新的数据''' s_obj = StudentSerizlizer(instance=obj,data=data) '''判断反序列化之后的数据是否合法''' if s_obj.is_valid(): s_obj.save() # 触发序列化器类的update方法 else: return JsonResponse({'error':'数据错误'})
Field字段参数:read_only和write_only
read_only和write_only是field字段里面的参数,也是用来对字段做限制的
from rest_framework import serializers class StudentSerizlizer(serializers.Serializer): '''read_only=True,序列化时序列化出该字段数据,反序列化校验时不要校验这个数据''' id = serializers.IntegerField(read_only=True) name = serializers.CharField(max_length=4,) '''write_only=True,序列化时不序列化这个字段数据,反序列校验时需要客户端传递这个数据过来进行校验''' age = serializers.IntegerField(max_value=18,write_only=True) class_null = serializers.CharField() description = serializers.CharField(allow_blank=True)
视图部分这样写:
from django.shortcuts import render from django.views import View from students import models from .serializers import StudentSerizlizer from django.http import JsonResponse class StudentView(View): def get(self,request): all = models.Student.objects.all() serializer = StudentSerizlizer(instance=all, many=True) return JsonResponse(serializer.data, safe=False, json_dumps_params={'ensure_ascii':False}) def post(self,request): print(request.POST) data = { 'name':request.POST.get('name'), 'sex':request.POST.get('sex'), 'age':request.POST.get('age'), 'class_null':request.POST.get('class_null'), 'description':request.POST.get('description'), } serializer = StudentSerizlizer(data=data) serializer.is_valid() print(serializer.errors) print('正确数据:',serializer.validated_data) return JsonResponse({'xx':'xx'})
Field字段参数:partial参数
partial参数默认为False,若partial=True则代表只需要校验传入给序列化器的数据(data),其他的字段不校验.适用于部分数据更新
应用场景:
如果用户在前端提交/更新数据时,必须要将五个字段都提交,否则就会报错。
但是在现实应用场景下,有很多情况是不需要将五个字段全部提交的。比如数据更新
这个时候想要更改,但是序列化器的代码已经成型了,不太好进行修改。 【eg:提交需要提交5个字段,更新提交2个字段】
class StudentView(View): def put(self,request): '''要更新的数据''' data = { 'name':'chao', 'age':18 } '''partial=True:只需要校验传入给序列化器的数据(data),其他的字段不校验.适用于部分数据更新''' serializer = StudentSerizlizer(data=data,partial=True) serializer.is_valid() print(serializer.errors) print('正确数据:', serializer.validated_data) return JsonResponse({'ss': 'kk'})
模型类序列化器的使用:Modelserializer
1.Modelserializer的简单介绍
ModelSerializer与常规的Serializer相同,但提供了:
-
基于模型类自动生成一系列字段
-
基于模型类自动为Serializer生成validators,比如unique_together
-
包含默认的create()和update()的实现
2.Modelserializer的使用
models.py
….
serializers.py
from rest_framework import serializers from students.models import Student class StudentModelSerializer(serializers.ModelSerializer): # 如果模型类序列化器,必须 1.声明本次调用是哪个模型,2.模型里面的哪些字段 class Meta: model = Student fields = ["id","name","age","description","sex"] # fields = "__all__" # 表示操作模型中的所有字段 # 添加额外的验证选项 exclude = ['id',] # 排除字段 extra_kwargs = { "sex":{"write_only":True,}, "id":{"read_only":True,} }
views.py
class StudentViewSet(View): def post(self,request): data = request.POST serializers = StudentsSerializer(data=data) status = serializers.is_valid() student = serializers.save() # 上面使用的ModelSerializer,所以不需要我们自己写create方法了 print(student) return JsonResponse({'msg':'henhao'})
drf:视图相关
1.APIView
原来的视图都是继承View类
class StudentView(View): def get: pass ......
而drf提供了一个APIView,如下所示
from django.shortcuts import render from django.shortcuts import HttpResponse from django.views import View from rest_framework.views import APIView from rest_framework.response import Response """ 1.drf提供的请求和响应类只能在drf封装过的子视图类中使用,也就是说不能再django.view.View中使用 2.只要类视图直接或者间接继承了APIView,则视图方法中使用的request,就是rest_framework.request.Request,同时,只有在APIVIew的子视图类中才可以使用rest_framework.respone.Response """ class StudentAPIView(APIView): # class StudentAPIView(View): #之前的写法 def get(self,request): print(request) # <rest_framework.request.Request object at 0x11d733e90> return Response({'msg':'ok'})
2.APIView中的request
REST framework 提供了Parser解析器,在接收到请求后会自动根据Content-Type指明的请求数据类型(如JSON、表单等)将请求数据进行parse解析,解析为类字典[QueryDict]对象保存到Request对象中。这里我们可以自行写一个接口测试一下django原来的模式是解析不了json数据的,drf可以解析。但是需要注意的是:客户端如果传递过来的是json数据,那么request.data获取到的字典类型数据,不是querydict类型,也就没有了getlist方法,多选的数据,通过get就能取出来。
无论前端发送的哪种格式的数据,我们都可以以统一的方式读取数据。
from rest_framework.views import APIView class UserView(APIView): def get(self,request): '''当我们访问http://127.0.0.1:8001/req/students/?age=100''' print(request.GET) # <QueryDict: {'age': ['100']}> print(request.query_params) # <QueryDict: {'age': ['100']}> # request.GET和request.query_params是等价的 return JsonResponse({'xx':'xxxxx'}) def post(self,request): print(request) ''' 1.当发送的数据格式为urlencoded类型时,request.POST和request.data等价 2.当发送过来的是json类型数据时,我们使用request.data属性能够获取到数据,request.data是无法获取json数据的 ''' print(request.data) #{'username': 'xxxx', 'password': '123'},普通字典类型 # request.data能够获取到客户端发送过来的json类型数据,但是得到的结果为普通字典类型,但是如果是多选数据,不能使用getlist方法获取 return JsonResponse({'xx': 'xxxxx'})
3.APIView中的response
1.response介绍
我们后端如果通过Response来想用数据时,通过postman发送get请求获取数据,你会发现得到的是纯json数据,但是通过浏览器访问时会的到一个页面。这是为什么呢?drf的APIView在响应内容的时候会自动通过请求头中的浏览器信息来进行数据的回复(Response),如果是浏览器,那么返回的就是个页面(当然我们也可以不让他返回页面,可设置的,后面说)如果不是浏览器,给你返回的就是纯json数据。这是由drf中配置的两个不同的响应类产生的效果,看下面的配置。
# 查看drf的默认配置 from rest_framework import settings # 用户配置替换drf内部配置的写法 REST_FRAMEWORK = { 'DEFAULT_RENDERER_CLASSES': ( # 默认响应渲染类 'rest_framework.renderers.JSONRenderer', # json渲染器 'rest_framework.renderers.BrowsableAPIRenderer', # 浏览器API渲染器 ) }
2.response参数解释
# 引入Response from rest_framework.response import Response # response里面的相关参数 return Response({},status=201,template_name=None,headers=None,content_type=None) ''' {}:响应的数据... status:状态码 template:用于自定义浏览器的响应页面 headers:自定义响应头键值对 content_type:.... '''
data
: 为响应准备的序列化处理后的数据;-
status
: 状态码,默认200; -
template_name
: 模板名称,如果使用HTMLRenderer
时需指明;就是有些人觉得Response返回的那个页面丑,那么就可以通过这个模板自行定制。 -
headers
: 用于存放响应头信息的字典;比如放一些cookie啊或者一些自定制的响应头啊都可以,例如:return Response({‘msg’: ‘ok’},status=204,headers={‘xx’:’oo’}) -
content_type
: 响应数据的Content-Type,通常此参数无需传递,REST framework会根据前端所需类型数据(accept请求头)来设置该参数。
3.状态码大全
# 查看所有状态码 from rest_framework import status HTTP_100_CONTINUE = 100 HTTP_101_SWITCHING_PROTOCOLS = 101 HTTP_200_OK = 200 HTTP_201_CREATED = 201 HTTP_202_ACCEPTED = 202 HTTP_203_NON_AUTHORITATIVE_INFORMATION = 203 HTTP_204_NO_CONTENT = 204 HTTP_205_RESET_CONTENT = 205 HTTP_206_PARTIAL_CONTENT = 206 HTTP_207_MULTI_STATUS = 207 HTTP_208_ALREADY_REPORTED = 208 HTTP_226_IM_USED = 226 HTTP_300_MULTIPLE_CHOICES = 300 HTTP_301_MOVED_PERMANENTLY = 301 HTTP_302_FOUND = 302 HTTP_303_SEE_OTHER = 303 HTTP_304_NOT_MODIFIED = 304 HTTP_305_USE_PROXY = 305 HTTP_306_RESERVED = 306 HTTP_307_TEMPORARY_REDIRECT = 307 HTTP_308_PERMANENT_REDIRECT = 308 HTTP_400_BAD_REQUEST = 400 HTTP_401_UNAUTHORIZED = 401 HTTP_402_PAYMENT_REQUIRED = 402 HTTP_403_FORBIDDEN = 403 HTTP_404_NOT_FOUND = 404 HTTP_405_METHOD_NOT_ALLOWED = 405 HTTP_406_NOT_ACCEPTABLE = 406 HTTP_407_PROXY_AUTHENTICATION_REQUIRED = 407 HTTP_408_REQUEST_TIMEOUT = 408 HTTP_409_CONFLICT = 409 HTTP_410_GONE = 410 HTTP_411_LENGTH_REQUIRED = 411 HTTP_412_PRECONDITION_FAILED = 412 HTTP_413_REQUEST_ENTITY_TOO_LARGE = 413 HTTP_414_REQUEST_URI_TOO_LONG = 414 HTTP_415_UNSUPPORTED_MEDIA_TYPE = 415 HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE = 416 HTTP_417_EXPECTATION_FAILED = 417 HTTP_418_IM_A_TEAPOT = 418 HTTP_422_UNPROCESSABLE_ENTITY = 422 HTTP_423_LOCKED = 423 HTTP_424_FAILED_DEPENDENCY = 424 HTTP_426_UPGRADE_REQUIRED = 426 HTTP_428_PRECONDITION_REQUIRED = 428 HTTP_429_TOO_MANY_REQUESTS = 429 HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE = 431 HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS = 451 HTTP_500_INTERNAL_SERVER_ERROR = 500 HTTP_501_NOT_IMPLEMENTED = 501 HTTP_502_BAD_GATEWAY = 502 HTTP_503_SERVICE_UNAVAILABLE = 503 HTTP_504_GATEWAY_TIMEOUT = 504 HTTP_505_HTTP_VERSION_NOT_SUPPORTED = 505 HTTP_506_VARIANT_ALSO_NEGOTIATES = 506 HTTP_507_INSUFFICIENT_STORAGE = 507 HTTP_508_LOOP_DETECTED = 508 HTTP_509_BANDWIDTH_LIMIT_EXCEEDED = 509 HTTP_510_NOT_EXTENDED = 510 HTTP_511_NETWORK_AUTHENTICATION_REQUIRED = 511
Tip:响应状态码的两种书写方式
from rest_framework import status # 响应状态码方式一 return Response({'xx':'xxxxx'}, status=201) # 响应状态码方式二 return Response({'xx':'xxxxx'}, status=status.HTTP_201_CREATED)