本节大纲

  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的重用

样例

class StudentListView(ListAPIView):
queryset = Student.objects.all()
serializer_class = StudentListSerializer ###########################################serilizers,单独建立一个serializers文件
from rest_framework.serializers import ModelSerializer
from app01.models import Student class StudentListSerializer(ModelSerializer): class Meta:
model = Student
fields = ('name', 'age', 'gender', 'uuid') ###########################################url path('student/', StudentListView.as_view(), name='list-student'),

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

class StudentListView(ListAPIView):
queryset = Student.objects.all()
serializer_class = StudentListSerializer def list(self, request, *args, **kwargs):
queryset = self.get_queryset()
serializer = StudentListSerializer(queryset, many=True)
return Response(serializer.data)

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

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

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属性控制

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

2、get_object(self)

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

def get_object(self):
queryset = self.get_queryset()
filter = {}
for field in self.multiple_lookup_fields:
filter[field] = self.kwargs[field] obj = get_object_or_404(queryset, **filter)
self.check_object_permissions(self.request, obj)
return obj

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

3、filter_queryset

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

def filter_queryset(self, queryset):
filter_backends = (CategoryFilter,) if 'geo_route' in self.request.query_params:
filter_backends = (GeoRouteFilter, CategoryFilter)
elif 'geo_point' in self.request.query_params:
filter_backends = (GeoPointFilter, CategoryFilter) for backend in list(filter_backends):
queryset = backend().filter_queryset(self.request, queryset, view=self) return queryset

4、get_serializer_class(self)

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

def get_serializer_class(self):
if self.request.user.is_staff:
return FullAccountSerializer
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到实例里面

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

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

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

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

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

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

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

get_serializer_context(self)  #返回一个字典包含任何额外的数据,需要添加到serializer里面的

get_serializer(self, instance=None, data=None, many=False, partial=False)  # 返回一个serializer实例

get_paginated_response(self, data)  # 返回一个分页格式的Response对象

paginate_queryset(self, queryset)  # 对有需求的queryset进行分页,返回一个page对象或者空None,如果没有配置

Mixins

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

导入

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类如下:

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

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

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

Creating Custom Base Classes

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

class BaseRetrieveView(MultipleFieldLookupMixin,
generics.RetrieveAPIView):
pass class BaseRetrieveUpdateDestroyView(MultipleFieldLookupMixin,
generics.RetrieveUpdateDestroyAPIView):
pass

Put As Create

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

2、ViewSets

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

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

from rest_framework.generics import get_object_or_404
from app01.serializers import PersonModelSerializer
from rest_framework.viewsets import ModelViewSet, ViewSet
from rest_framework.response import Response
from app01.models import PersonResource class StudentViewSet(ViewSet):
def list(self, request):
queryset = PersonResource.objects.filter(job=1)
serializer = PersonModelSerializer(queryset, many=True)
return Response(serializer.data) def retrieve(self, request, pk=None):
queryset = PersonResource.objects.filter(job=1)
student = get_object_or_404(queryset, pk=pk)
serializer = PersonModelSerializer(student)
return Response(serializer.data)

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

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

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

from rest_framework.routers import DefaultRouter
from app01.customviewset import StudentViewSet router = DefaultRouter()
router.register(r'student', StudentViewSet, base_name='student-viewset') urlpatterns = [
......
] urlpatterns += router.urls

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

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

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

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

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

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

ViewSet Actions

class UserViewSet(viewsets.ViewSet):
"""
Example empty viewset demonstrating the standard
actions that will be handled by a router class. If you're using format suffixes, make sure to also include
the `format=None` keyword argument for each action.
""" def list(self, request):
pass def create(self, request):
pass def retrieve(self, request, pk=None):
pass def update(self, request, pk=None):
pass def partial_update(self, request, pk=None):
pass def destroy(self, request, pk=None):
pass

Introspecting ViewSet Actions

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

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

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

def get_permissions(self):
"""
Instantiates and returns the list of permissions that this view requires.
"""
if self.action == 'list':
permission_classes = [IsAuthenticated]
else:
permission_classes = [IsAdmin]
return [permission() for permission in permission_classes]

Marking extra actions format routinng

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

