首先咱們須要先了解一下CBV的執行流程:前端
一般在寫CBV模式時,會在路由匹配時re_path('login/', views.LoginView.as_view()),進入as_view() 中發現最後返回的是view,而後又發現view函數中最後返回的是dispatch(),進入該函數發現,其實其中就是經過反射執行request.method對應的方法。總結,CBV本質是經過執行反射進行的。並且在執行對應方法以前會執行dispatch().web
當咱們的視圖對應的類繼承的是rest_framework.views 的APIView時,咱們進入dispatch(),發現request = self.initialize_request(request, *args, **kwargs)中最後返回的是Request的對象,對原生request進行封裝(進入Request發現原生的request爲_request),而且添加了如authenticators=self.get_authenticators()等屬性,進入get_authenticators()返回的是authentication_classes的實例對象,進而發現默認authentication_classes是對讀配置文件的。數據庫
ok,咱們繼續看dispatch(),在封裝以後又self.initial(request, *args, **kwargs),initial中又走self.perform_authentication(request),該方法返回了request.user,這時的request是最開始封裝的request,查看Requet其中的user,發現走了self._authenticate(),其中對self.authenticators進行循環,執行對應的authenticate()。django
from django.db import models # Create your models here. class UserInfo(models.Model): username = models.CharField(max_length=16) password = models.CharField(max_length=32) type = models.SmallIntegerField( choices=((1, '普通用戶'), (2, 'VIP用戶')), default=1 ) class Token(models.Model): user = models.OneToOneField(to='UserInfo',on_delete=models.CASCADE) token_code = models.CharField(max_length=128)
from django.shortcuts import render,HttpResponse from app01 import models from django.views import View from rest_framework.views import APIView from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed from django.http import JsonResponse # Create your views here. def get_random_token(username): """ 根據用戶名和時間戳生成隨機token :param username: :return: """ import hashlib, time timestamp = str(time.time()) m = hashlib.md5(bytes(username, encoding="utf8")) # 加鹽 m.update(bytes(timestamp, encoding="utf8")) return m.hexdigest() # 自定義認證類 class MyAuth(BaseAuthentication): def authenticate(self,request): try: token = request._request.GET.get('token') token_obj = models.Token.objects.get(token_code=token) if token_obj: return (token_obj.user, token_obj) else: raise AuthenticationFailed('認證失敗') except Exception as e: raise AuthenticationFailed('請攜帶token,認證失敗') class LoginView(APIView): """ 校驗用戶名密碼是否正確從而生成token的視圖 """ authentication_classes = [MyAuth, ] def post(self, request): # self.dispatch() res = {"code": 0} username = request.data.get("username") password = request.data.get("password") user = models.UserInfo.objects.filter(username=username, password=password).first() if user: # 若是用戶名密碼正確 token = get_random_token(username) # 找到user對象,有就更新(更新token_code),沒有就建立 models.Token.objects.update_or_create(defaults={"token_code": token}, user=user) res["token"] = token else: res["code"] = 1 res["error"] = "用戶名或密碼錯誤" return JsonResponse(res) def get(self,request): return HttpResponse('get')
若是咱們本身寫了authentication_classes,那麼就會走咱們寫在其中的類的實例對象,我自定義認證類中必須寫有authenticate()實現認證邏輯。json
re_path('login/', views.LoginView.as_view())
定義全局認證:後端
由於默認的authentication_classes是讀配置文件的,若是咱們想要設置全局的,咱們要在settings.py中設置api
REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": ('app01.utils.MyAuth.MyAuth', ), }
我把自定義認證類放在app01.utils.MyAuth中,若是某個視圖不想認證就設置authentication_classes=[]。瀏覽器
一樣讓咱們先從dispatch()開始走,在initial中看到有check_permissions(),其中有循環get_permissions(),進入get_permissions()發現仍是循環返回permission_classes中的類對象,進去發現默認仍是在配置文件中,若是咱們本身在視圖中寫,不就走咱們本身設置的了?ok,check_permissions()中循環get_permissions()時,都對has_permission進行判斷。緩存
咱們自定義的權限類中要有has_permission()實現權限邏輯流程。app
from django.shortcuts import render,HttpResponse from app01 import models from django.views import View from rest_framework.views import APIView from rest_framework.permissions import BasePermission from django.http import JsonResponse # Create your views here. def get_random_token(username): """ 根據用戶名和時間戳生成隨機token :param username: :return: """ import hashlib, time timestamp = str(time.time()) m = hashlib.md5(bytes(username, encoding="utf8")) # 加鹽 m.update(bytes(timestamp, encoding="utf8")) return m.hexdigest() # 自定義權限 class MyPermission(BasePermission): message = 'vip用戶才能訪問' def has_permission(self,request,view): ''' 自定義權限只有vip用戶才能訪問 :param request: :return: ''' # 由於在進行權限判斷以前已經作了認證,能夠直接拿到request.user # print('request.user',request.user) # print('request',request) # print('view',view) # print('request.user.type',request.user.type) if request.user and request.user.type==2: return True else: return False class LoginView(APIView): """ 校驗用戶名密碼是否正確從而生成token的視圖 """ authentication_classes = [ ] permission_classes=[] def post(self, request): self.dispatch res = {"code": 0} username = request.data.get("username") password = request.data.get("password") user = models.UserInfo.objects.filter(username=username, password=password).first() if user: # 若是用戶名密碼正確 token = get_random_token(username) # 找到user對象,有就更新(更新token_code),沒有就建立 models.Token.objects.update_or_create(defaults={"token_code": token}, user=user) res["token"] = token else: res["code"] = 1 res["error"] = "用戶名或密碼錯誤" return JsonResponse(res) def get(self,request): return HttpResponse('login get') class OrderView(APIView): permission_classes = [MyPermission,] def get(self,request): return HttpResponse('這裏是訂單頁面,只有vip用戶能夠訪問')
re_path('order/$', views.OrderView.as_view()),
定義全局權限認證
REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": ('app01.utils.MyAuth.MyAuth', ), 'DEFAULT_PERMISSION_CLASSES':('app01.utils.MyPermission.MyPermission',), }
同理若是某個視圖不須要權限認證就設置permission_classes=[]。
一樣讓咱們先從dispatch()開始走,在initial中看到有check_throttles(),其中有循環get_throttles(),進入其中發現是對throttle_classes中的類對象進行循環,而throttle_classes默認是讀配置文件的。一樣咱們能夠本身寫,咱們在check_throttles()會執行全部類對象中allow_request()因此須要重寫,(自定義節流認證)
from django.shortcuts import render,HttpResponse from app01 import models from django.views import View from rest_framework.views import APIView from rest_framework.request import Request from rest_framework.throttling import BaseThrottle from django.http import JsonResponse import time # Create your views here. # 自定義節流類 visit_record={} class MyThrottle(BaseThrottle): def __init__(self): self.history=None def allow_request(self,request,view): ''' 自定義頻率限制60s只能訪問三次 :param request: :param view: :return: ''' # 訪問ip ip=request.META.get('REMOTE_ADDR') ctime=time.time() # 若是這個ip本次訪問時間不在記錄就添加進去 if ip not in visit_record: visit_record[ip]=[ctime] return True history=visit_record[ip] self.history=history # 已經在就更新 history.insert(0,ctime) # {'ip':['22.10.10','22.10.8','22.10.6']} # 當最先的時間距離如今超過一分鐘,就刪除 while history and history[-1]<ctime-60: history.pop() if len(history)>3: return False else: return True def wait(self): ''' 限制時間還有多久 :return: ''' ctime=time.time() return 60-(ctime-self.history[-1]) def get_random_token(username): """ 根據用戶名和時間戳生成隨機token :param username: :return: """ import hashlib, time timestamp = str(time.time()) m = hashlib.md5(bytes(username, encoding="utf8")) # 加鹽 m.update(bytes(timestamp, encoding="utf8")) return m.hexdigest() class LoginView(APIView): """ 校驗用戶名密碼是否正確從而生成token的視圖 """ authentication_classes = [ ] permission_classes=[] throttle_classes = [MyThrottle,] def post(self, request): self.dispatch res = {"code": 0} username = request.data.get("username") password = request.data.get("password") user = models.UserInfo.objects.filter(username=username, password=password).first() if user: # 若是用戶名密碼正確 token = get_random_token(username) # 找到user對象,有就更新(更新token_code),沒有就建立 models.Token.objects.update_or_create(defaults={"token_code": token}, user=user) res["token"] = token else: res["code"] = 1 res["error"] = "用戶名或密碼錯誤" return JsonResponse(res) def get(self,request): return HttpResponse('login get') class OrderView(APIView): # permission_classes = [MyPermission,] def get(self,request): return HttpResponse('這裏是訂單頁面,只有vip用戶能夠訪問')
定義全局節流
REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": ('app01.utils.MyAuth.MyAuth', ), 'DEFAULT_PERMISSION_CLASSES':('app01.utils.MyPermission.MyPermission',), 'DEFAULT_THROTTLE_CLASSES':('app01.utils.MyThrottle.MyThrottle',) }
一樣咱們能夠用框架自帶的節流類
class MyThrottle2(SimpleRateThrottle): rate='3/m' def get_cache_key(self, request, view): return request.META.get('REMOTE_ADDR')
能夠省略寫不少邏輯,只要看看自帶的類的要求就行了。如上,rate是頻率,重寫的函數也是要求的。其實allow_request中含有重寫的函數。重寫的函數返回的是像IP,用戶名之類的能標示身份的key,去緩存中進行操做。在自定義的類中實現了類似的功能。注意節流通常不設置多個類,會重複,某個視圖須要特殊驗證, 就重寫throttle_classes。
咱們CBV執行流程中,會先走dispatch()方法,當咱們的視圖對應的類繼承的是rest_framework.views 的APIView時,在對request進行封裝以後,執行initial(request, *args, **kwargs)方法,進入發如今執行認證以前有對版本
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme
進入determine_version(),
scheme = self.versioning_class(),versioning_class()默認是讀配置文件,scheme 也就是版本控制類的對象。
執行scheme .determine_version()。
框架自帶的控制方案
這裏咱們以URLPathVersioning爲例。
from rest_framework.versioning import URLPathVersioning
發現其中有determine_version,其中的邏輯就不仔細贅述。有幾個須要配置的參數。
default_version = api_settings.DEFAULT_VERSION 默認版本
allowed_versions = api_settings.ALLOWED_VERSIONS 容許的版本
version_param = api_settings.VERSION_PARAM 對應url中的版本參數
REST_FRAMEWORK = { 'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.URLPathVersioning', #得到版本類 'DEFAULT_VERSION':'v2', #默認版本 'ALLOWED_VERSIONS':['v1','v2'], #容許版本 'VERSION_PARAM':'version', #取版本的字段在url中 }
from django.contrib import admin from django.urls import path,include,re_path from api import views urlpatterns = [ # path('admin/', admin.site.urls), re_path(r'^(?P<version>[v1|v2]+)/$',views.test.as_view()), ]
咱們在視圖中能夠經過訪問 request.version 來獲取當前請求的具體版本。
注意,一般咱們是不會單獨給某個視圖設置版本控制的,若是你確實須要給單獨的視圖設置版本控制,你能夠在視圖中設置versioning_class屬性,以下:
class PublisherViewSet(ModelViewSet): ... versioning_class = URLPathVersioning
解析器的做用就是服務端接收客戶端傳過來的數據,把數據解析成本身能夠處理的數據。本質就是對請求體中的數據進行解析。 在瞭解解析器以前,咱們要先知道Accept以及ContentType請求頭。 Accept是告訴對方我能解析什麼樣的數據,一般也能夠表示我想要什麼樣的數據。 ContentType是告訴對方我給你的是什麼樣的數據類型(服務端)。
解析器工做原理的就是拿到請求的ContentType來判斷前端給個人數據類型是什麼,而後咱們在後端使用相應的解析器去解析數據。
ContentType對應的值(補充)
application/x-www-form-urlencoded:
窗體數據被編碼爲名稱/值對,這是標準且默認的編碼格式。當action爲get時候,客戶端把form數據轉換成一個字串append到url後面,用?分割。當action爲post時候,瀏覽器把form數據封裝到http body中,而後發送到server。
application/x-www-form-urlencoded 傳遞時的數據構造:
......
username=twm&email=good@qq.com
......
multipart/form-data:
multipart表示的意思是單個消息頭包含多個消息體的解決方案。multipart媒體類型對發送非文本的各媒體類型是有用的。通常多用於文件上傳。
multipart/form-data只是multipart的一種。
application/json
ok,仍是從dispatch()開始,進去以後在對request進行封裝的initialize_request,進去發現對request封裝了parsers=self.get_parsers(),進去get_parsers(),返回的是解析類的對象列表[parser() for parser in self.parser_classes],parser_classes仍是讀的配置文件。
咱們進行請求數據經過request.data進行的,咱們經過查看Request(from rest_framework.request import Request),
其中的data,看看其中的_load_data_and_files(),進去看看self._parse(),發現其中的
parser = self.negotiator.select_parser(self, self.parsers)是根據ContentType選擇解析器
parsed = parser.parse(stream, media_type, self.parser_context)是對數據進行解析
最後返回return (parsed.data, parsed.files)。
總結:
全局配置解析器
REST_FRAMEWORK = { 'DEFAULT_PARSER_CLASSES': ( 'rest_framework.parsers.JSONParser', ) }
局部視圖配置解析器(配置後該視圖只能解析json數據。POST請求中)
parser_classes = [JSONParser, ]
restframework中的序列化主要功能有對django數據(如queryset)進行序列化,對數據進行校驗。
分別從普通的一個表,外鍵,多對多進行對數據序列化和校驗。
from django.db import models # Create your models here. class Article(models.Model): id=models.AutoField(primary_key=True) title=models.CharField(max_length=64) create_time=models.DateField(auto_now=True) type=models.SmallIntegerField( choices=((1,'原創'),(2,'轉載')), default=1 ) source=models.ForeignKey(to='Source',on_delete=models.CASCADE) tag=models.ManyToManyField(to='Tag') class Source(models.Model): id = models.AutoField(primary_key=True) name=models.CharField(max_length=32,unique=True,error_messages={'unique':'不能重複'}) class Tag(models.Model): id = models.AutoField(primary_key=True) name=models.CharField(max_length=32) class Comment(models.Model): content=models.CharField(max_length=255) article=models.ForeignKey(to='Article',on_delete=models.CASCADE)
# 表單的get和post re_path(r'my_source/$',views.MySourceView.as_view()), # 外鍵的get和post re_path(r'my_comment/$',views.MyCommentView.as_view()), # 多對多 re_path(r'my_article/$',views.MyArticleView.as_view()),
視圖:
# 表單的get和post # 序列化類 class MySourceViewSerializer(serializers.ModelSerializer): # 對數據進行序列化 class Meta: model=Source fields='__all__' # 對數據進行自定義校驗規則格式validate_字段名,value就是字段的值 def validate_name(self, value): # print('self',self) # print('value',value) if not value.endswith('出版社'): raise ValidationError('必須以 出版社 結尾') return value class MySourceView(APIView): def get(self,request,*args,**kwargs): res={'code':0} all_source=Source.objects.all() ser_obj=MySourceViewSerializer(instance=all_source,many=True) res['data']=ser_obj.data print(ser_obj.data) return HttpResponse(json.dumps(res,ensure_ascii=False)) def post(self,request,*args,**kwargs): res={'code':0} ser_obj=MySourceViewSerializer(data=request.data) if ser_obj.is_valid(): ser_obj.save() return HttpResponse(json.dumps(res, ensure_ascii=False)) else: res['code']=1 res['error']=ser_obj.errors return HttpResponse(json.dumps(res, ensure_ascii=False)) # 外鍵的get和post # 序列化類 class MyCommentSerializer(serializers.ModelSerializer): # article_name=serializers.CharField(source='article.title') # content=serializers.CharField() class Meta: model=Comment # fields=['content',] fields='__all__' extra_kwargs = { "content": {"error_messages": {"required": "評論內容不能爲空"}}, "article": {"error_messages": {"required": "文章不能爲空"}} } class MyCommentView(APIView): def get(self,request,*args,**kwargs): res={'code':0} all_data=Comment.objects.all() ser_obj=MyCommentSerializer(instance=all_data,many=True) res['data']=ser_obj.data return HttpResponse(json.dumps(res, ensure_ascii=False)) def post(self, request, *args, **kwargs): res = {'code': 0} ser_obj=MyCommentSerializer(data=request.data) if ser_obj.is_valid(): ser_obj.save() else: res['code']=1 res['error']=ser_obj.errors return HttpResponse(json.dumps(res, ensure_ascii=False)) # 多對多的get和post # 序列化類 class MyTagSerializer(serializers.ModelSerializer): class Meta: model=Tag fields='__all__' class MyArticleSerializer(serializers.ModelSerializer): type=serializers.CharField(source='get_type_display') tag=TagViewSerializer(many=True) class Meta: model=Article fields=['id','title','type','source','tag'] # fields='__all__' class MyArticleWriteSerializer(serializers.ModelSerializer): class Meta: model=Article fields='__all__' extra_kwargs = { "tag": { "error_messages": { "does_not_exist": '"{pk_value}"對應的tag對象不存在。' } } } class MyArticleView(APIView): def get(self, request, *args, **kwargs): res = {'code': 0} article_list=Article.objects.all() ser_obj=MyArticleSerializer(instance=article_list,many=True) res['data']=ser_obj.data # print('ret',ret) return Response(res) def post(self, request, *args, **kwargs): res = {"code": 0} ser_obj = MyArticleWriteSerializer(data=request.data) if ser_obj.is_valid(): ser_obj.save() else: res["code"] = 1 res["error"] = ser_obj.errors return Response(res)
還有超連接的序列化(返回前端某些字段是一個連接)
# 超連接的序列化 re_path(r'my_articlelinked/$',views.MyArticleLinkView.as_view()), re_path(r'my_source/(P<pk>\d+)/$',views.MySourceDetialView.as_view(),name='source_detail'),
# 超連接的序列化 # 序列化類 class ArticleHyperlinkedSerializer(serializers.HyperlinkedModelSerializer): # view_name=要反向生成的url, # lookup_field=數據庫中對應的顯示在生成的url上的字段值, # lookup_url_kwarg=要生成url對應的(?P<pk>\d+), source=serializers.HyperlinkedIdentityField( view_name='source_detail', lookup_field='source_id', lookup_url_kwarg='pk', ) class Meta: model=Article fields=["id", "type", "title", "source"] depth=1 class MyArticleLinkView(APIView): def get(self, request, *args, **kwargs): res = {"code": 0} article_list = Article.objects.all() ser_obj=ArticleHyperlinkedSerializer(instance=article_list, many=True, context={'request': request}) res["data"] = ser_obj.data return Response(res) class MySourceDetialView(APIView): def get(self, request, *args, **kwargs): source_id=kwargs.get('pk') Source.objects.filter(id=source_id) return HttpResponse(Source.objects.filter(id=source_id).first().name)
rest framework中提供了三種分頁模式:
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', 'PAGE_SIZE': 100 }
咱們能夠在視圖類中進行局部設置
class PublisherViewSet(ModelViewSet):
queryset = models.Publisher.objects.all()
serializer_class = PublisherModelSerializer
pagination_class = PageNumberPagination # 注意不是列表(只能有一個分頁模式)
分頁實例:
路由:
# PageNumberPagination # 按頁碼數分頁,第n頁,每頁顯示m條數據 # 例如:http://127.0.0.1:8000/api/article/?page=2&size=1 re_path(r'article_page1/$',views.ArticlePage1View.as_view()), # LimitOffsetPagination # 分頁,在n位置,向後查看m條數據 # 例如:http://127.0.0.1:8000/api/article/?offset=2&limit=2 re_path(r'article_page2/$',views.ArticlePage2View.as_view()), # CursorPagination # 加密分頁,把上一頁和下一頁的id值記住 re_path(r'article_page3/$',views.ArticlePage3View.as_view()),
視圖:
# 分頁 # PageNumberPagination # 按頁碼數分頁,第n頁,每頁顯示m條數據 from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination,CursorPagination from rest_framework.response import Response #分頁器 class MyPageNumber(PageNumberPagination): # The default page size. # Defaults to `None`, meaning pagination is disabled. page_size = 1 #每頁顯示多少條 # Client can control the page using this query parameter. page_query_param ='page' #url中頁碼的參數 # Client can control the page size using this query parameter. # Default is 'None'. Set to eg 'page_size' to enable usage. page_size_query_param = 'size' #url中每頁顯示條數的參數 # Set to an integer to limit the maximum page size the client may request. # Only relevant if 'page_size_query_param' has also been set. max_page_size = 20 # 序列化類 class ArticlePage1Serializer(serializers.ModelSerializer): class Meta: model=Article fields='__all__' depth=1 class ArticlePage1View(APIView): def get(self, request, *args, **kwargs): print(request.GET) res={"code":0} article_list = Article.objects.all().order_by("id") #分頁 page_obj=MyPageNumber() page_article=page_obj.paginate_queryset( queryset=article_list,request=request,view=self ) ser_obj=ArticlePage1Serializer(instance=page_article,many=True) res['data']=ser_obj.data return Response(res) # 返回帶有頁碼連接的響應 # return page_obj.get_paginated_response(res) # LimitOffsetPagination # 分頁,在n位置,向後查看m條數據 # 分頁器 class MyLimitOffSet(LimitOffsetPagination): default_limit = 1 limit_query_param = 'limit' #向後查看limit條數據 offset_query_param = 'offset' #在offset位置 max_limit = 999 class ArticlePage2View(APIView): def get(self, request, *args, **kwargs): res = {"code": 0} article_list = Article.objects.all().order_by("id") page_obj=MyLimitOffSet() page_article=page_obj.paginate_queryset( queryset=article_list, view=self, request=request ) ser_obj=ArticlePage1Serializer(instance=page_article,many=True) res['data']=ser_obj.data return Response(res) # CursorPagination # 加密分頁,把上一頁和下一頁的id值記住 # 分頁器 class MyCursor(CursorPagination): cursor_query_param = 'cursor' page_size = 1 ordering = '-id' #排序字段 class ArticlePage3View(APIView): def get(self, request, *args, **kwargs): res = {"code": 0} article_list = Article.objects.all().order_by("id") page_obj=MyCursor() page_article=page_obj.paginate_queryset( queryset=article_list, view=self, request=request ) ser_obj=ArticlePage1Serializer(instance=page_article,many=True) res['data']=ser_obj.data return page_obj.get_paginated_response(res)
渲染器同解析器相反,它定義了框架按照content_type來返回不一樣的響應。
想有restframework自帶的比較好看的頁面在app中註冊restframework。
DRF提供的渲染器有不少,默認是
'DEFAULT_RENDERER_CLASSES': ( 'rest_framework.renderers.JSONRenderer', 'rest_framework.renderers.BrowsableAPIRenderer', ),
咱們也能夠在視圖中局部設置也能夠在全局的settings.py中進行設置:
class PublisherViewSet(ModelViewSet): queryset = models.Publisher.objects.all() serializer_class = PublisherModelSerializer renderer_classes = [JSONRenderer, ]
這樣設置後就只能返回JSON格式的數據了,並不會像以前同樣提供一個閱讀友好的web頁面。
REST_FRAMEWORK = { 'DEFAULT_RENDERER_CLASSES': ( 'rest_framework.renderers.JSONRenderer', ), }
注意,在視圖類中定義的配置項的優先級要高於全局配置中的配置項。
舉一個例子分別從手寫路由,到半自動路由,到利用restframework的全自動路由
"""視圖和路由 URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/2.0/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: path('', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin from django.urls import path,re_path,include from app01 import views from rest_framework import routers router = routers.DefaultRouter() router.register('users',views.TestView3) urlpatterns = [ # path('admin/', admin.site.urls), # re_path(r'^school/$',views.SchoolView.as_view()), # re_path(r'^schooldetail/(?P<pk>\d+)/$',views.SchoolDetail.as_view()), # 路由系統 # 自定義路由 re_path(r'^test/$',views.TestView.as_view()), re_path(r'^test\.(?P<format>[a-z0-9]+)/$',views.TestView.as_view()), re_path(r'^test/(?P<pk>[^/.]+)/$',views.TestView.as_view()), re_path(r'^test/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/$',views.TestView.as_view()), # 半自動路由 re_path(r'^test2/$',views.TestView2.as_view({'get': 'list','post':'create'})), # 列出全部 re_path(r'^test2/(?P<pk>\d+)/$',views.TestView2.as_view({'get': 'retrieve', 'delete':'destroy', 'put':'update', 'patch':'partial_update'})), # retrieve檢索出某一個 ,update更新,partial_update局部更新 # 全自動路由 re_path(r'^',include(router.urls)) ]
from django.shortcuts import render,HttpResponse from rest_framework import serializers from rest_framework.views import * from rest_framework import mixins from rest_framework import generics from app01 import models # 路由系統 # 自定義路由 class TestView(APIView): def get(self, request, *args, **kwargs): print(kwargs) print(self.renderer_classes) return Response('...') # 半自動路由 from rest_framework.viewsets import ModelViewSet # 序列化類 class UserInfoSerializer(serializers.ModelSerializer): class Meta: model=models.UserInfo fields='__all__' class TestView2(ModelViewSet): queryset = models.UserInfo.objects.all() serializer_class = UserInfoSerializer # 全自動路由 class TestView3(ModelViewSet): queryset = models.UserInfo.objects.all() serializer_class = UserInfoSerializer
分狀況而用越往下功能越多,通常用APIView(比較原生)
用GenericViewSet加上個別的像ListModelMixin實現部分功能,
用ModelView實現增刪改查羣補功能。具體例子參考路由例子。