將上一篇文章中的寫(xiě)法進(jìn)一步封裝簡(jiǎn)化
成都創(chuàng)新互聯(lián)主要從事成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作、網(wǎng)頁(yè)設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)唐縣,10多年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來(lái)電咨詢建站服務(wù):18982081108
urls
from app01 import views
urlpatterns = [
......
url(r'^authors/$', views.AuthorView.as_view(), name="author"),
url(r'^authors/(?P<pk>\d+)/$', views.AuthorDetailView.as_view(), name="detail_author"),
]還要寫(xiě)一個(gè)ModelSerializer,方法與上一篇博文中相同
views
from rest_framework import mixins
from rest_framework import generics
#GenericAPIView繼承了APIView
class AuthorView(mixins.ListModelMixin, #查看所有
mixins.CreateModelMixin, #添加
generics.GenericAPIView):
queryset = Author.objects.all()
serializer_class = AuthorModelSerializers
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
class AuthorDetailView(mixins.DestroyModelMixin, #刪除
mixins.RetrieveModelMixin, #查看單條
mixins.UpdateModelMixin, #更新
generics.GenericAPIView):
queryset = Author.objects.all()
serializer_class = AuthorModelSerializers
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
這是最終封裝版本,關(guān)鍵在于讓兩條不同的url(帶pk值和不帶pk值)都匯聚到同一個(gè)視圖類中
urls.py:
url(r'^authors/$', views.AuthorView.as_view({"get":"list","post":"create"}),name="author"),
url(r'^authors/(?P<pk>\d+)$', views.AuthorView.as_view({
'get': 'retrieve',
'put': 'update',
'patch': 'partial_update',
'delete': 'destroy'
}),name="detail_author"),views.py:
from rest_framework import viewsets
class AuthorView(viewsets.ModelViewSet):
queryset = Author.objects.all()
serializer_class = AuthorModelSerializers
以下面這個(gè)url為例子,我們可以看到這條url大的變化就是as_view后面?zhèn)髦盗耍虼艘纯词侨绾翁幚淼?/p>
url(r'^authors/$', views.AuthorView.as_view({"get": "list", "post": "create"}), name="author")
我們需要看看此時(shí)的as_view是如何用一個(gè)視圖類處理兩條url的,首先尋找這個(gè)as_view方法在哪里,事實(shí)上它已經(jīng)不是原來(lái)的as_view方法了
AuthorView類-ModelViewSet類-GenericViewSet類-ViewSetMixin類
在ViewSetMixin類中找到as_view方法
def as_view(cls, actions=None, **initkwargs):
......
return csrf_exempt(view)找到同在ViewSetMixin類中的view:
def as_view(cls, actions=None, **initkwargs):
......
def view(request, *args, **kwargs):
......
for method, action in actions.items(): #循環(huán)actions{"get": "list", "post": "create"}
handler = getattr(self, action) #handler = self.list或handler = self.create
setattr(self, method, handler) #self.get = self.list或self.post = self.create
......
return self.dispatch(request, *args, **kwargs)Django啟動(dòng)后的url就等同于下面的情況,等待用戶訪問(wèn)
url(r'^authors/$', ViewSetMixin.view({"get": "list", "post": "create"}), name="author")
用戶訪問(wèn)開(kāi)始后:
在views.APIView中找到self.dispatch:
class APIView(View):
def dispatch(self, request, *args, **kwargs):
try:
......
# Get the appropriate handler method
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), #此處的request.method.lower()是字符串,get或post
self.http_method_not_allowed)
#因?yàn)樯厦嬉呀?jīng)通過(guò)反射綁定self.get = self.list或self.post = self.create,
#因此這里:
#handler = self.list或self.create
else:
handler = self.http_method_not_allowed
response = handler(request, *args, **kwargs) #這里去找self.list或self.create,將執(zhí)行的結(jié)果返回給response
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response #將ListModelMixin處理后的結(jié)果返回給請(qǐng)求者self.list或self.create在ModelViewSet類的父類mixins.ListModelMixin或mixins.CreateModelMixin中
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
"""
A viewset that provides default `create()`, `retrieve()`, `update()`,
`partial_update()`, `destroy()` and `list()` actions.
"""
passListModelMixin類將數(shù)據(jù)處理并序列化后返回給APIView下的dispatch
class ListModelMixin(object):
"""
List a queryset.
"""
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
思考:viewsets.ModelViewSet通過(guò)覆蓋APIView中同名的as_view來(lái)實(shí)現(xiàn)了新功能,如果有需求的話我們也可以通過(guò)覆蓋同名方法來(lái)實(shí)現(xiàn)新的功能,例如我們可以自己寫(xiě)一個(gè)list方法來(lái)實(shí)現(xiàn)不同的需求
#認(rèn)證組件
self.perform_authentication(request)
#權(quán)限組件
self.check_permissions(request)
#頻率組件
self.check_throttles(request)
在app01.service.auth.py:
from rest_framework import exceptions
from rest_framework.authentication import BaseAuthentication
class TokenAuth(BaseAuthentication):
def authenticate(self, request):
token = request.GET.get("token")
token_obj = Token.objects.filter(token=token).first()
if not token_obj: #認(rèn)證失敗拋錯(cuò),被源碼中的try捕獲
raise exceptions.AuthenticationFailed("驗(yàn)證失敗!")
return token_obj.user, token_obj.token #需要返回一個(gè)元組在views.py:
def get_random_str(user):
import hashlib, time
ctime = str(time.time())
md5 = hashlib.md5(bytes(user, encoding="utf8"))
md5.update(bytes(ctime, encoding="utf8"))
return md5.hexdigest()
from django.http import JsonResponse
class loginView(APIView):
authentication_classes = [TokenAuth]
def post(self, request):
res = {"code": 1000, "msg": None}
user = request.data.get("user")
pwd = request.data.get("pwd")
user_obj = User.objects.filter(name=user, pwd=pwd).first()
if not user_obj:
res["code"] = 1001
res["msg"] = "用戶名或密碼錯(cuò)誤"
else:
token = get_random_str(user)
Token.objects.update_or_create(user=user_obj, defaults={"token": token})
res["token"] = token
return JsonResponse(res, json_dumps_params={"ensure_ascii": False})
我們知道在APIView類中可以找到as_view,而此時(shí)的as_view又指向了父類View中的as_view,此時(shí)父類as_view又會(huì)return dispatch,因此我們?cè)贏PIView類中找到dispatch方法,從這里開(kāi)始看源碼的執(zhí)行過(guò)程。
class APIView(View):
def dispatch(self, request, *args, **kwargs):
self.initial(request, *args, **kwargs) #這一步就是在處理認(rèn)證、權(quán)限、頻率↓
class APIView(View):
def initial(self, request, *args, **kwargs):
self.perform_authentication(request) #認(rèn)證組件
self.check_permissions(request) #權(quán)限組件
self.check_throttles(request) #訪問(wèn)頻率組件↓
class APIView(View):
def perform_authentication(self, request):
request.user這個(gè)request是Request類的實(shí)例化對(duì)象,因此我們要去Request下面去找user方法
↓
class Request(object):
@property
def user(self):
if not hasattr(self, '_user'):
with wrap_attributeerrors():
self._authenticate() #調(diào)用user過(guò)程其實(shí)就是在執(zhí)行這個(gè)方法
return self._user查看self._authenticate
↓
class Request(object):
def _authenticate(self):
for authenticator in self.authenticators: #循環(huán)包含著一個(gè)個(gè)認(rèn)證類實(shí)例的列表,此時(shí)就是一個(gè)[TokenAuth(),]
try:
user_auth_tuple = authenticator.authenticate(self) #將視圖中的authenticate返回結(jié)果賦值給user_auth_tuple,此時(shí)傳進(jìn)去的self是Request類的實(shí)例化對(duì)象
except exceptions.APIException: #驗(yàn)證失敗拋錯(cuò)
self._not_authenticated()
raise
if user_auth_tuple is not None: #如果不為空
self._authenticator = authenticator
self.user, self.auth = user_auth_tuple #user_auth_tuple是個(gè)元祖,分成了兩個(gè)變量,這兩個(gè)變量可以為下面的權(quán)限組件所利用
return #認(rèn)證成功后返回self.authenticators是什么?
往上走,發(fā)現(xiàn)構(gòu)建request時(shí)傳進(jìn)來(lái)的參數(shù)
class APIView(View):
def initialize_request(self, request, *args, **kwargs):
......
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(), #在這里
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)點(diǎn)進(jìn)去看看,發(fā)現(xiàn)就是self.authentication_classes循環(huán)的結(jié)果
def get_authenticators(self):
"""
Instantiates and returns the list of authenticators that this view can use.
"""
return [auth() for auth in self.authentication_classes] #列表解析式,循環(huán)的是我們自己在視圖中寫(xiě)的一個(gè)個(gè)認(rèn)證類因此可見(jiàn),self.authenticators就是包含著一個(gè)個(gè)認(rèn)證類實(shí)例對(duì)象的列表
authenticator.authenticate(self)是什么意思?
我們?cè)倩氐絖authenticate方法中看看這句話
authenticator.authenticate(self)實(shí)例化對(duì)象調(diào)自己的方法是不需要傳self的,因此這是個(gè)形參,我要知道這個(gè)self是誰(shuí)
那么這個(gè)self是誰(shuí)?
要往上一級(jí)一級(jí)找,上一級(jí)是_authenticate(self),誰(shuí)調(diào)用的?
找到user(self),誰(shuí)調(diào)用的user(self)?
class APIView(View):
def perform_authentication(self, request):
request.userrequest.user調(diào)的user,因此self就是這個(gè)新構(gòu)建的request,這個(gè)request是Request類的實(shí)例化對(duì)象
GET訪問(wèn)時(shí)加上數(shù)據(jù)庫(kù)中已有的一個(gè)token就能通過(guò)驗(yàn)證
http://127.0.0.1:8000/books/?token=1a54a64ee1111738c5d8b7b5487e801b
如果我們自己不設(shè)authentication_classes,那么就會(huì)去父類APIView中找,里面有這么一段代碼
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSESapi_settings是APISettings類的一個(gè)實(shí)例化對(duì)象
api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)api_settings.DEFAULT_AUTHENTICATION_CLASSES會(huì)去找settings.py中的REST_FRAMEWORK
因此我們自己在settings.py設(shè)置這個(gè)REST_FRAMEWORK就可以
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.TokenAuth"]
}["app01.utils.TokenAuth"]這個(gè)值是具體路徑,也可以是元祖
如果某個(gè)視圖(比如Login)不希望它經(jīng)過(guò)全局認(rèn)證,那么可以在視圖類中添加一個(gè)
authentication_classes = []即可
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。
標(biāo)題名稱:DjangoREST_framework框架02-創(chuàng)新互聯(lián)
新聞來(lái)源:http://www.chinadenli.net/article42/pdjec.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站策劃、Google、移動(dòng)網(wǎng)站建設(shè)、服務(wù)器托管、網(wǎng)站建設(shè)、定制開(kāi)發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容
全網(wǎng)營(yíng)銷(xiāo)推廣知識(shí)