class StudentViewSet(ModelViewSet):
queryset = PersonResource.objects.filter(job=1)
serializer_class = PersonModelSerializer @action(methods=['post'], detail=True)
def change_age(self, request, pk=None):
student = self.get_object()
student.age += 1
student.save()
return Response({'status': '%s age changed.' % student.name}) @action(detail=False)
def ordering(self, request):
student = PersonResource.objects.filter(job=1).order_by('modify_time') page = self.paginate_queryset(student)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data) serializer = self.get_serializer(student, many=True)
return Response(serializer.data)

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

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

id=1指向的是dandy,此时

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

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

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

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

Reversing action URLS

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

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

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

或者有一种更简单的方法

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

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的官方文档

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

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

class AccountViewSet(viewsets.ModelViewSet):
"""
A simple ViewSet for viewing and editing the accounts
associated with the user.
"""
serializer_class = AccountSerializer
permission_classes = [IsAccountAdminOrReadOnly] def get_queryset(self):
return self.request.user.accounts.all()

ReadOnlyModelViewSet

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

class AccountViewSet(viewsets.ReadOnlyModelViewSet):
"""
A simple ViewSet for viewing accounts.
"""
queryset = Account.objects.all()
serializer_class = AccountSerializer

Custome ViewSet base classes

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

from rest_framework import mixins

class CreateListRetrieveViewSet(mixins.CreateModelMixin,
mixins.ListModelMixin,
mixins.RetrieveModelMixin,
viewsets.GenericViewSet):
"""
A viewset that provides `retrieve`, `create`, and `list` actions. To use it, override the class and set the `.queryset` and
`.serializer_class` attributes.
"""
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. 斯坦福大学公开课机器学习: advice for applying machine learning | deciding what to try next(revisited)(针对高偏差、高方差问题的解决方法以及隐藏层数的选择)

    针对高偏差.高方差问题的解决方法: 1.解决高方差问题的方案:增大训练样本量.缩小特征量.增大lambda值 2.解决高偏差问题的方案:增大特征量.增加多项式特征(比如x1*x2,x1的平方等等).减 ...

  2. mysql中CONCAT值为空的问题解决办法

    在mysql中concat函数有一个特点就是有一个值为null那么不管第二个字符有多少内容都返回为空了,这个特性让我们在实例应用中可能觉得不方便,但实现就是这样我们需要使用其它办法来解决. 天在做op ...

  3. 利用/dev/urandom文件创建随机数

    1:/dev/urandom和/dev/random是什么 这两个文件记录Linux下的熵池,所谓熵池就是当前系统下的环境噪音,描述了一个系统的混乱程度,环境噪音由这几个方面组成,如内存的使用,文件的 ...

  4. (最长回文子串 线性DP) 51nod 1088 最长回文子串

    输入一个字符串Str,输出Str里最长回文子串的长度. 回文串:指aba.abba.cccbccc.aaaa这种左右对称的字符串. 串的子串:一个串的子串指此(字符)串中连续的一部分字符构成的子(字符 ...

  5. DBMS客户端是否安装:Make sure DBMS client is installed and this required library is available for dynamic loading

    Symptom The full error message is as follows:Error logging in.  Unable to process the database trans ...

  6. 【强大知名的CAD绘图工具】AutoCAD 2019 for Mac

    以上图片来源于互联网分享,如涉及版权问题请联系作者删除. 文章素材来源:风云社区(www.scoee.com) 下载地址:风云社区(www.scoee.com)   [简介] AutoCAD 2019 ...

  7. Zookeeper+Kafka完全分布式实战部署

    Zookeeper+Kafka完全分布式实战部署 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 其实我之前部署过kafak和zookeeper的完全分布式,集群是可以正常使用没错, ...

  8. setAttribute和setParameter方法的区别

    getAttribute表示从request范围取得设置的属性,必须要先setAttribute设置属性,才能通过getAttribute来取得,设置与取得的为Object对象类型 getParame ...

  9. Hadoop记录-Hive调优:让任务并行执行

    set mapred.job.queue.name=pms;   //设置队列set hive.exec.reducers.max=8;  //设置最大的reducersset mapred.redu ...

  10. C#设计模式(7)——适配器模式

    1.适配器模式介绍 适配器模式的作用是将一个类的接口,转换成客户端希望的另外一种接口,适配器作为原始接口(我们的类中本来具有的功能)和目标接口(客户端希望的功能)之间的桥梁.举个例子:我们知道安卓数据 ...