我們可以在settings.py文件中定義登錄,權限,分頁,異常等的全局配置,如下所示
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'utils.page.Page',
'DEFAULT_AUTHENTICATION_CLASSES': (
'utils.permissions.AdminAuth',
),
'EXCEPTION_HANDLER': 'utils.custom_exception_handler.custom_exception_handler',
"DEFAULT_RENDERER_CLASSES":('rest_framework.renderers.JSONRenderer',),
'DATETIME_FORMAT': "%Y-%m-%d %H:%M",
# 'DEFAULT_PERMISSION_CLASSES': (
# 'utils.permissions.LoginPermission',
# )
}
也可以在對應的views,viewset中指定對應的class,來覆蓋settings.py中的配置。
登錄
drf 自己帶了一個登錄接口,在reset_framework.urls.py 里面,內容如下
urlpatterns = [
url(r'^login/$', views.LoginView.as_view(template_name='rest_framework/login.html'), name='login'),
url(r'^logout/$', views.LogoutView.as_view(), name='logout'),
]
其登錄的用戶是使用的django自己的User模塊,登錄方式為sessionid,相關信息存儲在數據庫中,登錄的相關邏輯同admin中一致。
有時候,我們需要自己定義自己的登錄用戶模塊,並在登錄的時候,將user放到request.user 屬性中,於是,我們可以編寫自己的用戶登錄模塊(具體的登錄處理邏輯這里不做討論,這里我們只看看怎么將我們的user model放到request.user中)
根據drf官方文檔的例子,我們可以寫出下面的代碼
from django.contrib.auth.models import User
from rest_framework import authentication
from rest_framework import exceptions
class ExampleAuthentication(authentication.BaseAuthentication):
def authenticate(self, request):
"""獲取META中的信息(也可以通過sessionid,token等在redis or mysql中查找),然后在model中取相應的用戶,取出來,則返回對應的對象,沒有,則返回None或則raise異常信息。返回的user對象會加載到requst的user屬性,如果沒有,則使用匿名用戶"""
username = request.META.get('X_USERNAME')
if not username:
return None
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
raise exceptions.AuthenticationFailed('No such user')
return (user, None)
可以發現,這一部分的工作僅僅是添加request.user屬性,並沒有對登錄做權限的驗證。
權限 permission
主要用於對接口權限的控制,比如知否具有該model,object的權限,是否登錄,登錄用戶是否admin等限制條件。drf自帶的權限驗證有AllowAny, IsAuthenticated, IsAdminUser, IsAuthenticatedOrReadOnly, DjangoModelPermissions, DjangoModelPermissionsOrAnonReadOnly, DjangoObjectPermissions。我們也可以根據自己需要自己定義所需的權限驗證類,如下
class Permission(permissions.BasePermission):
def has_permission(self, request, view):
# docs文檔接口不需要權限,因為真的online環境中沒有docs的路由
if "docs" in request.path:
return True
if getattr(request, "admin_login", None):
"""后台用戶驗證是否有權限"""
from precontract.views import CageView, PrecontractView, FosterView
from pet.views import PetView, ShapView, PetCategoryView
# 根據view的類型,判斷所需的權限名字
if isinstance(view, CageView) or isinstance(view, FosterView):
authority_name = "foster"
elif isinstance(view, PrecontractView):
authority_name = "precontract"
elif isinstance(view, PetView):
authority_name = "memeber"
else:
authority_name = "precontract"
try:
user = request.user # 自己定義的user model
role = user.role
authority = Authority.objects.get(role=role, state='1')
authority_info = authority.get_info()
if authority_info[authority_name] != '1':
# 判斷是否具有權限,返回False,則最終返回403狀態,
# 而這里需要我們自定義處理的結果,所以raise一個自己寫的異常
# return False
raise RightDenied
# 權限通過,返回True
return True
except BaseException:
raise RightDenied
# return False
raise PermissionDenied
異常處理
我們可以自己定義我們程序的異常的處理返回,或添加額外的返回信息,示例如下
import traceback
from rest_framework.views import exception_handler
from rest_framework.response import Response
from django.http import HttpResponseRedirect
import logging
logger = logging.getLogger('views')
def custom_exception_handler(exc, context):
# Call REST framework's default exception handler first,
# to get the standard error response.
response = exception_handler(exc, context)
# response = None
# Now add the HTTP status code to the response.
# 捕獲程序中的斷言異常,作相關的處理,
if response is not None:
# raise exc
response.data['status_code'] = response.status_code
response.data['error'] = str(exc)
elif isinstance(exc, AssertionError):
# 斷言錯誤,
response.data[‘detail’] = "斷言錯誤"
response.data['error'] = str(exc)
else:
raise exc
return response
分頁
示例如下
class CustomPagination(pagination.PageNumberPagination):
page_size = 20 # 默認分頁大小
page_size_query_param = 'page_size' # 分頁大小控制
max_page_size = 30
def get_paginated_response(self, data):
# 自定義分頁后的數據返回格式
return Response({
'links': {
'next': self.get_next_link(),
'previous': self.get_previous_link()
},
'count': self.page.paginator.count,
'results': data
})
