flyEn'blog

django-rest-framework的API视图

如果不需要序列化类,可在长注释里写参数(不同api文档写法不同,以下为swagger)

1
2
3
4
5
6
7
8
9
10
"""
<描述>
---
parameters:
- name: mobile_phone
required: true
type: string
description: 手机号
"""
mobile_phone = request.data.get('mobile_phone')

序列化器中,如果想拿到request中的参数,则可

1
2
3
4
def validate(self, attrs):
code = attrs.get("code")
user = self.context['request'].user
...

select_related()返回一个queryest,执行时它沿着外键关系查询关联对象的数据。

它会生成一个复杂的查询并引起性能的损耗,但是在以后使用外键关系时将不需要数据库查询。

select_related() 可用于任何对象的查询集
filter()和select_related顺序不重要,以下等同

1
2
Entry.objects.filter(pub_date__gt=timezone.now()).select_related('blog')
Entry.objects.select_related('blog').filter(pub_date__gt=timezone.now())

也可以缓存一个关联之下的关联

1
2
3
4
5
6
7
8
class City(models.Model):
pass

class Person(models.Model):
hometown = models.ForeignKey(City)

class Book(models.Model):
author = models.ForeignKey(Person)

1
b = Book.objects.select_related('author__hometown').get(id=4)

将缓存关联的Person 和关联的 City

也可以反向引用,可使用关联对象字段的related_name。,不要指定字段的名称。
调用不带参数的select_related(),select_related(None)它将查找能找到的所有不可为空外键 —— 可以为空的外键必须明确指定。

POST:新增。
GET:读取。
PUT:更新。
DELETE:删除。
PATCH:部分更新。

PUT和POST的区别为,PUT方法是幂等的(作用在任一元素两次后和其作用一次的结果相同,即请求成功执行所得到的的结果不依赖于该方法被执行的次数。)

例:在一个支付系统中,一个api的功能是创建收款金额二维码,它和金额相关,每个用户可以有多个二维码,如果连续调用则会创建新的二维码,这个时候就用POST

PATCH 部分更新,即如果有内容参数没有传,就不会被更新

API视图

ViewSet

1
2
3
4
5
6
viewsets.ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):

默认包括创建、查看、更新、删除、列表
create(), retrieve(), update(), partial_update(), destroy() and list()
perform_create创建方法

1
2
3
viewsets.ReadOnlyModelViewSet(mixins.RetrieveModelMixin,
mixins.ListModelMixin,
GenericViewSet):

只读,默认包括retrieve()和list()方法

1
viewsets.GenericViewSet(ViewSetMixin, generics.GenericAPIView):

默认不提供任何操作,需自己编写,但包括基本的通用视图行为,例如 get_objectget_queryset方法。(通常会使用提供默认行为的现有基类,而不是自己编写)

1
viewsets.ViewSet(ViewSetMixin, views.APIView):

默认不提供任何操作

ViewSet视图如果需要可以绑定到单独的视图当中去

1
user_list = UserViewSet.as_view({'get': 'list'})

但通常不这样用,而是用用路由器注册视图,并允许自动生成urlconf

1
2
3
router = DefaultRouter()
router.register(r'users', UserViewSet)
url(r'^api/', include(router.urls)), # 可在注定url下包括进去

REST框架附带的默认路由器将为标准的create / retrieve / update / destroy样式操作提供路由
如果您需要路由到特殊方法,则可以使用@detail_route或@list_route装饰器将其标记为需要路由。@detail_route装饰器在其URL模式中包含pk,旨在用于需要单个实例的方法。 @list_route装饰器适用于在对象列表上操作的方法。

比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer

@detail_route(methods=['post'])
def set_password(self, request, pk=None):
user = self.get_object()
serializer = PasswordSerializer(data=request.data)
if serializer.is_valid():
user.set_password(serializer.data['password'])
user.save()
return Response({'status': 'password set'})
else:
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)


@list_route()
def recent_users(self, request):
recent_users = User.objects.all().order('-last_login')
...

装饰器也可额外设置仅为路由视图设置的额外参数

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

装饰器默认路由GET请求,但也可以通过使用methods参数来接受其他http方法

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

有俩新操作将可用^users/{pk}/set_password$/and ^users/{pk}/unset_password/$

分析:

使用ViewSet类比之view类有两个主要优点:

  1. 重复的逻辑可组成一个类,比如只需要指定一次queryset,并且它将被跨多个视图使用。
  2. 通过使用路由器,我们不再需要处理连接自己的URL。

使用常规视图和URL confs更加明确,并为您提供更多的控制。


1
GenericAPIView(views.APIView)

所有其他通用视图的基类


APIView

REST框架提供了一个APIView类,它将Django的View类子类化。

