1. 程式人生 > >RESTFULL 07 rest-framework分頁器,相應器,解析器,url控制器,版本控制

RESTFULL 07 rest-framework分頁器,相應器,解析器,url控制器,版本控制

訪問 trie htm 獲取 elf temp ren cat view

rest-framework分頁器,相應器,解析器,url控制器,版本控制

一、分頁器

1、簡單分頁(查看第n頁,每頁顯示n條)

from rest_framework.pagination import PageNumberPagination
# 一 基本使用:url=url=http://127.0.0.1:8000/pager/?page=2&size=3,size無效
class  Pager(APIView):
    def get(self,request,*args,**kwargs):
        # 獲取所有數據
        ret=models.Book.objects.all()
        # 創建分頁對象
        page=PageNumberPagination()
        # 在數據庫中獲取分頁的數據
        page_list=page.paginate_queryset(ret,request,view=self)
        # 對分頁進行序列化
        ser=BookSerializer1(instance=page_list,many=True)
        return Response(ser.data)
# 二 自定制 url=http://127.0.0.1:8000/pager/?page=2&size=3
# size=30,無效,最多5條
class Mypage(PageNumberPagination):
    page_size = 2
    page_query_param = 'page'
    # 定制傳參
    page_size_query_param = 'size'
    # 最大一頁的數據
    max_page_size = 5
class  Pager(APIView):
    def get(self,request,*args,**kwargs):
        # 獲取所有數據
        ret=models.Book.objects.all()
        # 創建分頁對象
        page=Mypage()
        # 在數據庫中獲取分頁的數據
        page_list=page.paginate_queryset(ret,request,view=self)
        # 對分頁進行序列化
        ser=BookSerializer1(instance=page_list,many=True)
        # return Response(ser.data)
        # 這個也是返回Response對象,但是比基本的多了上一頁,下一頁,和總數據條數(了解即可)
        return page.get_paginated_response(ser.data)

setting裏

REST_FRAMEWORK = {
    # 每頁顯示兩條
    'PAGE_SIZE':2
}

路由:

url(r'^pager/$', views.Pager.as_view()),

Serializers

class BookSerializer1(serializers.ModelSerializer):
    class Meta:
        model=models.Book
        # fields="__all__"
        exclude=('authors',)

使用方法:

  • 1 導入PageNumberPagination類
  • 2 實例化產生一個對象
    page_pagination = PageNumberPagination()
  • 3 需要配置四個參數:page_size,page_query_param,page_size_query_param,max_page_size
    page_size必須指定:在setting中指定或者直接賦值
  • 4 調用下面的方法,返回ret數據
    ret = page_pagination.paginate_queryset(book_list, request, self)
  • 5 再序列化,就是序列化返回的數據,也就是ret

類中需要掌握的幾個屬性

-page_size:控制每頁顯示條數
-page_query_param:控制查詢第幾頁的查詢參數,
-比如page_query_param='xx'
-http://127.0.0.1:8000/books/?xx=2  表示查詢第二頁的數據
-page_size_query_param:控制每頁最大顯示的條數
-比如page_pagination.page_size_query_param='max'
-http://127.0.0.1:8000/books/?xx=2&max=6   表示查詢第二頁的數據,每頁顯示6條
-max_page_size:控制每頁最大顯示的條數
-比如:page_pagination.max_page_size=7
-http://127.0.0.1:8000/books/?max=1000    最多顯示7條

2、偏移分頁(在第n個位置,向後查看n條數據)

使用方式:

? 同簡單分頁

重點的參數

-default_limit:默認每頁顯示的條數,默認偏移的數量
-比如:default_limit=5
-http://127.0.0.1:8000/books/    就會顯示5條數據
-limit_query_param:往後偏移多少條
-就用默認值:limit
-offset_query_param:標桿值
-用默認值:offset
limit_query_param+offset_query_param聯合起來用:
-訪問:http://127.0.0.1:8000/books/?limit=1&offset=5  表示:以數據的第5條作為標桿,往後偏移1條
-max_limit:最大偏移的條數(最大取出的條數)
# http://127.0.0.1:8000/pager/?offset=4&limit=3
from rest_framework.pagination import LimitOffsetPagination
# 也可以自定制,同簡單分頁
class  Pager(APIView):
    def get(self,request,*args,**kwargs):
        # 獲取所有數據
        ret=models.Book.objects.all()
        # 創建分頁對象
        page=LimitOffsetPagination()
        # 在數據庫中獲取分頁的數據
        page_list=page.paginate_queryset(ret,request,view=self)
        # 對分頁進行序列化
        ser=BookSerializer1(instance=page_list,many=True)
        # return page.get_paginated_response(ser.data)
        return Response(ser.data)

