前文
团队里用flask的比较多,而Django相比flask的差别主要是库的应用和代码组织结构的区别,虽然有差别,但快速上手问题也不大;而drf在Django的基础上继续封装了权限、分页、序列化等操作(限流和过滤等用的少),使其快速适配rest的开发方式,快速上手相对困难,这里快速普及下相关。
两份文档:
Django(中文版): https://docs.djangoproject.com/zh-hans/2.1/intro/tutorial01/
DRF(Django rest framework): https://www.django-rest-framework.org/tutorial/quickstart/
DRF基本是Django的扩展,带来多功能的同时,因为封装的特性,其也带来了额外的性能损耗,所以在优化drf的性能时,可以考虑将其部分特性去除,比如序列化、分页;但相对来说,其带来的开发便利性还是非常强的,很多东西开箱即用。
这里主要讲述实战经常用到的功能,而如果要全面的功能了解,官网是最好的途径。
开发
序列化器(Serialization)
官网:https://www.django-rest-framework.org/tutorial/1-serialization/
serializer对model的序列化和反序列化,便于view视图的直接调用,web的流程(以post为例)无非就是取参、校验参数、创建或更新数据库、返回前端。对应的序列化器就是get_serializer、serializer.is_valid、serializer.save、response(serializer.data),而这之中is_valid通过定义字段获得,save不存在则是调用create,存在则是调用update方法校验,最后返回序列化后的data即可。序列化器主要分为ModelSerializers和Serializers,二者区别:
Serializers:
1.将数据库取出的数据序列化成Python所需的:字典格式
2.增加校验,数据库通常已经有默认值、长度的校验;但视图级的校验不同,会加上:required、max_length、format等校验
ModelSerializers:
1.如果不需要额外做require等校验,或者要做校验的字段很少,以及限制获取model字段的数量,用model序列化器
2.model序列化器默认实现了create、update方法
请求和返回(Request & Response)
drf对于请求和返回的封装比较简单,获取请求参数,直接request.data获取到一个字典结构;对于返回response,直接Response(serializer.data)即可,示例如下:
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
@api_view(['GET', 'POST'])
def snippet_list(request):
"""
List all code snippets, or create a new snippet.
"""
if request.method == 'GET':
snippets = Snippet.objects.all()
serializer = SnippetSerializer(snippets, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = SnippetSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
视图(ViewSet)
drf里视图有APIView、mixin、genericView,但用的最多的是终极封装Viewset,而viewset本身就是就是集成APIView和Mixin,其带来的好处是默认封装了list、get、put、delete、post等接口(对应的名字不同,具体看示例),覆盖了restful的基本请求;当我们要修改的时候,只要继承该类然后重写子类方法即可;如果不需要重写的话,则会默认跟序列化器组合生成返回。
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
其中每个方法具体的实现去看Mixin的源码就可以一目了然,本质上就是围绕序列化和数据库的一些操作,详细可参考:https://www.django-rest-framework.org/api-guide/generic-views/。摘选其中的重要方法介绍(其中perform的三个系列是类似于装饰器、中间件的效果,在创建、更新、删除的时候做一些额外的动作,比如更新本次请求的用户,这样就不需要在具体的业务做这些操作):
The following methods are provided by the mixin classes, and provide easy overriding of the object save or deletion behavior.
perform_create(self, serializer) - Called by CreateModelMixin when saving a new object instance.
perform_update(self, serializer) - Called by UpdateModelMixin when saving an existing object instance.
perform_destroy(self, instance) - Called by DestroyModelMixin when deleting an object instance.
Other methods:
You won't typically need to override the following methods, although you might need to call into them if you're writing custom views using GenericAPIView.
get_serializer_context(self) - Returns a dictionary containing any extra context that should be supplied to the serializer. Defaults to including 'request', 'view' and 'format' keys.
get_serializer(self, instance=None, data=None, many=False, partial=False) - Returns a serializer instance.
get_paginated_response(self, data) - Returns a paginated style Response object.
paginate_queryset(self, queryset) - Paginate a queryset if required, either returning a page object, or None if pagination is not configured for this view.
filter_queryset(self, queryset) - Given a queryset, filter it with whichever filter backends are in use, returning a new queryset.
权限(Authentication & Permissions)
Django里的权限也是围绕着每个new project的形式,本质上是利用到了permissions.py里面的BasePermission类,感兴趣的可以点进去,里面还扩展了很多类,但都是继承BasePermission类来实现的,其由两个核心方法has_permission和has_object_permission组成。调用方式是在指定的视图下(viewset)指定类属性permission_classes = [permissions.xxx(具体类)],然后在具体类的下面重写上述两个方法,前者是只要是该视图类进来的http请求都会进入has_permission判断,后者则是在方法里调用了get_object()方法后会去调用的,而只要这两个方法任一返回False,则会返回前端403 respons。这里贴下drf里面permissions.py里面的扩展类。
class AllowAny(BasePermission):
"""
Allow any access.
This isn't strictly required, since you could use an empty
permission_classes list, but it's useful because it makes the intention
more explicit.
"""
def has_permission(self, request, view):
return True
class IsAuthenticated(BasePermission):
"""
Allows access only to authenticated users.
"""
def has_permission(self, request, view):
return bool(request.user and request.user.is_authenticated)
class IsAdminUser(BasePermission):
"""
Allows access only to admin users.
"""
def has_permission(self, request, view):
return bool(request.user and request.user.is_staff)
class IsAuthenticatedOrReadOnly(BasePermission):
"""
The request is authenticated as a user, or is a read-only request.
"""
def has_permission(self, request, view):
return bool(
request.method in SAFE_METHODS or
request.user and
request.user.is_authenticated
)