第四章 restframework——视图组件
第四章 restframework——视图组件
一、基本视图
二、mixin模块+generics模块(GenericAPIView类)编写视图
三、generics模块下的ListCreateAPIView,RetrieveUpdateDestroyAPIView
四、viewsets模块下的ModelViewSet
一、基本视图
回顾上一章节的序列化组件,你会发现views里的代码逻辑部分大同小异
代码如下:
from django.db import models # Create your models here. from django.db import models # Create your models here. class Book(models.Model): title=models.CharField(max_length=32) price=models.IntegerField() pub_date=models.DateField() publish=models.ForeignKey("Publish") authors=models.ManyToManyField("Author") def __str__(self): return self.title class Publish(models.Model): name=models.CharField(max_length=32) email=models.EmailField() # def __str__(self): # return self.name class Author(models.Model): name=models.CharField(max_length=32) age=models.IntegerField() def __str__(self): return self.name
urls.py
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^publishes/$',views.PublishView.as_view()), url(r'^publishes/(?P<pk>\d+)/$',views.PublishDetailView.as_view(),name='publish_detail'), url(r'^books/$',views.BookView.as_view()), url(r'^books/(\d+)/$',views.BookDetailView.as_view()) ]
from django.shortcuts import * from django.views import View from .models import * import json # Create your views here. from rest_framework.views import APIView from rest_framework.response import Response from .all_serializers import * # class BookSerializers(serializers.Serializer): # title = serializers.CharField(max_length=32) # price = serializers.IntegerField() # pub_date = serializers.DateField() # publish = serializers.CharField(source="publish.name") # # authors=serializers.CharField(source="authors.all") #这样会返回queryset对象,看起来还不是特别舒服 # authors = serializers.SerializerMethodField() # 这个方法只是为多对多服务的,下面要写类似钩子的方法 # def get_authors(self,obj): # temp = [] # for obj in obj.authors.all(): # temp.append(obj.name) # return temp class BookView(APIView): def get(self,request): # get请求数据 book_list = Book.objects.all() bs = BookModelSerializers(book_list,many=True) ''' 序列化BookSerializers(book_list,many=True)做了如下操作: temp = [] for obj in book_list: temp.append({ "title":obj.title, "price":obj.price, "pub_date":obj.pub_date, "publish":obj.publish, # 这里的返回值就是上面get_authors的返回值 }) ''' return Response(bs.data) def post(self,request): # post请求数据 bs = BookModelSerializers(data=request.data) if bs.is_valid(): print(bs.validated_data) bs.save() # create方法 return Response(bs.data) else: return HttpResponse(bs.errors) class BookDetailView(APIView): def get(self,request,id): book = Book.objects.filter(pk=id).first() bs = BookModelSerializers(book) return Response(bs.data) def put(self,request,id): book = Book.objects.filter(pk=id).first() # 因为是做更新操作,所以要写data=request.data,把新数据放进去 bs = BookModelSerializers(book,data=request.data) if bs.is_valid(): bs.save() return Response(bs.data) else: return Response(bs.errors) def delete(self,request,id): Book.objects.filter(pk=id).delete() # 删除操作返回空即可 return Response() class PublishView(APIView): def get(self, request): publish_list = Publish.objects.all() ps = PublishModelSerializers(publish_list, many=True) return Response(ps.data) # 取数据 # print('restframework', request.data) # print('restframework type', type(request.data)) # print('restframework', request._request.GET) # print('restframework type', type(request._request.GET)) # print('restframework', request.GET) # print('restframework type', type(request.GET)) # return HttpResponse('ok') # 序列化 # publish_list = Publish.objects.all() #queryset对象 # pb = PublishSerializers(publish_list, many=True) # many = true 代表前面传入的参数是queryset对象 # print(pb.data) # publish1 = Publish.objects.filter(pk=1).first() # print(publish1) # publish对象 # pb = PublishSerializers(publish1,many=False) # PublishSerializers(model_obj,many = false) # many = false 代表前面传入的参数是model对象 # 默认many = false,所以如果是model对象可以不用写many # print(pb.data) # return HttpResponse(pb.data) # 方式一 # publish_list = list(Publish.objects.all().values('name','email')) # return HttpResponse(json.dumps(publish_list)) # 方式二 # from django.forms.models import model_to_dict # publish_list = Publish.objects.all() # temp = [] # for obj in publish_list: # temp.append(model_to_dict(obj)) # return HttpResponse(json.dumps(temp)) # 方式三 # from django.core import serializers # publish_list = Publish.objects.all() # temp = serializers.serialize("json",publish_list) # return HttpResponse(temp) def post(self, request): # 取数据 # 原生的request操作 # print('POST',request.POST) # print('body',request.body) # print(type(request)) # from django.core.handlers.wsgi import WSGIRequest # print(request.data) # 新的request支持的操作 # 要想使用之前request的方法 # ret = request._request.POST # django将request的所有方法的数据获取进行了不同的封装 # 而restframework将request下POST方法的数据封装成request.data # print('restframework',request.data) # print('restframework type',type(request.data)) # return HttpResponse('post') ps = PublishModelSerializers(data=request.data) if ps.is_valid(): print(ps.validated_data) ps.save() # create方法 return Response(ps.data) else: return Response(ps.errors) class PublishDetailView(APIView): def get(self, request, pk): publish = Publish.objects.filter(pk=pk).first() ps = PublishModelSerializers(publish) return Response(ps.data) def put(self, request, pk): publish = Publish.objects.filter(pk=pk).first() ps = PublishModelSerializers(publish, data=request.data) if ps.is_valid(): ps.save() return Response(ps.data) else: return Response(ps.errors) def delete(self, request, pk): Publish.objects.filter(pk=pk).delete() return Response()
from rest_framework import serializers from .models import * # 为queryset,model对象做序列化 # class PublishSerializers(serializers.Serializer): # # 下面写的字段取决于你需要哪些字段做序列化 # name = serializers.CharField() # email = serializers.CharField() class PublishModelSerializers(serializers.ModelSerializer): class Meta: model=Publish fields="__all__" class BookModelSerializers(serializers.ModelSerializer): class Meta: model = Book # 表名 fields = "__all__" # 默认把所有字段写入,不包括自己写的get_authors方法 # field = ['nid','title','authors','publish'] # 指定字段 # exclude = ('nid') # 除了...字段,不能跟fields同时用 # depth = 1 #深度控制,写 几 往里拿几层,层数越多,响应越慢,官方建议0--10之间,个人建议最多3层 # 如果要指定一对多,或多对多显示的字段需要重写create方法 publish = serializers.CharField(source='publish.pk') authors = serializers.CharField(source="authors.all") def create(self, validated_data): # print('validated_data',validated_data) book = Book.objects.create( title=validated_data['title'], price=validated_data['price'], pub_date=validated_data['pub_date'], publish_id=validated_data['publish']['pk'] ) book.authors.add(*validated_data['authors']) return book
二、mixin模块+generics模块(GenericAPIView类)编写视图
尝试用mixin类写一个AuthorView,对比下它的简便。
【mixin模块】
导入模块
from rest_framework import mixins
先简单熟悉下mixin模块下的几个class,它对应了restful的规范
查看下ListModeMixin的源码,与之前写的get方法对应起来看下
查看下CreateModeMixin的源码,与之前写的get方法对应起来看下
其他的将不再做对比,你会发现其实restframework替我们做了功能封装避免重复代码,那我们用mixin尝试写AuthorView看看
urls.py
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^authors/$',views.AuthorView.as_view(),name='authors'), url(r'^authors/(?P<pk>\d+)/$',views.AuthorDetailView.as_view(),name='author_detail'), url(r'^publishes/$',views.PublishView.as_view(),name='publishes'), url(r'^publishes/(?P<pk>\d+)/$',views.PublishDetailView.as_view(),name='publish_detail'), url(r'^books/$',views.BookView.as_view(),name='books'), url(r'^books/(\d+)/$',views.BookDetailView.as_view(),name='book_detail'), ]
all_serializers.py
class AuthorModelSerializers(serializers.ModelSerializer): class Meta: model = Author fields = "__all__"
【generics模块】
导入模块
from rest_framework import generics
views.py
注意:
①类必须继承generics.GenericAPIView
②切记:queryset和serializer_class 这两个名字不能改,固定写法,否则会出现如下报错
AssertionError: 'AuthorView' should either include a `queryset` attribute, or override the `get_queryset()` method.
from rest_framework import mixins from rest_framework import generics class AuthorView( mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView ): # GenericAPIView继承了之前的APIView # !!!切记:queryset和serializer_class 这两个名字不能改!!! queryset = Author.objects.all() serializer_class = AuthorModelSerializers def get(self,request,*args,**kwargs): return self.list(request,*args,**kwargs) def post(self,request,*args,**kwargs): return self.create(request,*args,**kwargs) class AuthorDetailView( mixins.RetrieveModelMixin, mixins.DestroyModelMixin, mixins.UpdateModelMixin, generics.GenericAPIView ): queryset = Author.objects.all() serializer_class = AuthorModelSerializers 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)
四、generics模块下的ListCreateAPIView,RetrieveUpdateDestroyAPIView
上面我们看了mixin模块及generics模块的确节省了大量代码,可是还是存在重复代码,此时我们再来看下generics模块下的类们。
通过使用mixin类,我们使用更少的代码重写了这些视图,但我们还可以再进一步。REST框架提供了一组已经混合好(mixed-in)的通用视图,我们可以使用它来简化我们的views.py
模块。
还是用author举例:
导入模块
from rest_framework import generics
views.py
class AuthorView(generics.ListCreateAPIView): queryset = Author.objects.all() serializer_class = AuthorModelSerializers class AuthorDetailView(generics.RetrieveUpdateDestroyAPIView): queryset = Author.objects.all() serializer_class = AuthorModelSerializers
对比下看看
五、viewsets模块下的ModelViewSet
像上面所述,还是有重复代码,别急,还有更简便的。
记住核心在于,利用字典的形式指定请求方法(get、post...)的操作(list,create....)
导入模块
from rest_framework import viewsets
urls.py
urlpatterns = [ url(r'^admin/', admin.site.urls), # url(r'^authors/$',views.AuthorView.as_view(),name='authors'), # url(r'^authors/(?P<pk>\d+)/$',views.AuthorDetailView.as_view(),name='author_detail'), url(r'^authors/$',views.AuthorModelView.as_view({"get":"list","post":"create"}),name='authors'), url(r'^authors/(?P<pk>\d+)/$',views.AuthorModelView.as_view({ 'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy' }),name='author_detail'), url(r'^publishes/$',views.PublishView.as_view(),name='publishes'), url(r'^publishes/(?P<pk>\d+)/$',views.PublishDetailView.as_view(),name='publish_detail'), url(r'^books/$',views.BookView.as_view(),name='books'), url(r'^books/(\d+)/$',views.BookDetailView.as_view(),name='book_detail'), ]
views.py
from rest_framework import viewsets class AuthorModelView(viewsets.ModelViewSet): queryset = Author.objects.all() serializer_class = AuthorModelSerializers
如果你想用自己方法来处理请求,只需自己重写函数,并更改路由即可