本节大纲

  1、Generic Views

  2、ViewSets 

1、Generic Views

CBV的主要的一个优点就是极大的允许了对于代码的从用。自然,rest framework取其优势,提供了很多可以重构的视图。rest framework 提供的 Generic Views可以让你很快速的构建跟数据库模型映射紧密的API视图。

如果 generic view不满足你的API需求,很简单,你可以放弃它去使用正常的APIView类,或者将generic view内部包含的mixins和基础类重构成自己需要的,完成对于generic view的重用

样例

  1. class StudentListView(ListAPIView):
  2. queryset = Student.objects.all()
  3. serializer_class = StudentListSerializer
  4.  
  5. ###########################################serilizers,单独建立一个serializers文件
  6. from rest_framework.serializers import ModelSerializer
  7. from app01.models import Student
  8.  
  9. class StudentListSerializer(ModelSerializer):
  10.  
  11. class Meta:
  12. model = Student
  13. fields = ('name', 'age', 'gender', 'uuid')
  14.  
  15. ###########################################url
  16.  
  17. path('student/', StudentListView.as_view(), name='list-student'),

对于更复杂的需求,你可能需要重写各种各样的方法在你的视图类上,例如

  1. class StudentListView(ListAPIView):
  2. queryset = Student.objects.all()
  3. serializer_class = StudentListSerializer
  4.  
  5. def list(self, request, *args, **kwargs):
  6. queryset = self.get_queryset()
  7. serializer = StudentListSerializer(queryset, many=True)
  8. return Response(serializer.data)

如上、我们重写了list方法,这是查看一下源码会发现这是ListAPIView的get方法调用的方法。

对于一些简单的例子,你可以通过as_view()方法传入各种属性,例如

  1. path('student/', StudentListView.as_view(queryset = Student.objects.all(), serializer_class = StudentListSerializer), name='list-student'),

API Reference

GenericAPIView继承并扩展了APIView类,添加了很多对于标准的列表跟详细视图的常用的行为,其中每一个具体的generic views都是由GenericAPIView和其他一个或者多个mixin类组合而成。

Attributes

Basic settings

下面的属性控制这基础视图的行为。

1、queryset

用来返回一个视图的对象。你必须设置这个属性,或者是重写get_queryset()方法。如果你在重写一个视图方法,调用get_queryset()比直接用这个属性更好,因为queryset会一次获取整体的范围,这些数据将被缓存给各个随后的请求

2、serializer_class

序列化的类,用来验证,反序列化输入和序列化输出。同样的可以设置这个属性,或者是重写get_serializer_class()方法

3、lookup_field

这个模型字段应该被用来给对象查找单独的模型实例,默认是pk,如果url上的匹配也是pk,视图会自动筛选出主键值等于pk值的单独模型实例。

4、lookup_url_kwarg

URL关键字参数,可以被用来查找对象,url设置里面用该包含一个关键字对应的值,如果没有设置,默认使用lookup_field.

Pagination

接下来的方法主要是和list view相关的分页。

1、pagination_class

用来对list结果进行分页。可以在setting里面配置用'rest_framework.pagination.PageNumberPagination'来配置一个'DEFAULT_PAGINATION_CLASS'参数

Filtering

1、filter_backends

有很多可用的filter backend类可以用来对queryset进行筛选。默认可以再settings文件中设置DEFAULT_FILTER_BACKENDS

Methods

Base methods

1、get_query(self)

返回一个可以被list视图使用的queryset,并且作为后面查询具体视图的基础,默认返回queryset由queryset属性控制

  1. def get_queryset(self):
  2. user = self.request.user
  3. return user.accounts.all()

2、get_object(self)

返回一个给具体视图(detail view)使用的对象实例。默认用lookup_field参数来筛选基础的queryset,可以被重写来提供更复杂的行为。

  1. def get_object(self):
  2. queryset = self.get_queryset()
  3. filter = {}
  4. for field in self.multiple_lookup_fields:
  5. filter[field] = self.kwargs[field]
  6.  
  7. obj = get_object_or_404(queryset, **filter)
  8. self.check_object_permissions(self.request, obj)
  9. return obj

如果你的api没有权限认证,那可以直接丢弃掉最后一句self.check_object_permissions(self.request, obj)

