2:Django-DRF-视图
视图
一:DRF中的request以及response
DRF中传入视图的request对象 不再是Django默认的HttpRequest对象,而是REST framework提供的扩展了HttpRequest类的Request类的对象。
DRF中传入视图的response对象 REST framework提供了一个响应类Response,使用该类构造响应对象时,响应的具体数据内容会被转换(render渲染)成符合前端需求的类型。
添加配置文件
REST_FRAMEWORK = {
# 默认响应渲染类
'DEFAULT_RENDERER_CLASSES':
(
'rest_framework.renderers.JSONRenderer', # json渲染器
'rest_framework.renderers.BrowsableAPIRenderer', # 浏览API渲染器
)
}
二:CBV视图和FBV视图的区别
CBV(class base views) 就是在视图里使用类处理请求。
Python是一个面向对象的编程语言,如果只用函数来开发,有很多面向对象的优点就错失了(继承、封装、多态)。所以Django在后来加入了Class-Based-View。可以让我们用类写View。这样做的优点主要下面两种:
提高了代码的复用性,可以使用面向对象的技术,比如Mixin(多继承)
可以用不同的函数针对不同的HTTP方法处理,而不是通过很多if判断,提高代码可读性
FBV视图:
如果我们要写一个处理GET方法的view,用函数写的话是下面这样。
from django.http import HttpResponse
def my_view(request):
if request.method == 'GET':
return HttpResponse('OK')
CBV视图:
如果用class-based view写的话,就是下面这样
from django.http import HttpResponse
from django.views import View
class MyView(View):
def get(self, request):
return HttpResponse('OK')
Django的url是将一个请求分配给可调用的函数,而不是一个class类。针对这个问题,class-based view提供了一个as_view()静态方法(也就是类方法),调用这个方法,会创建一个类的实例,然后通过实例调用dispatch()方法进行路由分发,dispatch()方法会根据request的method方法的不同调用来找到类中相应的方法来处理request(如get() , post()等)。到这里,这些方法和function-based view差不多了,要接收request,得到一个response返回。如果方法没有定义,会抛出HttpResponseNotAllowed异常。
CBV路由:
在url中,就这么写:
# urls.py
from django.conf.urls import url
from myapp.views import MyView
urlpatterns = [
url(r'^index/$', MyView.as_view()),
]
三:类的属性的设置
第一种是常见的Python的方法,可以被子类覆盖。
from django.http import HttpResponse
from django.views import View
class GreetingView(View):
# 设置类属性
name = "yuan"
def get(self, request):
return HttpResponse(self.name)
# You can override that in a subclass
# 继承了之前定义的类属性 name被覆盖了
class MorningGreetingView(GreetingView):
name= "alex"
第二种方法,url中指定类的属性:
类中必须存在此类才可以指定属性值
urlpatterns = [
url(r'^index/$', GreetingView.as_view(name="egon")),
]
四:类视图的继承(重点)
一、APIview(推荐):
APIView是REST framework提供的所有视图的基类,继承自Django的View
父类
APIView与View的不同之处在于:
传入到视图方法中的是REST framework的Request对象,而不是Django的HttpRequeset对象;
视图方法可以返回REST framework的Response对象,视图会为响应数据设置(render)符合前端要求的格式;
任何APIException异常都会被捕获到,并且处理成合适的响应信息;
在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制。
支持定义的属性:
- authentication_classes 列表或元祖,身份认证类
- permissoin_classes 列表或元祖,权限检查类
- throttle_classes 列表或元祖,流量控制类
在APIView
中仍和普通的类视图定义方法来实现get() 、post() 或者其他请求方式的方法。
class BookListView(APIView):
"""使用APIView实现“获取所有图书信息”接口"""
def get(self, request):
"""
获取所有图书信息
GET /books/
:param request: Request类型的对象
:return: JSON
"""
qs = BookInfo.objects.all() # 查询要序列化的模型数据
serializer = BookInfoSerializer(qs, many=True) # 准备序列化器:将模型数据转字典数据
return Response(serializer.data) # 响应序列化的结果
def post(self, request):
pass
优缺点:
优点:适合自定制发开
缺点:没有继承太多的功能,需要自己手动或开发
二、GenericAPIView
GenericAPIView在django rest framework中根据APIView又做了一层封装。
则继承关系就变成了GenericAPIView——>APIView——>View
这里由于封装的作用,GenericAPIView的内部的一些方法会根据会自动的获取数据库查询结果并分页和序列化。
定义的方法:
- get_queryset(): 会获取数据库查询结果,也就是queryset
- paginate_queryset(roles):会根据重写的pagination_class属性获取分页类进行分页操作,另外如果这里没有重写,会自动到settings.py配置文件去找
- self.get_serializer():就是根据分页后的对象进行数据的序列化,也会先找到序列化类
from rest_framework.generics import GenericAPIView
class GenericView(GenericAPIView):
"""
继承于GenericAPIView
"""
# 获取数据库的查询结果
queryset = Role.objects.get_queryset().order_by('id')
# 序列化器
serializer_class = PagerSerializer()
# 分页器
pagination_class = PageNumberPagination()
def get(self,request,*args,**kwargs):
# 数据库查询结果赋值给roles
roles = self.get_queryset()
# 对数据库的查询结果进行分页
pager_roles = self.paginate_queryset(roles)
# 对分页的数据进行序列化吗,多条数据 many=True 单挑则Flase
ser = self.get_serializer(instance=pager_roles,many=True)
# 通过DRF封装的Response返回序列化的数据
return Response(ser.data)
我们发现使用了GenericAPIView没有特别大的优化,别急,下边的GenericAPIView扩展类要大显身手了
我们发现前边的两个父类还需要进行函数的定义去处理不同的请求,这样我们还得在函数里边实现查询集以及序列化器的指定,工作量仍然很大。怎么解决呢?
GenericAPIView的五个扩展类给我们提供了五个方法分别进行增删改查的不同操作,这样我们就不用写那么多函数啦!!
五个扩展类(为啥是5个?答:增删改查,查有两个)
搭配GenericAPIView使用五种方法
1.ListModelMixin: 提供list方法快速实现列表视图
2.CreateModelMixin: 提供create方法快速实现创建资源的视图
3.RetrieveModelMixin 提供retrieve方法,可以快速实现返回一个存在的数据对象(需要传入pk)
4.UpdateModelMixin 提供update方法,可以快速实现更新一个存在的数据对象。 提供partial_update方法,可以实现局部更新
5.DestroyModelMixin 提供destroy方法,可以快速实现删除一个存在的数据对象
class BookDetailView(mixins.RetrieveModelMixin, GenericAPIView):
"""使用GenericAPIView实现“获取单一图书信息”接口"""
serializer_class = BookInfoSerializer # 指定序列化器
queryset = BookInfo.objects.all() # 指定查询集
def get(self, request, pk):
"""
获取单一图书信息
GET /books/<pk>/
:param request: Request类型的对象
:return: JSON
"""
# 返回了单挑的图书数据
return self.retrieve(request)
看到没有,函数里边只有一行代码了,简化了吧!明显看得出来,这个父类帮我们吧return之前的事情都做了
GenericAPIView搭配扩展类的使用的优势就显示出来了
优化:(推荐)
1.CreateAPIView(等价于GenericAPIView+CreateModelMixin) 提供 post 方法 继承自: GenericAPIView、CreateModelMixin
2.ListAPIView 提供 get 方法 继承自:GenericAPIView、ListModelMixin
3.RetrieveAPIView 提供 get 方法 继承自: GenericAPIView、RetrieveModelMixin
4.DestoryAPIView 提供 delete 方法 继承自:GenericAPIView、DestoryModelMixin
5.UpdateAPIView 提供 put 和 patch 方法 继承自:GenericAPIView、UpdateModelMixin
6.RetrieveUpdateAPIView 提供 get、put、patch方法 继承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin
7.RetrieveUpdateDestoryAPIView 提供 get、put、patch、delete方法 继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin
所以代码中的mixins.RetrieveModelMixin, GenericAPIView可以写成RetrieveAPIView
ListAPIView:获取列表数据
CreateAPIView:提交数据
RetrieveAPIView:获取单条数据
UpdateAPIView:更新全局或者局部数据
DestroyAPIView:删除数据 真删
GenericAPIView 是桥梁,以后使用继承于GenericAPIView 的 ListAPIView等则不需要写get等方法了
from rest_framework.generics import ListAPIView,\
CreateAPIView,UpdateAPIView,\
RetrieveAPIView,DestroyAPIView
class NewsView(ListAPIView):
# 获取数据
queryset = models.News.objects.all()
# 使用筛选
filter_backends = [NewsBaseFilterBackend,]
# 使用序列化
serializer_class = NewsSerializer
# 使用分页
pagination_class = PageNumberPagination
# 不用自己写方法内部帮我们实现了 只需要去配置取值即可
实际使用:
- ListAPIView,RetrieveAPIView 在同一个视图里只能使用一个 因为都是使用get方法
- 如果要两个都是用需要再写一个视图
- 还可以在内部重写get方法等进行自定义不适用类提供的方法
- 还可以写自定义钩子方法
class TagSer(serializers.ModelSerializer):
class Meta:
model = models.Tag
fields = "__all__"
class TagView(ListAPIView,CreateAPIView):
# 数据库查询集
queryset = models.Tag.objects.all()
# 序列化器
serializer_class = TagSer
# 钩子方法 如果是get请求返回TagSer postOtherTagSer
def get_serializer_class(self):
# self.request
# self.args
# self.kwargs
if self.request.method == 'GET':
return TagSer
elif self.request.method == 'POST':
return OtherTagSer
# 钩子方法 进行数据存储的时候 author=1
def perform_create(self,serializer):
serializer.save(author=1)
class TagDetailView(RetrieveAPIView,UpdateAPIView,DestroyAPIView):
queryset = models.Tag.objects.all()
serializer_class = TagSer
三、ViewSet(不推荐)
前边的父类仍需要写很多函数,这些函数定义仍然增加我们的工作量
ViewSet:将几个操作放在一起(前边的方法都是分开实现的)
list() 提供一组数据
retrieve() 提供单个数据
create() 创建数据
update() 保存数据
destory() 删除数据
ViewSet主要通过继承ViewSetMixin来实现在调用as_view()时传入字典(如{'get':'list'})的映射处理工作。
在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。
ViewSet视图集类不再实现get()、post()等方法,而是实现动作 action 如 list() 、create() 等。
class BookInfoViewSet(viewsets.ViewSet):
# 定义了list方法
def list(self, request):
books = BookInfo.objects.all()
serializer = BookInfoSerializer(books, many=True)
return Response(serializer.data)
# retrieve方法
def retrieve(self, request, pk=None):
try:
books = BookInfo.objects.get(id=pk)
except BookInfo.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = BookInfoSerializer(books)
return Response(serializer.data)
视图集只在使用as_view()方法的时候,才会将action动作与具体请求方式对应上
urls:
url(r'^books/$', BookInfoViewSet.as_view({'get':'list'}),
url(r'^books/(?P<pk>\d+)/$', BookInfoViewSet.as_view({'get': 'retrieve'})
四:GenericViewSet(不推荐)
使用ViewSet通常并不方便,因为list、retrieve、create、update、destory等方法都需要自己编写,而这些方法与前面讲过的Mixin扩展类提供的方法同名,所以我们可以通过继承Mixin扩展类来复用这些方法而无需自己编写。但是Mixin扩展类依赖与GenericAPIView,所以还需要继承GenericAPIView。
class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
urls:
url(r'^books/$', views.BookInfoViewSet.as_view({'get': 'list'})),
url(r'^books/(?P<pk>\d+)/$', views.BookInfoViewSet.as_view({'get': 'retrieve'}))
五、ModelViewSet(推荐)
继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。
比GenericViewSet优化的更多
class BookInfoViewSet(ModelViewSet):
queryset = BookInfo.objects.all()
serializer_class = BookInfoAllSerializer
六、ReadOnlyModelViewSet(不推荐)
继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin
class BookInfoViewSet(ReadOnlyModelViewSet):
"""使用视图集实现返回列表和单一数据"""
# 指定序列化器
serializer_class = BookInfoSerializer
# 指定查询集
queryset = BookInfo.objects.all()
# detail为False 表示路径名格式应该为 books/latest/
@action(methods=['get'], detail=False)
def latest(self, request):
"""
自定义action: 返回最新的图书信息
GET /books/latest/
"""
# latest : 先将数据根据'id'倒叙,再取第0个
book = BookInfo.objects.latest('id')
serializer = self.get_serializer(book)
return Response(serializer.data)