1.ViewSet
提示:本节中每个函数都来使用as.allow()中的键值,来就行函数的调用。
通过一张图片大致看出上一节学习的内容。
继承自APIView 与 ViewSetMixin作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。
ViewSet主要通过继承ViewSetMixin来实现在调用as_view()时传入字典(如{'get':'list'})的映射处理工作。
在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。
必须有的视图级中的 get方法获取一条,或者get方法获取多条 put方法 ,delete方法、POST增加一条数据。
可以自己在视图中就行自定制。
说白了,都是模仿怎么在继承ViewSet,怎么使用视图集,即是在调用方法的方法的时候,使用
as_view({"get": "get_5"})),中的键去调用方法。也即是模仿上一节中的最后一个终极方法。上节终极代码最后继承的是视图集:
1.1 复习一下之前的逻辑
提示:本节中每个函数都来使用as.allow()中的键值,来就行函数的调用。
# 优点1:方法可以自定制,可以扩展函数,调用函数即是对应键值中的值 "get": 函数名,即是get方法的时候调用该方法。 优点2:可能使用比较方便,主要是扩展性好。
在ser下面的views.py中视图函数: class Student11GenericAPIView(ModelViewSet): queryset = Student.objects.all() serializer_class = StudentModelSerializer 路由中的代码: # 视图集的使用 path("student7/", views.Student11GenericAPIView.as_view({"get": "list", "post": "create"})), re_path("^student7/(?P<pk>\d+)/$", views.Student11GenericAPIView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})), GET /req/student4/ # 获取全部数据 POST /req/student4/ # 添加数据 GET /req/student4/(?P<pk>\d+)/ # 获取一条数据 PUT /req/student4/(?P<pk>\d+)/ # 更新一条数据 DELETE /req/student4/(?P<pk>\d+)/ # 删除一条数据 # 11.继承到GenericAPIView类的时候,只是部分获取值的发生改变 #1. student_obj = self.get_object()(以get方法中增加一条数据示例) # 2.serializer = self.get_serializer(instance=student_obj) # 22. 然后在使用GenericAPIView结合视图Mixin扩展类之后一切都发生更简单的,上面的5种访问都有了在类中的封装,这个时候我们只需要写写函数名不用写实际的代码。 class Student7GenericAPIView(GenericAPIView, ListModelMixin, CreateModelMixin): def get(self, reqeust): return self.list(reqeust) # 33从22 之后更是更近一步的疯狂. 将之前继承的GenericAPIView, ListModelMixin可以写成一个。 class Student9GenericAPIView(ListAPIView, CreateAPIView): 那么我们连上面的最基本的函数可以不用写了。 class Student10GenericAPIView(RetrieveUpdateDestroyAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer # 44直到最后 # class Student11GenericAPIView(GenericViewSet, ListModelMixin, CreateModelMixin, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin): 等价于这个, class Student11GenericAPIView(ModelViewSet): 到了#44这一步之后视图中视图中的访问变成了这样, # 视图集的使用 path("student7/", views.Student11GenericAPIView.as_view({"get": "list", "post": "create"})), re_path("^student7/(?P<pk>\d+)/$", views.Student11GenericAPIView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})), 于是我们开始研究都在视图中调用as_allow()中的键就行访问。于是开始了本节的内容。 看看从基础的开始往这个方向写写!
附图:上一节代码中url中的代码: 都是分有参数就行访问和没有参数就行访问。
1.有参数就行访问的时候,是Get就行获取指定的一条数据;POST拿着pk值去修改一个数据;delete拿着pk值去删除一条数据。
2.没有参数的时候,GET是就行获取多条数据,POST创建一条数据。
from django.urls import path, re_path from . import views urlpatterns = [ # 区分View与APIView path("student1/", views.Student1View.as_view()), path("student2/", views.Student2APIView.as_view()), # 使用APIView实现接口 path("student3/", views.Student3APIView.as_view()), re_path("^student3/(?P<pk>\d+)/$", views.Student4APIView.as_view()), # 使用GenericAPIView实现接口 path("student4/", views.Student5GenericAPIView.as_view()), re_path("^student4/(?P<pk>\d+)/$", views.Student6GenericAPIView.as_view()), # 使用GenericAPIView 结合mixin的扩展类 实现接口 path("student5/", views.Student7GenericAPIView.as_view()), re_path("^student5/(?P<pk>\d+)/$", views.Student8GenericAPIView.as_view()), # 使用内置的扩展子类,生成API接口 path("student6/", views.Student9GenericAPIView.as_view()), re_path("^student6/(?P<pk>\d+)/$", views.Student10GenericAPIView.as_view()), # 视图集的使用 path("student7/", views.Student11GenericAPIView.as_view({"get": "list", "post": "create"})), re_path("^student7/(?P<pk>\d+)/$", views.Student11GenericAPIView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})), ]
ViewSet中代码部分:
urls中的代码:
# # # 不要在同一个路由的as_view中书写两个同样的键的http请求,会产生覆盖!!! # ViewSet # 定义一个get的自定义方法 path("student1/", views.Student1ViewSet.as_view({"get": "get_5"})), # 如果自定义的其他方法也是get请求的话,重新写一个url去访问这个 path("student1/get_girl/", views.Student1ViewSet.as_view({"get": "get_5_girl"})), re_path(r"^student1/(?P<pk>\d+)/$", views.Student1ViewSet.as_view({"get": "get_one"})),
对应的视图中的函数代码:
# 除了内置的增删改查还可以自定如下: class Student1ViewSet(ViewSet): def get_5(self, request): """获取5条数据""" student_list = Student.objects.all()[:5] serializer = StudentModelSerializer(instance=student_list, many=True) return Response(serializer.data) def get_one(self, request, pk): """获取一条数据""" student_obj = Student.objects.get(pk=pk) serializer = StudentModelSerializer(instance=student_obj) return Response(serializer.data) def get_5_girl(self, request): """获取5条女孩数据""" student_list = Student.objects.filter(sex=False)[:5] serializer = StudentModelSerializer(instance=student_list, many=True) return Response(serializer.data)
小结:其实和前一节中的代码写法一样,只是使用视图集之后可以在as.allow()中的字典上添加自定制方法,就行访问,更灵活!
2.GenericViewSet
即是之前没有继承视图集时候,继承的GenericAPIView加上视图集之后的写法。可以指定制吧! 无论之前的哪一个低级的类,到这里想要使用视图集都是要继承ViewSetMixin才能在as_view()时候,使用键就行调用函数。
GenericViewSet就帮助我们完成了这样的继承工作,继承自GenericAPIView与ViewSetMixin,在实现了调用as_view()时传入字典(如{'get':'list'})的映射处理工作的同时,
还提供了GenericAPIView提供的基础方法,可以直接搭配Mixin扩展类使用。
视图中的代码:
过渡版本:
# 这个视图类,没有对应的路由信息.这个只是参考过渡版本. class Student2ViewSet(ViewSet, GenericAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer def get_5(self, request): """获取5条数据""" student_list = self.get_queryset()[:5] serializer = self.get_serializer(instance=student_list, many=True) return Response(serializer.data) def get_one(self, request, pk): """获取一条数据""" student_obj = self.get_object() serializer = self.get_serializer(instance=student_obj) return Response(serializer.data) def get_5_girl(self, request): """获取5条女孩数据""" student_list = self.get_queryset().filter(sex=False)[:5] serializer = self.get_serializer(instance=student_list, many=True) return Response(serializer.data)
上面的方式,虽然实现视图集中调用GenericAPIView,但是我们要多了一些类的继承。所以我们可以直接继承 GenericViewSet
GenericViewSet代码:
视图中的代码: 视图中2个方法都是get所以将url分开写! 都是写的制定制的。不是制定的,可以参考前面一节的代码:
# GenericViewSet # 下面的方法需要自己去写 path('student2/', views.Student3ViewSet.as_view({"get": "get_5"})), path('student2/get_girl/', views.Student3ViewSet.as_view({"get": "get_5_girl"})),
视图函数中的代码:
from rest_framework.viewsets import GenericViewSet class Student3ViewSet(GenericViewSet): queryset = Student.objects.all() serializer_class = StudentModelSerializer def get_5(self, request): """获取5条数据""" student_list = self.get_queryset()[:5] serializer = self.get_serializer(instance=student_list, many=True) return Response(serializer.data) def get_5_girl(self, request): """获取5条女孩数据""" student_list = self.get_queryset().filter(sex=False)[:5] serializer = self.get_serializer(instance=student_list, many=True) return Response(serializer.data)
小结:在使用GenericViewSet时,虽然已经提供了基本调用数据集(queryset)和序列化器属性,但是我们要编写一些基本的API时,还是需要调用DRF提供的模型扩展类 [Mixins]
3.继承GenericViewSet扩展类模型之后
urls.py中的代码:
# GenericViewSet,可以和模型类进行组合快速生成基本的API接口 # 这里利用扩展类之后就不用写方法了 直接快速的就行 path('student3/', views.Student4ViewSet.as_view({"get": "list", "post": "create"})), # ModelViewSet 默认提供了5个API接口 path('student4/', views.Student5ViewSet.as_view({"get": "list", "post": "create"})), re_path(r"^student4/(?P<pk>\d+)/$", views.Student5ViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),
视图函数中代码:
class Student4ViewSet(GenericViewSet, ListModelMixin, CreateModelMixin): queryset = Student.objects.all() serializer_class = StudentModelSerializer
4.ModelViewSet
注意:即是上一篇退出的终极版本。
视图函数中的代码:
from rest_framework.viewsets import ModelViewSet class Student5ViewSet(ModelViewSet): queryset = Student.objects.all() serializer_class = StudentModelSerializer
urls.py中的代码:
# ModelViewSet 默认提供了5个API接口 path('student4/', views.Student5ViewSet.as_view({"get": "list", "post": "create"})), re_path(r"^student4/(?P<pk>\d+)/$", views.Student5ViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),
5.ReadOnlyModelViewSet
视图函数中的代码:
from rest_framework.viewsets import ReadOnlyModelViewSet class Student6ViewSet(ReadOnlyModelViewSet): queryset = Student.objects.all() serializer_class = StudentModelSerializer
urls中的代码:
对应的方法只有获取多条和获取一条的方法。
# ReadOnlyModelViewSet path('student5/', views.Student6ViewSet.as_view({'get': 'list'})), re_path(r"^student5/(?P<pk>\d+)/$", views.Student6ViewSet.as_view({"get": "retrieve"})),
6.一个视图类调用多个序列化器类
urls.py中的代码:
# 一个视图类调用多个序列化器类个人理解一般针对get path("student8/", views.Student8GenericAPIView.as_view()),
视图函数中的代码:
重写get_serializer_class中的方法,但是这种视图类中去加这个方法会显得,条件不够,只要在get一条和多条的时候有用.于是出现了在一个视图集中调用多个序列化器。
""在多个视图类合并成一个视图类以后,那么有时候会出现一个类中需要调用多个序列化器""" """1. 在视图类中调用多个序列化器""" """原来的视图类中基本上一个视图类只会调用一个序列化器,当然也有可能要调用多个序列化器""" from .serializers import StudentInfoModelSerializer class Student8GenericAPIView(GenericAPIView): queryset = Student.objects.all() # GenericAPI内部调用序列化器的方法,我们可以重写这个方法来实现根据不同的需求来调用不同的序列化器 # 这种方式只能是get才能返回不同的,self.request.method == "GET": def get_serializer_class(self): if self.request.method == "GET": return StudentInfoModelSerializer return StudentModelSerializer def get(self, request): """获取所有数据的id和name""" student_list = self.get_queryset() serializer = self.get_serializer(instance=student_list, many=True) return Response(serializer.data) def post(self, request): """添加数据""" data = request.data serializer = self.get_serializer(data=data) serializer.is_valid(raise_exception=True) serializer.save() return Response(serializer.data)
7.视图集中调用多个序列化器
弥补上面那种只能是get请求方式, 以判断方法的方式就行,调用多个学序列化器。
视图函数中的代码:
class Student9GenericAPIView(ModelViewSet): queryset = Student.objects.all() """要求: 列表数据list,返回2个字段, 详情数据retrieve,返回所有字段, """ # 通过self.action 你给我不同的方法我给你返回对应的序列化器类。 def get_serializer_class(self): # print(self.action) # 当前请求要执行的方法名称 if self.action == "list": return StudentInfoModelSerializer return StudentModelSerializer
urls.py中的代码:
# 一个视图集调用多个序列化器类,针对不同的方法不只是get path("student9/", views.Student9GenericAPIView.as_view({"get": "list"})), re_path(r"^student9/(?P<pk>\d+)/$", views.Student9GenericAPIView.as_view({"get": "retrieve"})),
8.路由类就行代码简单化
有了视图集以后,视图文件中多个视图类可以合并成一个,但是,路由的代码就变得复杂了,
需要我们经常在as_view方法 ,编写http请求和视图方法的对应关系,
事实上,在路由中,DRF也提供了一个路由类给我们对路由的代码进行简写。
当然,这个路由类仅针对于 视图集 才可以使用。
视图中的代码:
# 路由 from rest_framework.decorators import action class Student7ViewSet(ModelViewSet): queryset = Student.objects.all() serializer_class = StudentModelSerializer # 这2条即是自带的带参数不带参数的都是可以访问到的!全部生成路由信息,自行访问。4种模式都是自行生成创建的。下面的urls中可以看到! # 自己也可以指定方法,在MdelViewSet中 通过装饰器 如果detail = True的话就默认需要自定的该方法需要参数pk # 访问的时候加上方法名称 http://127.0.0.1:8000/col/student7/5/get_6/ # methods 指定允许哪些http请求访问当前视图方法 # detail 指定生成的路由地址中是否要夹带pk值,True为需要 # @action(methods=['get', "post"], detail=False) # def get_6(self, request): # < URLPattern # '^student7/get_6/$'[name = 'student-get-6'] >, # < URLPattern # '^student7/get_6\.(?P<format>[a-z0-9]+)/?$'[name = 'student-get-6'] >, @action(methods=['get', "post"], detail=True) def get_6(self, request, pk): # 写上pk值即是没哟写死!!! # < URLPattern # '^student7/(?P<pk>[^/.]+)/get_6/$'[name = 'student-get-6'] >, # < URLPattern # '^student7/(?P<pk>[^/.]+)/get_6\.(?P<format>[a-z0-9]+)/?$'[name = 'student-get-6'] >, # 在就行访问这种带有参数的的url时候,前面会直接拼接出 访问的url的格式,这里是要传入pk参数,那么访问这方法的url是 student_obj = self.get_queryset().get(pk=pk) serilizer = self.get_serializer(instance=student_obj) return Response(serilizer.data)
url中的代码:
from rest_framework.routers import DefaultRouter, SimpleRouter # 实例化router对象 router = DefaultRouter() # 会生成api-root # router = SimpleRouter() # router.register("访问地址前缀","视图集类","访问别名[可选]") # 注册视图视图集类 router.register("student7", views.Student7ViewSet) # url后缀不要加/ print(router.urls) # router = DefaultRouter() # 会生成api-root # [<URLPattern '^student7/$' [name='student-list']>, # <URLPattern '^student7\.(?P<format>[a-z0-9]+)/?$' [name='student-list']>, # <URLPattern '^student7/(?P<pk>[^/.]+)/$' [name='student-detail']>, # <URLPattern '^student7/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$' [name='student-detail']>, # <URLPattern '^$' [name='api-root']>, # <URLPattern '^\.(?P<format>[a-z0-9]+)/?$' [name='api-root']>] # # router = SimpleRouter() # [<URLPattern '^student7/$' [name='student-list']>, # <URLPattern '^student7/(?P<pk>[^/.]+)/$' [name='student-detail']>] # 把路由列表注册到django项目中 urlpatterns += router.urls # print(urlpatterns) # [<URLPattern 'student1/'>, <URLPattern 'student1/get_girl/'>, # <URLPattern '^student1/(?P<pk>\d+)/$'>, <URLPattern 'student2/'>, # <URLPattern 'student2/get_girl/'>, <URLPattern 'student3/'>, # <URLPattern 'student4/'>, <URLPattern '^student4/(?P<pk>\d+)/$'>, # <URLPattern 'student5/'>, <URLPattern '^student5/(?P<pk>\d+)/$'>, # <URLPattern '^student7/$' [name='student-list']>, # <URLPattern '^student7\.(?P<format>[a-z0-9]+)/?$' [name='student-list']>, # <URLPattern '^student7/(?P<pk>[^/.]+)/$' [name='student-detail']>, # <URLPattern '^student7/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$' [name='student-detail']>, # <URLPattern '^$' [name='api-root']>, <URLPattern '^\.(?P<format>[a-z0-9]+)/?$' [name='api-root']>]
使用这个
router = DefaultRouter() # 会生成api-root会生成一个api如下:
如果使用:# router = SimpleRouter() 生成简单路由的话,没APi接口如下:
- col/ ^student7/(?P<pk>[^/.]+)/$ [name='student-detail']
9.扩展功能
在测试60 opt子app下:
玩之前创建一个超级用户:
本处创建的是:longer:123456
使用127.0.0.1:8000/admin/ 使用longer用户进入。
再次创建一个用户。勾选下面的状态,在另外一个平台就行登录,发现,都是可以登录的查看的。
ergou:longer123
当我们在longer 用户登录的下面就行修改ergou用户的管理员状态之后:
ergou用户去登录访问,那2个网站,会出现auth2不能访问。因为我们把管理用资格给ergou用户取消了。
1. 认证Authentication
urls中的代码:
from django.urls import path from opt import views urlpatterns = [ path('auth1/', views.Demo1APIView.as_view()), path('auth2/', views.Demo2APIView.as_view()),
视图中的代码:
"""用户的认证和权限识别""" class Demo1APIView(APIView): """只允许登录后的用户访问""" permission_classes = [IsAuthenticated] def get(self, request): """个人中心""" return Response("个人中心") class Demo2APIView(APIView): """只允许管理员访问""" permission_classes = [IsAdminUser] def get(self, request): """个人中心2""" return Response("个人中心2")
2.
# 自定义权限 from rest_framework.permissions import BasePermission class MyPermission(BasePermission): def has_permission(self, request, view): """ 针对访问视图进行权限判断 :param request: 本次操作的http请求对象 :param view: 本次访问路由对应的视图对象 :return: """ if request.user.username == "ergou": return True return False class Demo3APIView(APIView): permission_classes = [MyPermission] def get(self, request): """个人中心3""" return Response("个人中心3")
url中的代码:
# 自定义权限 path('auth3/', views.Demo3APIView.as_view()),
运行结果:代码中只是给ergou用户给与了查看个人中心3的权限那么,就算是超级管理员longer也无法查看。
ergou用户就行查看:
3. 限流Throttling
视图中的代码:
# 限流 from rest_framework.throttling import UserRateThrottle, AnonRateThrottle class Demo4APIView(APIView): throttle_classes = [UserRateThrottle, AnonRateThrottle] # 全局配置后,这里就不用指定 def get(self, request): """投票页面""" return Response("投票页面")
在setting中的设置:
REST_FRAMEWORK = { # 限流 # 'DEFAULT_THROTTLE_CLASSES': ( # 对全局进行设置 # 'rest_framework.throttling.AnonRateThrottle', # 'rest_framework.throttling.UserRateThrottle' # ), 'DEFAULT_THROTTLE_RATES': { 'anon': '3/hour', 'user': '3/minute', } }
urls.py中的代码:
# 限流 path('auth4/', views.Demo4APIView.as_view()),
运行结果:
正常用户:
第一次投票:
投票三次之后:
匿名用户:
将当前longer用户退出,然后就行访问。
第一次和上面一样,
三次后会发现和我们设置的一样。
4. 过滤Filtering
视图中的代码:
# 过滤 from rest_framework.generics import GenericAPIView, ListAPIView from app01.models import Student from .serializers import StudentModelSerializer from django_filters.rest_framework import DjangoFilterBackend class Demo5APIView(ListAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer filter_backends = [DjangoFilterBackend] # 全局配置后,这里就不用指定了。 filter_fields = ['age', "id"] # 声明过滤字段
urls.py中的代码:
# 过滤 path('data5/', views.Demo5APIView.as_view()),
5. 排序Ordering
视图中的代码:
# 排序 from rest_framework.filters import OrderingFilter class Demo6APIView(ListAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer filter_backends = [DjangoFilterBackend, OrderingFilter] # 局部配置会覆盖全局配置 filter_fields = ['id', "age", "sex"] ordering_fields = ['id', "age"]
urls.py中的代码:
# 排序 path('data6/', views.Demo6APIView.as_view()),
升序:
降序:
6. 分页Pagination
视图中的代码:
# 分页 from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination """1. 自定义分页器,定制分页的相关配置""" """ # 页码分页 PageNumberPagination 前端访问形式:GET http://127.0.0.1:8000/opt/data7/?page=4 page=1 limit 0,10 page=2 limit 10,20 # 偏移量分页 LimitOffsetPagination 前端访问形式:GET http://127.0.0.1:8000/opt/data7/?start=4&size=3 start=0 limit 0,10 start=10 limit 10,10 start=20 limit 20,10 """ class StandardPageNumberPagination(PageNumberPagination): """分页相关配置""" page_query_param = "page" # 设置分页页码关键字名 page_size = 3 # 设置每页显示数据条数 page_size_query_param = "size" # 设置指定每页大小的关键字名 max_page_size = 5 # 设置每页显示最大值 class StandardLimitOffsetPagination(LimitOffsetPagination): default_limit = 2 # 默认限制,默认值与PAGE_SIZE设置一致 limit_query_param = "size" # limit参数名 offset_query_param = "start" # offset参数名 max_limit = 5 # 最大limit限制 class Demo7APIView(ListAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer # 分页 # 页码分页类 pagination_class = StandardPageNumberPagination # 偏移量分页类 # pagination_class = StandardLimitOffsetPagination
urls.py中的代码:
# 分页 path('data7/', views.Demo7APIView.as_view()),
参考博客代码:https://www.cnblogs.com/Michael--chen/p/11241840.html