3、filter_queryset

传入一个queryset,通过在使用的filter backends来筛选它,返回一个新的queryset

  1. def filter_queryset(self, queryset):
  2. filter_backends = (CategoryFilter,)
  3.  
  4. if 'geo_route' in self.request.query_params:
  5. filter_backends = (GeoRouteFilter, CategoryFilter)
  6. elif 'geo_point' in self.request.query_params:
  7. filter_backends = (GeoPointFilter, CategoryFilter)
  8.  
  9. for backend in list(filter_backends):
  10. queryset = backend().filter_queryset(self.request, queryset, view=self)
  11.  
  12. return queryset

4、get_serializer_class(self)

返回一个序列化的类,默认返回serializer_class属性也可以重写成动态行为

  1. def get_serializer_class(self):
  2. if self.request.user.is_staff:
  3. return FullAccountSerializer
  4. return BasicAccountSerializer

Save and deletion hooks(保存和删除的钩子)

下面这些方法是mixin类提供的,提供了对对象的保存和删除行为简易重写

.perform_create(self, serializer)       ==> 当保存一个新的实例对象时由CreateModelMixin发起

.perform_update(self, serializer)   ==> 当更新一个已有的实例对象时由UpdateModelmixin发起

.perform_destory(self, instance)   ==> 当删除一个实例对象时由DestoryModelMixin发起

当你需要的数据在request里面,但是不是request.data的一部分的时候,这些钩子就会变得特别有用,

比如你想记录request.user到实例里面

  1. def perform_create(self, serializer):
  2. serializer.save(user=self.request.user)

比如你想在保存操作的同时发送一封邮件出去

  1. def perform_update(self, serializer):
  2. instance = serializer.save()
  3. send_email_confirmation(user=self.request.user, modified=instance)

比如利用这些钩子提供额外的验证在数据库存储数据之前,并可以引发ValidationError()

  1. def perform_create(self, serializer):
  2. queryset = SignupRequest.objects.filter(user=self.request.user)
  3. if queryset.exists():
  4. raise ValidationError('You have already signed up')
  5. serializer.save(user=self.request.user)

注意,这些方法代替了2.X版本里面的pre_savepost_savepre_deletepost_delete方法,它们不再可用

在GenericAPIView里面,顺便跟下面的方法有个眼缘。。

  1. get_serializer_context(self) #返回一个字典包含任何额外的数据,需要添加到serializer里面的
  2.  
  3. get_serializer(self, instance=None, data=None, many=False, partial=False) # 返回一个serializer实例
  4.  
  5. get_paginated_response(self, data) # 返回一个分页格式的Response对象
  6.  
  7. paginate_queryset(self, queryset) # 对有需求的queryset进行分页,返回一个page对象或者空None,如果没有配置

Mixins

mixin类提供了一些是用来提供基础视图行为的措施。注意是mixin类提供的行为方法,而不是自定义处理方法,比如.get()或者.post(). 它允许更加灵活的组合行为

导入

  1. rest_framework.mixins

ListModelMixin

提供.list(request, *args, **kwargs)方法,正常会返回200 ok的返回和序列化的queryset作为body,返回的数据可以被分页。

CreateModelMixin

提供了.create(request, *args, **kwargs)方法,正常返回201 created返回和序列化的对象作为body. 如果返回的数据不合法,返回400和错误的详细作为body

RetrieveModelMixin

提供.Retrieve(request, *args, **kwargs)方法,正常返回200 ok和创建好的序列化实例. 否则返回404 Not Found.

UpdateModelMixin

提供.update(request, *args, **kwargs)方法,更新现有的实例。也提供了.partial_update(request, *args, **kwargs)方法,跟update很像,除了所有的更新字段都是可选的。允许支持patch请求。正常返回200 ok返回,序列化的对象。否则400 bad request.

DestoryModelMixin

.destory(request, *args, **kwargs)方法,正常204 No Content返回,否则404 Not Found.

Concrete View Classes

CreateAPIView

只创建;支持post方法处理;扩展:GenericAPIView, CreateModelMixin

ListAPIView

只读,展示一组模型实例;支持get方法处理;扩展:GenericAPIView, ListModelMixin

RetrieveAPIView

