1.反序列化类校验部分源码分析(了解)
1.当我们在视图类中生成一个序列化类对象ser,并且用ser.is_valid()是就会执行校验,校验通过返回True,不通过返回False。首先对象ser和序列化类中都没有方法is_valid()。
然后我们去其父类Serializer中找,也没找到,再去其父类BaseSerializer中找,找到了方法is_valid():
2.关键语句在于:self._validated_data = self.run_validation(self.initial_data)
self是序列化类BookSerializer的对象,从头开始找方法run_validation。此时我们不能按ctrl点run_validation方法,因为这样找到的是其父类fields中的run_validation。但是在BookSerializer对的父类Serializer中就可以找到名字run_validation。
3.可以发现在异常捕获中看到了我们之前见过的全局钩子名字:validate。所以当我们用ser点is_valid时会触发全局钩子进行校验。
4.value = self.to_internal_value(data)这一步是在进行局部钩子的校验。还是重头开始查找名字to_internal_value,最终在类Serializer中找到了名字to_internal_value:
def to_internal_value(self, data):
# fields是一个个字段名对象,field是字段名对象
for field in fields:
# 如果对象ser或其父类(BookSerializer)中有'validate_' + field.field_name的名字,例如validate_name,那么此时validate_method就是例如validate_name。
validate_method = getattr(self, 'validate_' + field.field_name, None)
try:
validated_value = field.run_validation(primitive_value)
if validate_method is not None:
# validate_method是validate_name,validated_value就是待校验的某个字段名,这也是为什么我们在局部钩子要上传字段名。
validated_value = validate_method(validated_value)
except ValidationError as exc:
# 如果有错误将每个字段名对应的错误添加到错误信息当中。
errors[field.field_name] = exc.detail
except DjangoValidationError as exc:
errors[field.field_name] = get_error_detail(exc)
except SkipField:
pass
else:
set_value(ret, field.source_attrs, validated_value)
if errors:
raise ValidationError(errors)
return ret
"""
面向对象中名字的查找顺序:每一次对象查找名字都要从自身的名称空间中查找,然后再到类名称空间,再到父类名称空间。
"""
2.断言
源码中大量使用try和断言:
assert后面跟的语句如果成立,就会执行下面的语句;不成立则会马上抛出异常。但是异常信息无法编辑。
assert isinstance(111,int)
print('111是整形') # 111是整形
assert isinstance(111,str)
print('111是整形') # 报错:AssertionError
3.drf请求
1.Request能够解析的前端传入的编码格式
如果我们只想接收前端发送的json格式的数据,不接受其他形式发送的数据,我们需要做如下设置:
方式一:局部配置:在继承自APIView及其子类的视图类中配置
from rest_framework.parsers import JSONParser,FormParser,MultiPartParser
class BookView(APIView):
parser_classes = [JSONParser,]
def post(self,request):
print(request.data)
ser = BookSerializer(data=request.data)
if ser.is_valid():
ser.save()
return Response({'code':100,'msg':'新增成功'})
else:
return Response({'code':101,'msg':ser.errors})
方式二:全局配置:在settings中设置,会影响全局,如果想禁用掉哪个提交方式,只需要注掉就可以。
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': [
# 'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
# 'rest_framework.parsers.MultiPartParser',
],
}
"""
如果我们在全局配置了一种,部分视图函数需要三种,我们只需要在视图函数配置三种发送方式。因为配置查找顺序是:先从视图类自身查找,再到drf配置中找,再去drf默认配置中查找。
"""
2.Request类中的方法:
data,__getattr__,query_params
4.drf响应
drf响应,如果使用浏览器会好看一些,使用postman只能拿到json数据。
方式一:在视图类中写(局部配置)
-两个响应类---》找---》drf的配置文件中找--》两个类
-from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer
class BookView(APIView):
renderer_classes=[JSONRenderer,]
方式二:在项目配置文件中写(全局配置)
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
],
}
"""
优先使用视图类中的配置,其次使用项目匹配,最后使用那个内置的配置。
"""
5.Response源码属性或方法
当我们进到Response源码中发现有以下参数:
def __init__(self, data=None, status=None,
template_name=None, headers=None,
exception=False, content_type=None):
data:是我们返回给前端的数据,可以是字典、列表、字符串。我们之前写的ser.data就是data的内容。
status:响应状态码,默认是200,可以手动定义数字,也可以通过导模块来使用drf内部定义的响应状态码:
template_name:了解即可,修改响应模板的样子
content_type:响应编码格式,一般不动
headers:响应头
"""
在Http四件套中如何添加响应头?
def indexfunc(request):
obj = HttpResponse('hhh') 先生成一个HttpResponse的对象
obj['msg'] = 'success' 给对象添加键值对
return obj
"""
6.视图组件介绍及两个视图基类
1.APIView跟之前的View区别
传入到视图方法中的是REST framework的Request对象,而不是Django的HttpRequeset对象;
视图方法可以返回REST framework的Response对象-
任何APIException异常都会被捕获到,并且处理成合适的响应信息;
在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制
2.APIView类属性:
renderer_classes # 响应格式
parser_classes #能够解析的请求格式
authentication_classes#认证类
throttle_classes#频率类
permission_classes#权限类
3.APIView+ModelSerializer+Resposne写5个接口:
serializer.py:
from rest_framework import serializers
from app01.models import Book
from rest_framework.exceptions import ValidationError
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['name','price','publish','authors','publish_detail','author_list']
extra_kwargs = {
'name':{'max_length':8},
'publish_detail':{'read_only':True},
'author_list':{'read_only':True},
'publish':{'write_only':True},
'authors':{'write_only':True}
}
def validate_name(self,name):
if name.startswith('s'):
raise ValidationError('书名不能以s开头')
else:
return name
models.py:
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.CharField(max_length=32)
publish = models.ForeignKey(to='Publish',on_delete=models.CASCADE)
authors = models.ManyToManyField(to='Author')
@property
def publish_detail(self):
return {'name':self.publish.name,'addr':self.publish.addr}
@property
def author_list(self):
l = []
for author_obj in self.authors.all():
l.append({'name':author_obj.name,'phone':author_obj.phone})
return l
class Publish(models.Model):
name = models.CharField(max_length=32)
addr = models.CharField(max_length=32)
class Author(models.Model):
name = models.CharField(max_length=32)
phone = models.CharField(max_length=11)
views.py:
from rest_framework.views import APIView
from rest_framework.response import Response
from app01.models import Book
from .serializer import BookSerializer
class BookView(APIView):
def get(self,request):
book_queryset = Book.objects.all()
ser = BookSerializer(instance=book_queryset,many=True)
return Response(ser.data)
def post(self,request):
ser = BookSerializer(data=request.data)
if ser.is_valid():
ser.save()
return Response({'code':100,'msg':'新增成功','result':ser.data})
else:
return Response({'code':101,'msg':ser.errors})
class BookDetailView(APIView):
def get(self,request,pk):
book_obj = Book.objects.filter(pk=pk).first()
ser = BookSerializer(instance=book_obj)
return Response(ser.data)
def put(self,request,pk):
book_obj = Book.objects.filter(pk=pk).first()
ser = BookSerializer(instance=book_obj,data=request.data)
if ser.is_valid():
ser.save()
return Response({'code':100,'msg':'修改成功','result':ser.data})
else:
return Response({'code':101,'msg':ser.errors})
def delete(self,request,pk):
Book.objects.filter(pk=pk).delete()
return Response({'code':100,'msg':'删除成功'})
urls.py:
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('books/',views.BookView.as_view()),
path('books/<int:pk>/',views.BookDetailView.as_view())
]
7.基于GenericAPIView写接口
1.GenericAPIView两个重要的类属性:
queryset:序列化或反序列化指定的queryset,需要指定
serializer_class:序列化类需要指定
lookup_field:查询单条的路由通过转换器分出来的字段名
filter_backends:过滤器的配置
pagination_class:分页器的配置
2.方法:
get_queryset:获取序列化的对象
get_object:获取单个对象,查找时不需要在括号内上传参数pk
get_seriializer:获取序列化类(和它差不多的:get_serializer_class:一般重写它,不调用它)
filter_queryset:过滤器有关
3.代码:
models.py:(和之前没变)
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.CharField(max_length=32)
publish = models.ForeignKey(to='Publish',on_delete=models.CASCADE)
authors = models.ManyToManyField(to='Author')
@property
def publish_detail(self):
return {'name':self.publish.name,'addr':self.publish.addr}
@property
def author_list(self):
l = []
for author_obj in self.authors.all():
l.append({'name':author_obj.name,'phone':author_obj.phone})
return l
class Publish(models.Model):
name = models.CharField(max_length=32)
addr = models.CharField(max_length=32)
class Author(models.Model):
name = models.CharField(max_length=32)
phone = models.CharField(max_length=11)
serializer.py:(和之前没变)
from rest_framework import serializers
from app01.models import Book
from rest_framework.exceptions import ValidationError
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['name','price','publish','authors','publish_detail','author_list']
extra_kwargs = {
'name':{'max_length':8},
'publish_detail':{'read_only':True},
'author_list':{'read_only':True},
'publish':{'write_only':True},
'authors':{'write_only':True}
}
def validate_name(self,name):
if name.startswith('s'):
raise ValidationError('书名不能以s开头')
else:
return name
views.py:
rom rest_framework.response import Response
from app01.models import Book
from .serializer import BookSerializer
from rest_framework.generics import GenericAPIView
class BookView(GenericAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
def get(self, request):
# 在这里不能直接self.queryset,而是要用drf提供的get_queryset()方法,objs拿到的还是类中的queryset
objs = self.get_queryset()
# 序列化类也要用指定的方法
ser = self.get_serializer(instance=objs, many=True)
return Response(ser.data)
def post(self, request):
ser = self.get_queryset(data=request.data)
if ser.is_valid():
ser.save()
return Response({'code':100,'msg':'新增成功','result':ser.data})
else:
return Response({'code':101,'msg':ser.errors})
from rest_framework.mixins import ListModelMixin
class BookDetailView(GenericAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
def get(self,request,pk):
obj = self.get_object()
ser = self.get_serializer(instance=obj)
return Response(ser.data)
def put(self,request,pk):
obj = self.get_object()
ser = self.get_serializer(instance=obj,data=request.data )
if ser.is_valid():
ser.save()
return Response({'code':100,'msg':'修改成功','result':ser.data})
else:
return Response({'code':101,'msg':ser.errors})
def delete(self,request,pk):
self.get_object().delete()
return Response({'code':100,'msg':'删除成功'})
"""
到这一步我们再写其他的表,只需要改变类中:
queryset = Book.objects.all()
serializer_class = BookSerializer
即可
"""
8.基于GenericAPIView+5个视图扩展类
针对接口中5个不同的功能,drf提供了5个不同的类:CreateModelMixin,UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin,ListModelMixin。这些类封装了5个功能中重复的代码。
urls.py:
urlpatterns = [
path('admin/', admin.site.urls),
path('books/',views.BookView.as_view()),
path('books/<int:pk>/',views.BookDetailView.as_view())
]
views.py:
from app01.models import Book
from .serializer import BookSerializer
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import CreateModelMixin,UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin,ListModelMixin
class BookView(GenericAPIView,ListModelMixin,CreateModelMixin):
queryset = Book.objects.all()
serializer_class = BookSerializer
def get(self,request):
return self.list(request)
def post(self,request):
return self.create(request)
class BookDetailView(GenericAPIView,UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin):
queryset = Book.objects.all()
serializer_class = BookSerializer
def get(self,request,*args,**kwargs):
return self.retrieve(request,*args,**kwargs)
def put(self,request,*args,**kwargs):
return self.update(request,*args,**kwargs)
def delete(self,request,*args,**kwargs):
return self.destroy(request,*args,**kwargs)
serializer.py:
from rest_framework import serializers
from app01.models import Book
from rest_framework.exceptions import ValidationError
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['name','price','publish','authors','publish_detail','author_list']
extra_kwargs = {
'name':{'max_length':8},
'publish_detail':{'read_only':True},
'author_list':{'read_only':True},
'publish':{'write_only':True},
'authors':{'write_only':True}
}
def validate_name(self,name):
if name.startswith('s'):
raise ValidationError('书名不能以s开头')
else:
return name
标签:GenericAPIView,return,ser,models,self,视图,import,drf,name
From: https://www.cnblogs.com/zkz0206/p/17090649.html