APIView与常规的View的不同:

  1. 传递给处理程序方法的请求将是REST框架的Request实例,而不是Django的HttpRequest实例。
  2. 处理器方法可能返回REST框架的响应,而不是Django的HttpResponse。该视图将管理内容协商,并根据响应设置正确的渲染器。
  3. 任何APIException异常将被捕获并调解为适当的响应。
  4. 传入请求将被认证,并且在将请求发送给处理程序方法之前,将运行适当的权限和/或throttle checks。

@api_view()
@api_view(http_method_names=['GET'], exclude_from_schema=False)
如果要编写一个非常简单的视图,比如返回一些数据

1
2
3
4
5
from rest_framework.decorators import api_view

@api_view()
def hello_world(request):
return Response({"message": "Hello, world!"})

默认只接受GET方法, 如果要指定视图允许的方法:

1
2
3
4
5
@api_view(['GET', 'POST'])
def hello_world(request):
if request.method == 'POST':
return Response({"message": "Got some data!", "data": request.data})
return Response({"message": "Hello, world!"})

要覆盖默认设置,REST框架提供了一组可以添加到您的视图的其他装饰器。这些必须来自@api_view装饰器

1
2
3
4
5
6
7
class OncePerDayUserThrottle(UserRateThrottle):
rate = '1/day'

@api_view(['GET'])
@throttle_classes([OncePerDayUserThrottle])
def view(request):
return Response({"message": "Hello for today! See you tomorrow!"})

GenericAPIView

GenericAPIView
Mixins:ListModelMixin、CreateModelMixin、RetrieveModelMixin、UpdateModelMixin、DestroyModelMixin
View Classes:CreateAPIView、ListAPIView、RetrieveAPIView、DestroyAPIView、UpdateAPIView、ListCreateAPIView、RetrieveUpdateAPIView、RetrieveDestroyAPIView、RetrieveUpdateDestroyAPIView

1
2
3
4
5
class UserList(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = (IsAdminUser,)
...

简单的情况可直接用url

1
url(r'^/users/', ListCreateAPIView.as_view(queryset=User.objects.all(), serializer_class=UserSerializer), name='user-list')

GenericAPIView

属性

基础配置
queryset:或者用get_queryset()覆盖。如果覆盖视图方法,调用get_queryset()不是直接访问此属性很重要,因为queryset将被评估一次,并且这些结果将被缓存为所有后续请求。
serializer_class:用于验证和反序列化输入以及序列化输出的serializer类。通常,您必须设置此属性,或覆盖get_serializer_class()方法。
lookup_field:应用于执行单个模型实例的对象查找的模型字段。默认为“pk”。注意,当使用超链接的API时,如果需要使用自定义值,则需要确保API视图和序列化器类都设置查找字段。
lookup_url_kwarg:用于对象查找的URL关键字参数。 URL conf应包含与该值相对应的关键字参数。如果取消设置,则默认使用与lookup_field相同的值

分页Pagination
pagination_class :分页列表结果时应使用的分页类。默认值与DEFAULT_PAGINATION_CLASS设置相同,即’rest_framework.pagination.PageNumberPagination’,设置pagination_class = None将禁用此视图上的分页。

过滤
filter_backends:应用于过滤查询集的过滤器后端类的列表。默认值与DEFAULT_FILTER_BACKENDS设置相同。

方法

def get_queryset(self):查询集方法。
get_object(self):得到对象方法。
filter_queryset(self, queryset):给定一个查询器,使用任何过滤器后端进行过滤,返回一个新的查询器。

1
2
3
4
5
6
7
8
9
10
11
12
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

get_serializer_class(self):返回应该用于序列化程序的类。


以下方法由mixin类提供,并提供容易地覆盖对象保存或删除行为。
perform_create(self, serializer) - 在保存新对象实例时由CreateModelMixin调用。
perform_update(self, serializer)- 在保存现有对象实例时由UpdateModelMixin调用。
perform_destroy(self, instance) - 删除对象实例时由DestroyModelMixin调用。

这些特别适用于设置请求中隐含的属性,但不是请求数据的一部分。例如,您可以根据请求用户或基于URL关键字参数在对象上设置属性。

对于添加在保存对象之前或之后发生的行为(例如发送确认或记录更新)也特别有用。

1
2
3
4
5
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)

通常您不需要覆盖以下方法,尽管如果使用GenericAPIView编写自定义视图,则可能需要调用它们。

get_serializer_context(self):返回包含应提供给序列化程序的额外上下文的字典。默认为包含'request', 'view''format'键。
get_serializer(self, instance=None, data=None, many=False, partial=False):返回一个序列化器实例。
get_paginated_response(self,data) :返回分页样式的Response对象。
paginate_queryset(self, queryset):如果需要,请分页查询器,返回页面对象,如果未为此视图配置分页,则为无。
filter_queryset(self,queryset) :给定一个查询器,使用任何过滤器后端进行过滤,返回一个新的查询器。

Fork me on GitHub