只读,展示一个单独的模型实例,支持get方法处理;扩展:GenericAPIView, RetrieveModelMixin

DestoryAPIView

只删除,针对单独的模型实例. 支持put和patch方法处理;扩展:GenericAPIView, UpdateModelMixin

UpdateAPIView

只更新,针对单独模型实例. 支持put和patch方法处理;扩展:GenericAPIView, UpdateModelMixin

ListCreateAPIView

读写,展示一组模型实例. get和post方法处理;扩展:GenericAPIView,ListModelMIxin,CreateModelMixin

RetrieveUpdateAPIView

读或者更新,展示一个独立的模型实例. get, put和patch方法处理;扩展:GenericAPIView, RetrieveModelMixin,UpdateModelMixin

RetrieveDestoryAPIView

读或删除,展示一个独立的模型实例. get和delete方法处理;扩展:GenericAPIView,RetrieveModelMixin, DestoryModelMIxin

RetrieveUpdateDestoryAPIView

读或者删除,展示一个独立的模型实现. get和delete方法处理;扩展:GenericAPIView,RetrieveModelMixin,DestoryModelMixin

RetrieveUpdateDestoryAPIView

读写删除,展示一个独立的模型实例. get, put, patch和delete方法处理;扩展:GenericAPIView,RetrieveMinxin,UpdateModelMixin,DestoryModelMixin

Customizing the generic views

Creating custom mixins

比如,需要查找对象基于多个url字段,你可以创建一个mixin类如下:

  1. class MultipleFieldLookupMixin(object):
  2. """
  3. Apply this mixin to any view or viewset to get multiple field filtering
  4. based on a `lookup_fields` attribute, instead of the default single field filtering.
  5. """
  6. def get_object(self):
  7. queryset = self.get_queryset() # Get the base queryset
  8. queryset = self.filter_queryset(queryset) # Apply any filter backends
  9. filter = {}
  10. for field in self.lookup_fields:
  11. if self.kwargs[field]: # Ignore empty fields.
  12. filter[field] = self.kwargs[field]
  13. obj = get_object_or_404(queryset, **filter) # Lookup the object
  14. self.check_object_permissions(self.request, obj)
  15. return obj

你可以简单得将这个mixin应用在view或者viewset,当你想需要应用这个客制化行为的任何时候

  1. class RetrieveUserView(MultipleFieldLookupMixin, generics.RetrieveAPIView):
  2. queryset = User.objects.all()
  3. serializer_class = UserSerializer
  4. lookup_fields = ('account', 'username')

Creating Custom Base Classes

如果你使用mixin链接多个视图,可以用下面的这种更进一步创建你自己的基础视图,贯穿你的项目

  1. class BaseRetrieveView(MultipleFieldLookupMixin,
  2. generics.RetrieveAPIView):
  3. pass
  4.  
  5. class BaseRetrieveUpdateDestroyView(MultipleFieldLookupMixin,
  6. generics.RetrieveUpdateDestroyAPIView):
  7. pass

Put As Create

3.0版本的rest framework mixins 对待put即是更新也是创建,主要依赖于你的项目是不是已经存在

2、ViewSets

首先需要记住的一点是viewset是一种简单的CBV,不提供任何处理方法比如.get()或者.post(),而是用.list()和.create()来替代,这种东西翻一下源码就知道了,当然熟悉了也就不需要特别记忆了。

还有一点值得注意的是相比于直接注册带有视图集在url设置里,使用路由类注册视图集将会是更好的选择,他将自动的为你决定url的设置,例如

  1. from rest_framework.generics import get_object_or_404
  2. from app01.serializers import PersonModelSerializer
  3. from rest_framework.viewsets import ModelViewSet, ViewSet
  4. from rest_framework.response import Response
  5. from app01.models import PersonResource
  6.  
  7. class StudentViewSet(ViewSet):
  8. def list(self, request):
  9. queryset = PersonResource.objects.filter(job=1)
  10. serializer = PersonModelSerializer(queryset, many=True)
  11. return Response(serializer.data)
  12.  
  13. def retrieve(self, request, pk=None):
  14. queryset = PersonResource.objects.filter(job=1)
  15. student = get_object_or_404(queryset, pk=pk)
  16. serializer = PersonModelSerializer(student)
  17. return Response(serializer.data)

