在这以前我写过一篇关于Django与Drf快速开发实践的博客,Django快速开发实践:Drf框架和xadmin配置指北,粗略说了一下Drf配置和基本使用,不过里面只是涉及到最基本的CRUD,在正常的后端开发中涉及的诸如认证和权限、消息队列、缓存之类的操做,上一篇博客并无涉及,此次开发我仔细了看了官方文档的这几个部分,把这部分的功能完善了起来。html
Drf的设计颇有Django的味道,(毕竟就是伴生框架嘛)封装了不少功能,稍微配置一下就能够用,这点在快速开发方面真的好评。java
开始进入正题。python
任何系统都离不开认证和受权,Drf内置一套强大的鉴权系统,框架提供了几种基本的认证和权限控制方式,小型系统基本够用,还能够自定义权限中间件,很方便就能实现节流这样的功能。linux
Drf内置的四种API认证方式,基本信息我作了一个表格:android
认证方式 | 说明 |
---|---|
BasicAuthentication | 每次提交请求的时候附加用户名和密码来进行认证 |
TokenAuthentication | 每次提交请求的时候在HTTP headers里附加Token进行认证 |
SessionAuthentication | 用户登陆以后系统在cookies存入sessionid进行认证 |
RemoteUserAuthentication | 经过web服务器认证(apache/nginx这些) |
我选择的是基于Token的认证,客户端登陆以后维护一个token,每次请求附加到HTTP headers,还算是方便。nginx
Drf还能够自定义认证方式,只要继承authentication.BaseAuthentication
这个类而后重写authenticate
方法就行了。这里只简单说下步骤,具体的参考官方文档。web
BaseAuthentication
、重写authenticate
方法authenticate()
返回值
None
:当前认证无论,等下一个认证来执行raise exceptions.AuthenticationFailed('用户认证失败')
request.user
、元素2复制给request.auth
在settings.py
中能够配置默认的认证方式,这里我添加了三个:apache
REST_FRAMEWORK = { # 身份验证 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.TokenAuthentication', 'rest_framework.authentication.SessionAuthentication', ) }
固然也支持各类第三方的认证框架,好比下面这些:django
使用这些认证方式,认证经过后,在views里面request.user
就是一个Django用户对象,若是未认证,就是一个AnonymousUser
对象。json
接下来讲说权限
Drf的接口权限有如下几种:
通常来讲小网站用到DjangoModelPermissions
就是够用的,或者干脆简单一点,用IsAuthenticated
和queryset
限定请求的数据便可。
介绍完了基本概念,来看看代码中是如何操做的。
对于操做用户信息的viewset,我只用了permissions.IsAuthenticated
这个权限,而后覆盖了ReadOnlyModelViewSet
的get_queryset
方法,把queryset变成只包括当前用户,这样就保证了一个用户只能操做本身的信息。
from rest_framework import authentication, permissions, viewsets class UserViewSet(viewsets.ReadOnlyModelViewSet): permission_classes = [permissions.IsAuthenticated] serializer_class = UserSerializer def get_queryset(self): return User.objects.filter(pk=self.request.user.pk)
viewset的action一样可使用权限,加在装饰器的参数上便可:
@action(detail=True, methods=['GET'], permission_classes=[permissions.IsAuthenticated]) def some_actions(self, request, pk=None): dosomething return Response(SomeSerializer(some_data, many=True).data)
这里提一下装饰器的detail参数,这个表明了是对列表操做仍是对单个对象操做,True就是对单个对象。
从请求的URL上应该能够很好理解。
http://hostname/viewset/some_action/
http://hostname/viewset/1/some_action/
这部分的参考资料(优先阅读官方文档):
Drf的ApiView至关于Django的View,每一个ViewSet是一组Restful的接口,看看viewset的代码,其中定义了6个action,对应get、post、put、delete这些http方法。
class UserViewSet(viewsets.ViewSet): 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
除了自带的这些,还能够定义额外的action,这个在前文已经提过了。
ApiView的话,使用起来和Django的差很少,一个apiview只能对应一个url,经过重写get
、post
这些方法能够实现不一样的http方法响应。
ApiView和ViewSet一样经过在类字段中加入authentication_classes
和permission_classes
实现认证和受权。
Drf和Django同样自带分页功能,很好用(固然也支持使用第三方的分页功能)。
首先进行配置(不配置的话使用默认配置),这里我设置每页显示十条记录:
REST_FRAMEWORK = { # 设置分页 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 10, }
使用得最多的ModelViewSet
已经自带分页了,这个咱们不用操心,不过若是本身定义了action来返回列表数据的话,就没有分页,这时候要用paginate_queryset
方法来处理。
代码以下:
@action(detail=False) def tag(self, request): queryset = SomeModel.objects.all().order_by('-add_time') page = self.paginate_queryset(queryset) if page is not None: return self.get_paginated_response(self.get_serializer(page, many=True).data) return Response(self.get_serializer(queryset, many=True).data)
能够看出Drf自动处理了不一样页面的请求,不用像Django同样本身从GET或者POST数据里读取page,分页相关的方法直接在viewset对象里面,很是方便。
Drf支持不少文档插件,这里我用一下比较传统的coreapi文档,首先配置:
REST_FRAMEWORK = { # 文档 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema', }
在使用以前还须要使用pip安装coreapi:
pip install coreapi
全部的ViewSet只要用了Router注册的话都默认添加到文档里,不过ApiView的话是要本身添加的。
from rest_framework.schemas.coreapi import AutoSchema class SomeView(APIView): schema = AutoSchema() def post(self, request): data = dosomething() return Response(data)
这样就能够了,别忘了注册路由,路由我在下文统一介绍。
配置完成以后文档的效果应该是下图这样,这个页面能够测试请求,还能够以各类方式登陆认证,比Drf默认的界面丰富一些~
使用Drf框架能够有两种路由方式,一种是Drf的路由,一直是Django的路由,两种搭配使用。
from rest_framework import routers router = routers.DefaultRouter() router.register('users', UserViewSet, basename='api-users') router.register('user-profiles', UserProfileViewSet, basename='api-user-profiles') router.register('tags', TagViewSet, basename='api-tags') router.register('categories', CategoryViewSet, basename='api-categories') router.register('articles', ArticleViewSet, basename='api-articles')
定于完成以后要添加到Django的urlpatterns
里面。
urlpatterns = [ path('api/', include(router.urls)), ]
对于ApiView,路由和Django的ClassBaseView差很少:
urlpatterns = [ path('login/', views.LoginView.as_view()), path('signup/', views.SignUpView.as_view()), ]
from rest_framework.authtoken import views as authtoken_view from rest_framework.documentation import include_docs_urls urlpatterns = [ path('api-auth/', include('rest_framework.urls', namespace='rest_framework')), path('api-token-auth/', authtoken_view.obtain_auth_token), path('api-docs/', include_docs_urls(title='One Cat Docs')), ]
序列化器也是Drf的一个重要概念,和Django的Form很像,做用是将Model对象的数据转换为json、xml之类的结构化数据。
使用起来很简单,我通常都是这么定义
class CategorySerializer(serializers.ModelSerializer): class Meta: model = Category fields = '__all__'
使用__all__
直接把全部模型字段都包括进去,针对外键等关系字段,咱们须要作一些其余处理。
这里简单介绍一下我经常使用的几种关系字段处理方式
StringRelatedField
:将关系字段显示为一个字符串,这个字符串取决于该模型定义的__str__
方法PrimaryKeyRelatedField
:显示成该关系字段的主键,这个也是默认的具体的使用方式能够在官网找到~
下面再介绍几个经常使用的参数:
read_only
:通常针对不想被修改的字段可使用,好比说用户idmany
:用于多对多关系或者一对多,会将全部引用序列化为一个列表其实就是一个自定义的认证过程。
Drf内置有BaseThrottle
、 SimpleRateThrottle
,后者是前者的之类。
BaseThrottle
须要本身写allow_request
和wait
方法,控制粒度更细 SimpleRateThrottle
重写get_cache_key
和设置scope
名称就能够,更简单 SimpleRateThrottle
代码以下:
from rest_framework.throttling import SimpleRateThrottle class VisitThrottle(SimpleRateThrottle): '''匿名用户60s只能访问三次(根据ip)''' scope = 'throttle' #这里面的值,本身随便定义,settings里面根据这个值配置throttle def get_cache_key(self, request, view): #经过ip限制节流 return self.get_ident(request) class UserThrottle(SimpleRateThrottle): '''登陆用户60s能够访问10次''' scope = 'userThrottle' #这里面的值,本身随便定义,settings里面根据这个值配置userThrottle def get_cache_key(self, request, view): return request.user.user_id
BaseThrottle
代码以下:
from rest_framework.throttling import BaseThrottle import time VISIT_RECORD = {} #保存访问记录 class VisitThrottle(BaseThrottle): '''60s内只能访问3次''' def __init__(self): self.history = None #初始化访问记录 def allow_request(self,request,view): #获取用户ip (get_ident) remote_addr = self.get_ident(request) ctime = time.time() #若是当前IP不在访问记录里面,就添加到记录 if remote_addr not in VISIT_RECORD: VISIT_RECORD[remote_addr] = [ctime,] #键值对的形式保存 return True #True表示能够访问 #获取当前ip的历史访问记录 history = VISIT_RECORD.get(remote_addr) #初始化访问记录 self.history = history #若是有历史访问记录,而且最先一次的访问记录离当前时间超过60s,就删除最先的那个访问记录, #只要为True,就一直循环删除最先的一次访问记录 while history and history[-1] < ctime - 60: history.pop() #若是访问记录不超过三次,就把当前的访问记录插到第一个位置(pop删除最后一个) if len(history) < 3: history.insert(0,ctime) return True def wait(self): '''还须要等多久才能访问''' ctime = time.time() return 60 - (ctime - self.history[-1])
#全局 REST_FRAMEWORK = { # 设置全局节流 "DEFAULT_THROTTLE_CLASSES":['api.utils.throttle.UserThrottle'], #全局配置,登陆用户节流限制(10/m) # 设置访问频率 "DEFAULT_THROTTLE_RATES":{ 'throttle':'3/m', #没登陆用户3/m,throttle就是scope定义的值,经过IP地址 'userThrottle':'10/m', #登陆用户10/m,userThrottle就是scope定义的值, 经过user_id } } # 局部:在类视图中添加 throttle_classes = [VisitThrottle,]
大概就这些吧,官方文档真的很详细,看官方文档能够解决95%的疑问,加上Django和python自带的快速开发生产力,写起来太爽啦~
我整理了一系列的技术文章和资料,在公众号「程序设计实验室」后台回复 linux、flutter、c#、netcore、android、java、python 等可获取相关技术文章和资料