3、 CursorPagination(加密分頁,只能看上一頁和下一頁,速度快)

重點參數:

-page_size:每頁顯示的條數
-cursor_query_param:不需要動
-ordering:按什麽排序

通過get_paginated_response返回結果中帶上一頁和下一頁的鏈接地址

page_pagination.get_paginated_response(book_ser.data) 方法的用法
from rest_framework.pagination import CursorPagination
# 看源碼,是通過sql查詢,大於id和小於id
class  Pager(APIView):
    def get(self,request,*args,**kwargs):
        # 獲取所有數據
        ret=models.Book.objects.all()
        # 創建分頁對象
        page=CursorPagination()
        page.ordering='nid'
        # 在數據庫中獲取分頁的數據
        page_list=page.paginate_queryset(ret,request,view=self)
        # 對分頁進行序列化
        ser=BookSerializer1(instance=page_list,many=True)
        # 可以避免頁碼被猜到
        return page.get_paginated_response(ser.data)

二、url控制器

1、自定義路由

from django.conf.urls import url
from app01 import views
urlpatterns = [
    url(r'^books/$', views.BookView.as_view()),
    url(r'^books/(?P<pk>\d+)$', views.BookDetailView.as_view()),
]
class BookView(APIView):

    def get(self, request):
        book_list = models.Book.objects.all()
        bs = BookSerializers(book_list, many=True)
        return Response(bs.data)

    def post(self, request):
        # 添加一條數據
        print(request.data)

        bs=BookSerializers(data=request.data)
        if bs.is_valid():
            bs.save()  # 生成記錄
            return Response(bs.data)
        else:

            return Response(bs.errors)

class BookDetailView(APIView):
    def get(self,request,pk):
        book_obj=models.Book.objects.filter(pk=pk).first()
        bs=BookSerializers(book_obj,many=False)
        return Response(bs.data)
    def put(self,request,pk):
        book_obj = models.Book.objects.filter(pk=pk).first()

        bs=BookSerializers(data=request.data,instance=book_obj)
        if bs.is_valid():
            bs.save() # update
            return Response(bs.data)
        else:
            return Response(bs.errors)
    def delete(self,request,pk):
        models.Book.objects.filter(pk=pk).delete()

        return Response("")

2、半自動路由(試圖類繼承ModelViewSet)

from django.conf.urls import url
from app01 import views
urlpatterns = [
    url(r'^publish/$', views.PublishView.as_view({'get':'list','post':'create'})),
    url(r'^publish/(?P<pk>\d+)/$', views.PublishView.as_view({'get':'retrieve','put':'update','delete':'destroy'})),

]
from rest_framework.viewsets import ModelViewSet
class PublishView(ModelViewSet):
    queryset=models.Publish.objects.all()
    serializer_class=PublishSerializers

3、全自動路由(自動路由生成)

from django.conf.urls import url,include
from app01 import views
from rest_framework import routers
#生成一個router對象
router=routers.DefaultRouter()
# 兩個參數,一個是匹配的路由,一個是視圖中寫的CBV的類
router.register('publish',views.PublishView)
urlpatterns = [
    # http://127.0.0.1:8000/publish/format=json(渲染器通過這個判斷,返回渲染的頁面)
    # url(r'^publish/', views.PublishView.as_view({'get':'list','post':'create'})),
    # http://127.0.0.1:8000/publish.json(渲染器通過這個判斷,返回渲染的頁面)
    # url(r'^publish\.(?P<format>\w+)$', views.PublishView.as_view({'get':'list','post':'create'})),
    
    # 可以用 以下方式訪問
    # 1 http://127.0.0.1:8000/publish/
    # 2 http://127.0.0.1:8000/publish.json
    # 3 http://127.0.0.1:8000/publish/3
    # 4 http://127.0.0.1:8000/publish/3.json   
    url(r'',include(router.urls))
]
from rest_framework.viewsets import ModelViewSet
class PublishView(ModelViewSet):
    queryset=models.Publish.objects.all()
    serializer_class=PublishSerializers

三、響應器

1、 作用

根據 用戶請求URL 或 用戶可接受的類型,篩選出合適的 渲染組件。
用戶請求URL:
http://127.0.0.1:8000/test/?format=json
http://127.0.0.1:8000/test.json

2、 內置渲染器

顯示json格式:JSONRenderer

訪問URL:

  • http://127.0.0.1:8000/test/?format=json
  • http://127.0.0.1:8000/test.json
  • http://127.0.0.1:8000/test/