如果需要的话,可以丙丁这个视图集到两个分开的视图,如下

  1. student_list = StudentViewSet.as_view({'get': 'list'})
  2. student_detail = StudentViewSet.as_view({'get': 'retrieve'})

但实际上不会这么做,而是注册一个路由绑定视图集来替代,这允许了url自动生成

  1. from rest_framework.routers import DefaultRouter
  2. from app01.customviewset import StudentViewSet
  3.  
  4. router = DefaultRouter()
  5. router.register(r'student', StudentViewSet, base_name='student-viewset')
  6.  
  7. urlpatterns = [
  8. ......
  9. ]
  10.  
  11. urlpatterns += router.urls

此时就已经借助ViewSet完成了2类API

  1. http://127.0.0.1:8001/api/student/
  2. http://127.0.0.1:8001/api/student/1/

下面截图可以看到具体提供的URL格式,带format的就不说了,看了应该懂,只看对目前有用的就行

在使用viewset来完成此API工作的时候,可以发现还是有很多重复的变量申明的,对于追求极致的API来说显然是不允许的,所以接下来修改成ModelViewSet

  1. class StudentViewSet(ModelViewSet):
  2. queryset = PersonResource.objects.filter(job=1)
  3. serializer_class = PersonModelSerializer

其实这个定义跟上面讲的封装的高级视图很相似。

ViewSet Actions

  1. class UserViewSet(viewsets.ViewSet):
  2. """
  3. Example empty viewset demonstrating the standard
  4. actions that will be handled by a router class.
  5.  
  6. If you're using format suffixes, make sure to also include
  7. the `format=None` keyword argument for each action.
  8. """
  9.  
  10. def list(self, request):
  11. pass
  12.  
  13. def create(self, request):
  14. pass
  15.  
  16. def retrieve(self, request, pk=None):
  17. pass
  18.  
  19. def update(self, request, pk=None):
  20. pass
  21.  
  22. def partial_update(self, request, pk=None):
  23. pass
  24.  
  25. def destroy(self, request, pk=None):
  26. pass

Introspecting ViewSet Actions

在dispatch的时候,下面的属性在视图集内是可用的

  1. basename - the base to use for the URL names that are created.
  2. action - the name of the current action (e.g., list, create).
  3. detail - boolean indicating if the current action is configured for a list or detail view.
  4. suffix - the display suffix for the viewset type - mirrors the detail attribute.

你可以注入下面的属性来调整当前操作的行为,比如限制权限

  1. def get_permissions(self):
  2. """
  3. Instantiates and returns the list of permissions that this view requires.
  4. """
  5. if self.action == 'list':
  6. permission_classes = [IsAuthenticated]
  7. else:
  8. permission_classes = [IsAdmin]
  9. return [permission() for permission in permission_classes]

Marking extra actions format routinng

如果你有特殊的方法应该是可路由的,你可以用@action装饰器来标记它们。与常规操作一样,额外的操作可以用于对象列表或单个实例。通过detail参数的True、False标示这个。通过DefaultRouter来配置action,包含pk在URL里面。

  1. class StudentViewSet(ModelViewSet):
  2. queryset = PersonResource.objects.filter(job=1)
  3. serializer_class = PersonModelSerializer
  4.  
  5. @action(methods=['post'], detail=True)
  6. def change_age(self, request, pk=None):
  7. student = self.get_object()
  8. student.age += 1
  9. student.save()
  10. return Response({'status': '%s age changed.' % student.name})
  11.  
  12. @action(detail=False)
  13. def ordering(self, request):
  14. student = PersonResource.objects.filter(job=1).order_by('modify_time')
  15.  
  16. page = self.paginate_queryset(student)
  17. if page is not None:
  18. serializer = self.get_serializer(page, many=True)
  19. return self.get_paginated_response(serializer.data)
  20.  
  21. serializer = self.get_serializer(student, many=True)
  22. return Response(serializer.data)

注意,我们一直使用的是DefaultRouter,而不是url里面自己定义。。当然上面的这种写法有点烦。。因为Detail=True的是只允许了post方法,可以改成get方法

可以随便打一个错误的url来看一下链接

id=1指向的是dandy,此时

