Django Rest framework

首先咱們須要先了解一下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)
models.py
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')
views.py

 

若是咱們本身寫了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用戶能夠訪問')
view.py

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用戶能夠訪問')
views.py

定義全局節流

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)
models.py
    # 表單的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)
views.py

 

還有超連接的序列化(返回前端某些字段是一個連接)

# 超連接的序列化
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)
views.py

 

渲染器

渲染器同解析器相反,它定義了框架按照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))

]
urls.py
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
views.py

 

視圖系統

分狀況而用越往下功能越多,通常用APIView(比較原生)

用GenericViewSet加上個別的像ListModelMixin實現部分功能,

用ModelView實現增刪改查羣補功能。具體例子參考路由例子。

相關文章
相關標籤/搜索