默認顯示格式:BrowsableAPIRenderer(可以修改它的html文件)

訪問URL:

  • http://127.0.0.1:8000/test/?format=api
  • http://127.0.0.1:8000/test.api
  • http://127.0.0.1:8000/test/

表格方式:AdminRenderer

訪問URL:

  • http://127.0.0.1:8000/test/?format=admin
  • http://127.0.0.1:8000/test.admin
  • http://127.0.0.1:8000/test/

form表單方式:HTMLFormRenderer

訪問URL:

  • http://127.0.0.1:8000/test/?format=form
  • http://127.0.0.1:8000/test.form
  • http://127.0.0.1:8000/test/

3、局部使用

from rest_framework.renderers import  HTMLFormRenderer,BrowsableAPIRenderer
class BookDetailView(APIView):
    renderer_classes = [HTMLFormRenderer,BrowsableAPIRenderer ]
    def get(self,request,pk):
        book_obj=models.Book.objects.filter(pk=pk).first()
        bs=BookSerializers(book_obj,many=False)
        return Response(bs.data)
    def put(self,request,pk):
        book_obj = models.Book.objects.filter(pk=pk).first()
        bs=BookSerializers(data=request.data,instance=book_obj)
        if bs.is_valid():
            bs.save() # update
            return Response(bs.data)
        else:
            return Response(bs.errors)
    def delete(self,request,pk):
        models.Book.objects.filter(pk=pk).delete()

        return Response("")

4、全局使用

settings裏配置:

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES':['rest_framework.renderers.JSONRenderer']
}

5、自定義顯示模版

from rest_framework.renderers import  TemplateHTMLRenderer
class BookDetailView(APIView):
    renderer_classes = [TemplateHTMLRenderer]
    def get(self,request,pk):
        book_obj=models.Book.objects.filter(pk=pk).first()
        bs=BookSerializers(book_obj,many=False)
        return Response(bs.data,template_name='aa.html')
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{{ title }}
{{ publishDate }}
</body>
</html>

註意:如果同時多個存在時,自動根據URL後綴來選擇渲染器。

四、解析器

1、解析器的作用

根據請求頭 content-type 選擇對應的解析器對請求體內容進行處理。

有application/json,x-www-form-urlencoded,form-data等格式

2、全局使用解析器

setting裏

REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES':[
        'rest_framework.parsers.JSONParser'
        'rest_framework.parsers.FormParser'
        'rest_framework.parsers.MultiPartParser'
    ]

}

路由:

urlpatterns = [
    url(r'test/', TestView.as_view()),
]

視圖函數:

from rest_framework.views import APIView
from rest_framework.response import Response

class TestView(APIView):
    def post(self, request, *args, **kwargs):
        print(request.content_type)

        # 獲取請求的值,並使用對應的JSONParser進行處理
        print(request.data)
        # application/x-www-form-urlencoded 或 multipart/form-data時,request.POST中才有值
        print(request.POST)
        print(request.FILES)
        return Response('POST請求,響應內容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求,響應內容')

3、局部使用解析器

a. 僅處理請求頭content-type為application/json的請求體

from django.conf.urls import url, include
from web.views.s5_parser import TestView

urlpatterns = [
    url(r'test/', TestView.as_view(), name='test'),
]
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import JSONParser


class TestView(APIView):
    parser_classes = [JSONParser, ]

    def post(self, request, *args, **kwargs):
        print(request.content_type)

        # 獲取請求的值,並使用對應的JSONParser進行處理
        print(request.data)

        # application/x-www-form-urlencoded 或 multipart/form-data時,request.POST中才有值
        print(request.POST)
        print(request.FILES)

        return Response('POST請求,響應內容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求,響應內容')

b. 僅處理請求頭content-type為application/x-www-form-urlencoded 的請求體

from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'test/', TestView.as_view(), name='test'),
]
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import FormParser


class TestView(APIView):
    parser_classes = [FormParser, ]

    def post(self, request, *args, **kwargs):
        print(request.content_type)

        # 獲取請求的值,並使用對應的JSONParser進行處理
        print(request.data)

        # application/x-www-form-urlencoded 或 multipart/form-data時,request.POST中才有值
        print(request.POST)
        print(request.FILES)

        return Response('POST請求,響應內容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求,響應內容')

c. 僅處理請求頭content-type為multipart/form-data的請求體

from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'test/', TestView.as_view(), name='test'),
]
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import MultiPartParser