也可以在装饰器里面添加额外的参数,这边就直接copy官方文档的了,随便看看

  1. @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf])
  2. def set_password(self, request, pk=None):
  3. ...

装饰器默认是get请求们也可以自己添加

  1. @action(methods=['post', 'delete'], detail=True)
  2. def unset_password(self, request, pk=None):
  3. ...

Reversing action URLS

本身正常情况下,在django里面,我们通过url里面的name进行url反转,再特殊一点,如果遇到两个一样的name,会根据命名空间appname来进行区别。而如果使用了viewset视图集,可以抛弃.reverse()方法,使用再次封装的.reverse_action()进行url反转。

  1. @action(methods=['get', 'put'], detail=True)
  2. def change_age(self, request, pk=None):
  3. student = self.get_object()
  4. student.age += 1
  5. student.save()
  6. return Response({
  7. 'status': '%s age changed.' % student.name,
  8. 'url': self.reverse_action('change-age', args=[pk])
  9. })

注意 反转的basename其实就是action装饰器的函数名,尤其是当函数名跟上面的实例一样,有下划线_的时候,需要转换成-,别掉进坑。。

或者有一种更简单的方法

  1. @action(methods=['get', 'put'], detail=True)
  2. def change_age(self, request, pk=None):
  3. student = self.get_object()
  4. student.age += 1
  5. student.save()
  6. return Response({
  7. 'status': '%s age changed.' % student.name,
  8. 'url': self.reverse_action('change-age', args=[pk]),
  9. 'url1': self.reverse_action(self.change_age.url_name, args=[pk])
  10. })

API Reference

ViewSet

ViewSet继承了APIView,你可以使用APIView的提供的任何标准的属性,比如permission_classesauthentication_classes...

视图集类不能提供任何执行操作,为了使用视图集类,你可以直白地重写类或者定义执行操作。

GenericViewSet

GenericViewSet继承自GenericAPIView,包含get_object(), get_queryset()方法和其他generic view基础行为,默认不包含操作。同样地,为了使用GenericViewSet类,你需要直白的重写类无论是mixin还是自定义执行操作。

ModelViewSet

继承自GenericAPIView,包含各种执行操作,源自各种各样的mixin类。

ModelViewSet提供:.list(), .retrieve(), .create(), .update(), .partial_update()和.destory()

因为是扩展了GenericAPIView,所以有很多属性可以使用,下面的几个示例都不是很重要,直接copy的官方文档

  1. class AccountViewSet(viewsets.ModelViewSet):
  2. """
  3. A simple ViewSet for viewing and editing accounts.
  4. """
  5. queryset = Account.objects.all()
  6. serializer_class = AccountSerializer
  7. permission_classes = [IsAccountAdminOrReadOnly]

或者同样的跟GenericAPIView的其他扩展类一样。

  1. class AccountViewSet(viewsets.ModelViewSet):
  2. """
  3. A simple ViewSet for viewing and editing the accounts
  4. associated with the user.
  5. """
  6. serializer_class = AccountSerializer
  7. permission_classes = [IsAccountAdminOrReadOnly]
  8.  
  9. def get_queryset(self):
  10. return self.request.user.accounts.all()

ReadOnlyModelViewSet

继承自GenericAPIView,但是从名字就可以猜到read-only,所以支持.list(), .retrieve()方法

  1. class AccountViewSet(viewsets.ReadOnlyModelViewSet):
  2. """
  3. A simple ViewSet for viewing accounts.
  4. """
  5. queryset = Account.objects.all()
  6. serializer_class = AccountSerializer

Custome ViewSet base classes

有时你需要提供客制化的视图集类,而ModelViewSet并没有满足,就需要自定制。

  1. from rest_framework import mixins
  2.  
  3. class CreateListRetrieveViewSet(mixins.CreateModelMixin,
  4. mixins.ListModelMixin,
  5. mixins.RetrieveModelMixin,
  6. viewsets.GenericViewSet):
  7. """
  8. A viewset that provides `retrieve`, `create`, and `list` actions.
  9.  
  10. To use it, override the class and set the `.queryset` and
  11. `.serializer_class` attributes.
  12. """
  13. pass

这里,我们讲GenericView跟ViewSet放在了一起,因为这其中有很多很多相似的地方,如果每一个都看示例,翻源码就很好理解了,如果只是一笔看过,可能会混淆掉其中的内容,当然,如果真正做到看rest framework这一块,相信在这个领域已经不算是小白了,源码随便看看应该是可以的。