class TestView(APIView):
    parser_classes = [MultiPartParser, ]

    def post(self, request, *args, **kwargs):
        print(request.content_type)

        # 獲取請求的值,並使用對應的JSONParser進行處理
        print(request.data)
        # application/x-www-form-urlencoded 或 multipart/form-data時,request.POST中才有值
        print(request.POST)
        print(request.FILES)
        return Response('POST請求,響應內容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求,響應內容')
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="http://127.0.0.1:8000/test/" method="post" enctype="multipart/form-data">
    <input type="text" name="user" />
    <input type="file" name="img">

    <input type="submit" value="提交">

</form>
</body>
</html>

d. 僅上傳文件

from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'test/(?P<filename>[^/]+)', TestView.as_view(), name='test'),
]
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import FileUploadParser


class TestView(APIView):
    parser_classes = [FileUploadParser, ]

    def post(self, request, filename, *args, **kwargs):
        print(filename)
        print(request.content_type)

        # 獲取請求的值,並使用對應的JSONParser進行處理
        print(request.data)
        # application/x-www-form-urlencoded 或 multipart/form-data時,request.POST中才有值
        print(request.POST)
        print(request.FILES)
        return Response('POST請求,響應內容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求,響應內容')
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="http://127.0.0.1:8000/test/f1.numbers" method="post" enctype="multipart/form-data">
    <input type="text" name="user" />
    <input type="file" name="img">

    <input type="submit" value="提交">

</form>
</body>
</html>

e. 同時多個Parser

當同時使用多個parser時,rest framework會根據請求頭content-type自動進行比對,並使用對應parser

from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'test/', TestView.as_view(), name='test'),
]
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import JSONParser, FormParser, MultiPartParser


class TestView(APIView):
    parser_classes = [JSONParser, FormParser, MultiPartParser, ]

    def post(self, request, *args, **kwargs):
        print(request.content_type)

        # 獲取請求的值,並使用對應的JSONParser進行處理
        print(request.data)
        # application/x-www-form-urlencoded 或 multipart/form-data時,request.POST中才有值
        print(request.POST)
        print(request.FILES)
        return Response('POST請求,響應內容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求,響應內容')

4、源碼分析

1 在調用request.data時,才進行解析,由此入手
    @property
    def data(self):
        if not _hasattr(self, '_full_data'):
            self._load_data_and_files()
        return self._full_data
        
2 查看self._load_data_and_files()方法---->self._data, self._files = self._parse()

        def _parse(self):
            #用戶請求頭裏content_type的值
            media_type = self.content_type

            #self.parsers 就是用戶配置的parser_classes = [FileUploadParser,FormParser ]
            #self裏就有content_type,傳入此函數
            parser = self.negotiator.select_parser(self, self.parsers)

3 查看self.negotiator.select_parser(self, self.parsers)
     def select_parser(self, request, parsers):
        #同過media_type和request.content_type比較,來返回解析器,然後調用解析器的解析方法
        #每個解析器都有media_type = 'multipart/form-data'屬性
        for parser in parsers:
            if media_type_matches(parser.media_type, request.content_type):
                return parser
        return None
    
4 最終調用parser的解析方法來解析parsed = parser.parse(stream, media_type, self.parser_context)
1 Request實例化,parsers=self.get_parsers()
    Request(
                request,
                parsers=self.get_parsers(),
                authenticators=self.get_authenticators(),
                negotiator=self.get_content_negotiator(),
                parser_context=parser_context
            )
2 get_parsers方法,循環實例化出self.parser_classes中類對象
    def get_parsers(self):
        return [parser() for parser in self.parser_classes]            

3 self.parser_classes 先從類本身找,找不到去父類找即APIVIew 中的
    parser_classes = api_settings.DEFAULT_PARSER_CLASSES
4 api_settings是一個對象,對象裏找DEFAULT_PARSER_CLASSES屬性,找不到,會到getattr方法
        def __getattr__(self, attr):
            if attr not in self.defaults:
                raise AttributeError("Invalid API setting: '%s'" % attr)

            try:
                #調用self.user_settings方法,返回一個字典,字典再取attr屬性
                val = self.user_settings[attr]
            except KeyError:
                # Fall back to defaults
                val = self.defaults[attr]

            # Coerce import strings into classes
            if attr in self.import_strings:
                val = perform_import(val, attr)

            # Cache the result
            self._cached_attrs.add(attr)
            setattr(self, attr, val)
            return val
 5 user_settings方法 ,通過反射去setting配置文件裏找REST_FRAMEWORK屬性,找不到,返回空字典
    @property
    def user_settings(self):
        if not hasattr(self, '_user_settings'):
            self._user_settings = getattr(settings, 'REST_FRAMEWORK', {})
        return self._user_settings

RESTFULL 07 rest-framework分頁器,相應器,解析器,url控制器,版本控制