Django REST Framework API Guide 02的更多相关文章

  1. Django REST Framework API Guide 01

    之前按照REST Framework官方文档提供的简介写了一系列的简单的介绍博客,说白了就是翻译了一下简介,而且翻译的很烂.到真正的生产时,就会发现很鸡肋,连熟悉大概知道rest framework都 ...

  2. Django REST Framework API Guide 03

    本节大纲 1.Routers 2.Parsers 3.Renderers Routers Usage from rest_framework import routers router = route ...

  3. Django REST Framework API Guide 08

    1.Filtering 2.Pagination FIltering GenericAPIView的子类筛选queryset的简单方法是重写.get_quueryset()方法. 1.根据当前用户进行 ...

  4. Django REST Framework API Guide 06

    本节大纲 1.Validators 2.Authentication Validators 在REST框架中处理验证的大多数时间,您将仅仅依赖于缺省字段验证,或在序列化器或字段类上编写显式验证方法.但 ...

  5. Django REST Framework API Guide 04

    本节大纲 1.serializers 1.Serializers Serializers允许复杂的数据,像queryset和模型实例转换成源生的Python数据类型.从而可以更简单的被渲染成JSON, ...

  6. Django REST Framework API Guide 07

    本节大纲 1.Permissions 2.Throttling Permissions 权限是用来授权或者拒绝用户访问API的不同部分的不同的类的.基础的权限划分 1.IsAuthenticated ...

  7. Django REST Framework API Guide 05

    本节大纲 1.Serializer fields 2.Serializer relations Serializer fields 1.serializer 字段定义在fields.py文件内 2.导 ...

  8. Django Rest Framework API指南

    Django Rest Framework API指南 Django Rest Framework 所有API如下: Request 请求 Response 响应 View 视图 Generic vi ...

  9. tastypie Django REST framework API [Hello JSON]

    tastypie is a good thing. Haven't test it thoroughly. Gonna need some provement. Now I will introduc ...

随机推荐

  1. 关于Java中扫描仪next()与nextLine()的区别

    首先,next()一定要读取到有效字符后才可以结束输入,对输入有效字符之前遇到的空格键.Tab键或Enter键等结束符,next()方法会自动将其去掉,只有在输入有效字符之后,next()方法才将其后 ...

  2. MySQL事务,事务隔离级别详解

    1.什么是事务 指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行. 2.事务的4个特性 原子性(Atomicity).一致性(Consistency).隔离性(Isolatio ...

  3. Html的简单学习笔记

    1.Html简介 1)什么是html: HyperText Markup Language:超文本标记语言,网页语言. >超文本:超出文本范围. >标记: html中所有的操作都是使用标记 ...

  4. PHP-FIG - PHP 标准规范

    转自:https://psr.phphub.org/ PHP 标准规范 PSR 是 PHP Standard Recommendations 的简写,由 PHP FIG 组织制定的 PHP 规范,是 ...

  5. centos7下安装pip以及mysql等软件

    1.安装pip 安装失败了的提示: No package pip available.Error: Nothing to do 解决方法: 需要先安装扩展源EPEL. EPEL(http://fedo ...

  6. 2Servlet笔记

    1.静态页面(html) 2.动态 用户可以输入数据,和页面交互. 3 BS和CS的区别 BS :Browser Server(WEB 服务器)   CS : Client(qq tcp/ip协议) ...

  7. docker swarm 简易版

    节点名称 相关服务 ip地址 master1/node1 swarm manager(master) / consul 192.168.132.131 master2/node2 swarm mana ...

  8. IIS 为应用程序池提供服务的进程在与 Windows Process Activation Service 通信时出现严重错误的解决方法

    系统环境:Windows Server 2008 R2 64位, IIS 7.0 错误信息: 为应用程序池提供服务的进程在与 Windows Process Activation Service 通信 ...

  9. ipython介绍及使用

    1. IPython介绍 ipython是一个python的交互式shell,比默认的python shell好用得多,支持变量自动补全,自动缩进,支持bash shell命令,内置了许多很有用的功能 ...

  10. Part-Four

    1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12.