Django_rest_framework

什麼是restful

  • REST與技術無關,表明的是一種軟件架構風格,REST是Representational State Transfer的簡稱,中文翻譯爲「表徵狀態轉移」
  • REST從資源的角度類審視整個網絡,它將分佈在網絡中某個節點的資源經過URL進行標識,客戶端應用經過URL來獲取資源的表徵,得到這些表徵導致這些應用轉變狀態
  • 全部的數據,不過是經過網絡獲取的仍是操做(增刪改查)的數據,都是資源,將一切數據視爲資源是REST區別與其餘架構風格的最本質屬性
  • 對於REST這種面向資源的架構風格,有人提出一種全新的結構理念,即:面向資源架構(ROA:Resource Oriented Architecture)

什麼是API

一、什麼是API?html

答:API就是接口,提供的url。接口有兩個用途:vue

  • - 爲別人提供服務
  • - 先後端分離,一個寫vue,一個寫後端,他們之間都是經過ajax請求

restful API設計規範

  • API與用戶的通訊協議,老是使用HTTPS協議。
  • 域名 
    • https://api.example.com                         儘可能將API部署在專用域名(會存在跨域問題)
    • https://example.org/api/                        API很簡單
  • 版本
    • URL,如:https://api.example.com/v1/
    • 請求頭                                                  跨域時,引起發送屢次請求
  • 路徑,視網絡上任何東西都是資源,均使用名詞表示(可複數)
    • https://api.example.com/v1/zoos
    • https://api.example.com/v1/animals
    • https://api.example.com/v1/employees
  • method
    • GET      :從服務器取出資源(一項或多項)
    • POST    :在服務器新建一個資源
    • PUT      :在服務器更新資源(客戶端提供改變後的完整資源)
    • PATCH  :在服務器更新資源(客戶端提供改變的屬性)
    • DELETE :從服務器刪除資源
  • 過濾,經過在url上傳參的形式傳遞搜索條件
    • https://api.example.com/v1/zoos?limit=10:指定返回記錄的數量
    • https://api.example.com/v1/zoos?offset=10:指定返回記錄的開始位置
    • https://api.example.com/v1/zoos?page=2&per_page=100:指定第幾頁,以及每頁的記錄數
    • https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回結果按照哪一個屬性排序,以及排序順序
    • https://api.example.com/v1/zoos?animal_type_id=1:指定篩選條件
  • 狀態碼
200 OK - [GET]:服務器成功返回用戶請求的數據,該操做是冪等的(Idempotent)。
201 CREATED - [POST/PUT/PATCH]:用戶新建或修改數據成功。
202 Accepted - [*]:表示一個請求已經進入後臺排隊(異步任務)
204 NO CONTENT - [DELETE]:用戶刪除數據成功。
400 INVALID REQUEST - [POST/PUT/PATCH]:用戶發出的請求有錯誤,服務器沒有進行新建或修改數據的操做,該操做是冪等的。
401 Unauthorized - [*]:表示用戶沒有權限(令牌、用戶名、密碼錯誤)。
403 Forbidden - [*] 表示用戶獲得受權(與401錯誤相對),可是訪問是被禁止的。
404 NOT FOUND - [*]:用戶發出的請求針對的是不存在的記錄,服務器沒有進行操做,該操做是冪等的。
406 Not Acceptable - [GET]:用戶請求的格式不可得(好比用戶請求JSON格式,可是隻有XML格式)。
410 Gone -[GET]:用戶請求的資源被永久刪除,且不會再獲得的。
422 Unprocesable entity - [POST/PUT/PATCH] 當建立一個對象時,發生一個驗證錯誤。
500 INTERNAL SERVER ERROR - [*]:服務器發生錯誤,用戶將沒法判斷髮出的請求是否成功。
View Code
  • 錯誤處理,狀態碼是4xx時,應返回錯誤信息,error當作key。
    {
        error: "Invalid API key"
    }
  • 返回結果,針對不一樣操做,服務器向用戶返回的結果應該符合如下規範。
    GET /collection:返回資源對象的列表(數組)
    GET /collection/resource:返回單個資源對象
    POST /collection:返回新生成的資源對象
    PUT /collection/resource:返回完整的資源對象
    PATCH /collection/resource:返回完整的資源對象
    DELETE /collection/resource:返回一個空文檔
  • Hypermedia API,RESTful API最好作到Hypermedia,即返回結果中提供連接,連向其餘API方法,使得用戶不查文檔,也知道下一步應該作什麼。

基於Django實現restful api

參考博客:https://www.cnblogs.com/wusir66/p/10016584.htmlpython

基於Django Rest Framework框架實現

PS:如下介紹的使用方式都是一些經常使用的,還有一些方式使用較少,在此不作介紹。ajax

1、自定義用戶認證

  在models.py的UserInfo表中建立一些數據django

from django.db import models

class UserInfo(models.Model):
    user_type_choices = (
        (1,'普通用戶'),
        (2,'VIP'),
        (3,'SVIP'),
    )
    user_type = models.IntegerField(choices=user_type_choices)
    username = models.CharField(max_length=32,unique=True)
    password = models.CharField(max_length=64)

class UserToken(models.Model):
    user = models.OneToOneField(to='UserInfo')
    token = models.CharField(max_length=64)
models.py
from django.http import JsonResponse
from rest_framework.views import APIView
from app.models import *
import hashlib
import time

#建立token字符串
def md5(user):
    ctime = str(time.time())
    m = hashlib.md5(bytes(user,encoding='utf-8'))
    m.update(bytes(ctime,encoding='utf-8'))
    return m.hexdigest()

#找到指定用戶給其token值
class AuthView(APIView):
    authentication_classes = []
    def post(self,request,*args,**kwargs):
        ret = {'code':1000,'msg':None}
        try:
            name = request._request.POST.get('username')
            pwd = request._request.POST.get('password')
            obj = UserInfo.objects.filter(username=name,password=pwd).first()
            if not obj:
                ret['code'] = 1001
                ret['msg'] = '用戶名或密碼錯誤'
            token = md5(name)
            UserToken.objects.update_or_create(user=obj,defaults={'token':token})
            ret['token'] = token
        except Exception as e:
            ret['code'] = 1002
            ret['msg'] = '請求異常'
        return JsonResponse(ret)

#利用token值來進行認證
class OrderView(APIView):
    def get(self,request,*args,**kwargs):
        ret = {'code':1000,'msg':None,'data':None}
        order = {'goods':'food'}
        try:
            ret['data'] = order
        except Exception as e:
            ret['msg'] = '有問題'
        return JsonResponse(ret)
view.py

  在app目錄下建立一個rest_utils的文件夾json

from rest_framework.authentication import BaseAuthentication
from app.models import *
from rest_framework import exceptions

class MyAuthentication(BaseAuthentication):
    def authenticate(self, request):
        token = request._request.GET.get('token')
        obj = UserToken.objects.filter(token=token).first()
        if not obj:
            raise exceptions.AuthenticationFailed('用戶未認證')
        return (obj.user,obj)
    def authenticate_header(self, request):
        pass
auth.py(具體認證代碼)

  配置文件後端

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES':['app.rest_utils.auth.MyAuthentication',],
}
settings.py

若是有某些類不須要使用認證,能夠在類中加上如下代碼api

authentication_classes = []

源碼大體流程跨域

def dispatch(self, request, *args, **kwargs):
        """
        `.dispatch()` is pretty much the same as Django's regular dispatch,
        but with extra hooks for startup, finalize, and exception handling.
        """
        self.args = args
        self.kwargs = kwargs
        第一步:對request進行加工(添加數據)
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?

        try:
            #第二步:
                #處理版權信息
                #認證
                #權限
                #請求用戶進行訪問頻率的限制
            self.initial(request, *args, **kwargs)

            # Get the appropriate handler method
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed

            # 第三步、執行:get/post/put/delete函數
            response = handler(request, *args, **kwargs)

        except Exception as exc:
            response = self.handle_exception(exc)

        #第四步、 對返回結果再次進行加工
        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response
View Code

源碼具體流程數組

  請求來了先走dispatch方法作分發

一、對request進行加工(添加數據)

a、首先  request = self.initialize_request(request, *args, **kwargs)點進去,會發現:在Request裏面多加了四個,以下

def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        """
        #把請求弄成一個字典返回
        parser_context = self.get_parser_context(request)

        return Request(
            request,
            parsers=self.get_parsers(),  #解析數據,默認的有三種方式,可點進去看
            #self.get_authenticator優先找本身的,沒有就找父類的
            authenticators=self.get_authenticators(), #獲取認證相關的全部類並實例化,傳入request對象供Request使用
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )
View Code

b、獲取認證相關的類的具體   authenticators=self.get_authenticators()

 def get_authenticators(self):
        """
        Instantiates and returns the list of authenticators that this view can use.
        """
        #返回的是對象列表
        return [auth() for auth in self.authentication_classes]  #[SessionAuthentication,BaseAuthentication]
View Code

c、查看認證的類:self.authentication_classes

authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES  #默認的,若是本身有會優先執行本身的
View Code

d、接着走進api_settings

api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)  #點擊繼承的DEFAULTS類


DEFAULTS類
DEFAULTS = {
    # Base API policies
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.SessionAuthentication',   #這時候就找到了他默認認證的類了,能夠導入看看
        'rest_framework.authentication.BasicAuthentication'
    ),
View Code

e、導入了類看看類裏面具體幹了什麼

from rest_framework.authentication import SessionAuthentication
from rest_framework.authentication import BaseAuthentication
View Code

f、看到裏面有個authenticate方法和authenticate_header方法

class BaseAuthentication(object):
    """
    All authentication classes should extend BaseAuthentication.
    """

    def authenticate(self, request):
        """
        Authenticate the request and return a two-tuple of (user, token).
        """
        raise NotImplementedError(".authenticate() must be overridden.")

    def authenticate_header(self, request):
        """
        Return a string to be used as the value of the `WWW-Authenticate`
        header in a `401 Unauthenticated` response, or `None` if the
        authentication scheme should return `403 Permission Denied` responses.
        """
        pass
View Code

具體處理認證,從headers裏面能獲取用戶名和密碼

class BasicAuthentication(BaseAuthentication):
    """
    HTTP Basic authentication against username/password.
    """
    www_authenticate_realm = 'api'

    def authenticate(self, request):
        """
        Returns a `User` if a correct username and password have been supplied
        using HTTP Basic authentication.  Otherwise returns `None`.
        """
        auth = get_authorization_header(request).split()

        if not auth or auth[0].lower() != b'basic':
            return None   #返回none不處理。讓下一個處理

        if len(auth) == 1:
            msg = _('Invalid basic header. No credentials provided.')
            raise exceptions.AuthenticationFailed(msg)
        elif len(auth) > 2:
            msg = _('Invalid basic header. Credentials string should not contain spaces.')
            raise exceptions.AuthenticationFailed(msg)

        try:
            auth_parts = base64.b64decode(auth[1]).decode(HTTP_HEADER_ENCODING).partition(':')   #用partition切割冒號也包括
        except (TypeError, UnicodeDecodeError, binascii.Error):
            msg = _('Invalid basic header. Credentials not correctly base64 encoded.')
            raise exceptions.AuthenticationFailed(msg)

        userid, password = auth_parts[0], auth_parts[2]  # 返回用戶和密碼
        return self.authenticate_credentials(userid, password, request)

    def authenticate_credentials(self, userid, password, request=None):
        """
        Authenticate the userid and password against username and password
        with optional request for context.
        """
        credentials = {
            get_user_model().USERNAME_FIELD: userid,
            'password': password
        }
        user = authenticate(request=request, **credentials)

        if user is None:
            raise exceptions.AuthenticationFailed(_('Invalid username/password.'))

        if not user.is_active:
            raise exceptions.AuthenticationFailed(_('User inactive or deleted.'))

        return (user, None)

    def authenticate_header(self, request):
        return 'Basic realm="%s"' % self.www_authenticate_realm
View Code

g、固然restfulframework默認定義了兩個類。咱們也能夠自定製類,本身有就用本身的了,本身沒有就去找父類的了,可是裏面必須實現authenticate方法,否則會報錯。

二、加工完request以後的操做

  • 處理版權信息
  • 認證
  • 權限
  • 請求用戶進行訪問頻率的限制

認證流程

a、首先 self.initial(request, *args, **kwargs)能夠看到作了如下操做

def initial(self, request, *args, **kwargs):
        """
        Runs anything that needs to occur prior to calling the method handler.
        """
        self.format_kwarg = self.get_format_suffix(**kwargs)

        # Perform content negotiation and store the accepted info on the request
        neg = self.perform_content_negotiation(request)
        request.accepted_renderer, request.accepted_media_type = neg

        # Determine the API version, if versioning is in use.
        #2.1 處理版本信息
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme

        # Ensure that the incoming request is permitted
        #2.2 認證
        self.perform_authentication(request)
        # 2.3 權限
        self.check_permissions(request)
        # 2.4 請求用戶進行訪問頻率的限制
        self.check_throttles(request)
View Code

 b、咱們先來看認證,self.perform_authentication(request) 具體幹了什麼,按住ctrl點擊進去

def perform_authentication(self, request):
        """
        Perform authentication on the incoming request.

        Note that if you override this and simply 'pass', then authentication
        will instead be performed lazily, the first time either
        `request.user` or `request.auth` is accessed.
        """
        request.user   #執行request的user,這是的request已是加工後的request了
View Code

c、那麼咱們能夠從視圖裏面導入一下Request,找到request對象的user方法

from rest_framework.views import Request
@property
    def user(self):
        """
        Returns the user associated with the current request, as authenticated
        by the authentication classes provided to the request.
        """
        if not hasattr(self, '_user'):
            with wrap_attributeerrors():
                self._authenticate()  #
        return self._user  #返回user
View Code

d、執行self._authenticate() 開始用戶認證,若是驗證成功後返回元組: (用戶,用戶Token)

def _authenticate(self):
        """
        Attempt to authenticate the request using each authentication instance
        in turn.
        """
        #循環對象列表
        for authenticator in self.authenticators:
            try:
                #執行每個對象的authenticate 方法
                user_auth_tuple = authenticator.authenticate(self)   
            except exceptions.APIException:
                self._not_authenticated()
                raise

            if user_auth_tuple is not None:
                self._authenticator = authenticator
                self.user, self.auth = user_auth_tuple  #返回一個元組,user,和auth,賦給了self,
                # 只要實例化Request,就會有一個request對象,就能夠request.user,request.auth了
                return

        self._not_authenticated()
View Code

e、在user_auth_tuple = authenticator.authenticate(self) 進行驗證,若是驗證成功,執行類裏的authenticatie方法 

f、若是用戶沒有認證成功:self._not_authenticated()

def _not_authenticated(self):
        """
        Set authenticator, user & authtoken representing an unauthenticated request.

        Defaults are None, AnonymousUser & None.
        """
        #若是跳過了全部認證,默認用戶和Token和使用配置文件進行設置
        self._authenticator = None  #

        if api_settings.UNAUTHENTICATED_USER:
            self.user = api_settings.UNAUTHENTICATED_USER() # 默認值爲:匿名用戶AnonymousUser
        else:
            self.user = None  # None 表示跳過該認證

        if api_settings.UNAUTHENTICATED_TOKEN:
            self.auth = api_settings.UNAUTHENTICATED_TOKEN()  # 默認值爲:None
        else:
            self.auth = None

    # (user, token)
    # 表示驗證經過並設置用戶名和Token;
    # AuthenticationFailed異常
View Code

三、執行get/post/delete等方法

四、對返回結果在進行加工

2、自定義權限

  在app目錄下建立一個rest_utils的文件夾

from rest_framework.permissions import BasePermission

class MyPermission(BasePermission):
    message = '必須是svip才能訪問'
    def has_permission(self,request,view):
        if request.user.user_type != 3:
            return False
        return True
permission.py

  配置文件

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': ['app.rest_utils.permission.MyPermission', ],
    
}
settings.py

  若是有某些類不須要使用權限,能夠在類中加上如下代碼

    permission_classes = []

3、自定義節流

   在app目錄下建立一個rest_utils的文件夾

from rest_framework.throttling import BaseThrottle

import time
VISIT_RECORD = {}
class VisitThrottle(BaseThrottle):

    def __init__(self):
        self.history = None

    def allow_request(self,request,view):
        # 1. 獲取用戶IP
        remote_addr = self.get_ident(request)

        ctime = time.time()
        if remote_addr not in VISIT_RECORD:
            VISIT_RECORD[remote_addr] = [ctime,]
            return True
        history = VISIT_RECORD.get(remote_addr)
        self.history = history

        while history and history[-1] < ctime - 60:
            history.pop()

        if len(history) < 3:
            history.insert(0,ctime)
            return True

        # return True    # 表示能夠繼續訪問
        # return False # 表示訪問頻率過高,被限制

    def wait(self):
        # 還須要等多少秒才能訪問
        ctime = time.time()
        return 60 - (ctime - self.history[-1])
throttle.py

  配置文件

REST_FRAMEWORK = {
    "DEFAULT_THROTTLE_CLASSES": ["app.rest_utils.throttle.VisitThrottle"],
}
settings.py

  若是有某些類不須要使用節流,能夠在類中加上如下代碼

throttle_classes = []

4、版本

  配置文件

REST_FRAMEWORK = {
 "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",
    "DEFAULT_VERSION":'v1',
    "ALLOWED_VERSIONS":['v1','v2'],
    "VERSION_PARAM":'version',
}
settings.py

  路由

from django.conf.urls import url
from app.views import *

urlpatterns = [
    url('^(?P<version>[v1|v2]+)/orderview/$',OrderView.as_view(),name='wusir'),
]
urls.py

  視圖

from django.http import JsonResponse
from rest_framework.views import APIView
from app.models import *
import hashlib
import time

def md5(user):
    ctime = str(time.time())
    m = hashlib.md5(bytes(user,encoding='utf-8'))
    m.update(bytes(ctime,encoding='utf-8'))
    return m.hexdigest()

class AuthView(APIView):
    authentication_classes = []
    permission_classes = []
    def post(self,request,*args,**kwargs):
        ret = {'code':1000,'msg':None}
        try:
            name = request._request.POST.get('username')
            pwd = request._request.POST.get('password')
            obj = UserInfo.objects.filter(username=name,password=pwd).first()
            if not obj:
                ret['code'] = 1001
                ret['msg'] = '用戶名或密碼錯誤'
            token = md5(name)
            UserToken.objects.update_or_create(user=obj,defaults={'token':token})
            ret['token'] = token
        except Exception as e:
            ret['code'] = 1002
            ret['msg'] = '請求異常'
        return JsonResponse(ret)

class OrderView(APIView):
    def get(self,request,*args,**kwargs):
        ret = {'code':1000,'msg':None,'data':None}
        order = {'goods':'food'}
        # 得到版本信息
        print(request.version)
        # 獲取處理版本的對象
        print(request.versioning_scheme)
        #反向生成url(rest_framework)
        u1 = request.versioning_scheme.reverse(viewname='wusir',request=request)
        print(u1)
        try:
            ret['data'] = order
        except Exception as e:
            ret['msg'] = '有問題'
        return JsonResponse(ret)
views.py

5、解析器

  配置文件

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

}
settings.py

  視圖函數中能夠直接經過request.data拿到解析以後的數據

6、序列化

序列化共有兩個功能:一、數據序列化  二、請求數據校驗

  數據序列化

from django.db import models

class UserGroup(models.Model):
    title = models.CharField(max_length=32)


class UserInfo(models.Model):
    user_type_choices = (
        (1,'普通用戶'),
        (2,'VIP'),
        (3,'SVIP'),
    )
    user_type = models.IntegerField(choices=user_type_choices)
    username = models.CharField(max_length=32,unique=True)
    password = models.CharField(max_length=64)

    group = models.ForeignKey("UserGroup")
    roles = models.ManyToManyField("Role")


class UserToken(models.Model):
    user = models.OneToOneField(to='UserInfo')
    token = models.CharField(max_length=64)


class Role(models.Model):
    title = models.CharField(max_length=32)
models.py建表
import json
from rest_framework.views import APIView
from django.http import HttpResponse
from rest_framework import serializers
from app.models import *

#=========================================
# 自定義序列化類一
class RolesSerializers(serializers.Serializer):
    id = serializers.IntegerField()
    title = serializers.CharField()

class RoleViews(APIView):
    def get(self,request,*args,**kwargs):
        # 方式一 ,沒有使用數據序列化
        res = Role.objects.all().values('title')
        res = list(res)
        res = json.dumps(res,ensure_ascii=False)

        # 方式二 ,序列化多個數據[obj,obj,obj]
        res = Role.objects.all()
        ser = RolesSerializers(instance=res,many=True)
        res = json.dumps(ser.data,ensure_ascii=False)

        # 方式三 ,序列化單個數據 [obj]
        res = Role.objects.all().first()
        ser = RolesSerializers(instance=res,many=False)
        res = json.dumps(ser.data,ensure_ascii=False)
        return HttpResponse(res)
# =========================================

# +++++++++++++++++++++++++++++++++++++++++
# 自定義序列化類二
class UserInfoSerializers(serializers.Serializer):
    # user_type = serializers.IntegerField()
    aaaaa = serializers.IntegerField(source='user_type')
    bbbbb = serializers.CharField(source='get_user_type_display')
    username = serializers.CharField()
    password = serializers.CharField()
    gps = serializers.CharField(source='group.id')
    # rls = serializers.CharField(source='roles.all')   # 用這個拿到的是一個一個的對象
    rls = serializers.SerializerMethodField()    # 自定義顯示
    def get_rls(self,row):
        obj_list = row.roles.all()
        ret = []
        for i in obj_list:
            ret.append({'id':i.id,'title':i.title})
        return ret

# 自定義序列化類三
class UserInfoSerializers(serializers.ModelSerializer):
    bbbbb = serializers.CharField(source='get_user_type_display')
    group = serializers.CharField(source='group.title')
    rls = serializers.SerializerMethodField()
    def get_rls(self,row):
        obj_list = row.roles.all()
        ret = []
        for i in obj_list:
            ret.append({'id':i.id,'title':i.title})
        return ret

    class Meta:
        model = UserInfo
        # fields = '__all__'
        fields = ['id','username','password','bbbbb','group','rls']

# 自定義序列化類四(深度化控制)
class UserInfoSerializers(serializers.ModelSerializer):
    class Meta:
        model = UserInfo
        # fields = '__all__'
        fields = ['id','username','password','user_type','group','roles']
        depth = 1

# 自定義序列化類五(Hypermedia API)
class UserInfoSerializers(serializers.ModelSerializer):
    group = serializers.HyperlinkedIdentityField(view_name='wusir',lookup_field='group_id',lookup_url_kwarg='pk')
    class Meta:
        model = UserInfo
        # fields = '__all__'
        fields = ['id','username','password','user_type','group','roles']

class UserInfoViews(APIView):
    def get(self,request,*args,**kwargs):
        users = UserInfo.objects.all()
        ser = UserInfoSerializers(instance=users,many=True,context={'request':request})
        res = json.dumps(ser.data,ensure_ascii=False)
        return HttpResponse(res)

# GroupSerializers和GroupViews配合自定義序列化類五生成Hypermedia API後,點擊url能夠看詳情
class GroupSerializers(serializers.ModelSerializer):
    class Meta:
        model = UserGroup
        fields = '__all__'

class GroupViews(APIView):
    def get(self,request,*args,**kwargs):
        pk = kwargs.get('pk')
        res = UserGroup.objects.filter(id=pk).first()
        ser = GroupSerializers(instance=res,many=False)
        res = json.dumps(ser.data,ensure_ascii=False)
        return HttpResponse(res)
# +++++++++++++++++++++++++++++++++++++++++
views.py
from django.conf.urls import url
from app.views import *

urlpatterns = [
    url('role/$',RoleViews.as_view()),
    url('info/$',UserInfoViews.as_view()),
    url('group/(?P<pk>\d+)$',GroupViews.as_view(),name='wusir'),
]
urls.py

  請求數據校驗

class XXValidator(object):
    def __init__(self, base):
        self.base = base
    def __call__(self, value):
        if not value.startswith(self.base):
            message = '標題必須以 %s 爲開頭。' % self.base
            raise serializers.ValidationError(message)
    def set_context(self, serializer_field):
        pass


class UserGroupSerializer(serializers.Serializer):
    title = serializers.CharField(error_messages={'required': '標題不能爲空'},validators=[XXValidator('wusir'),])
    
# required爲錯誤時的提示信息,validators爲自定義驗證器,能夠自定義功能,也能夠不寫,不寫的話只能校驗數據是否爲空

class UserGroupView(APIView):
    def post(self, request, *args, **kwargs):
        ser = UserGroupSerializer(data=request.data)
        if ser.is_valid():
            print(ser.validated_data['title'])
        else:
            print(ser.errors)
        return HttpResponse('提交數據')
views.py
from django.conf.urls import url
from app.views import *

urlpatterns = [
    url('usergroup/$',UserGroupView.as_view()),
]
urls.py

7、分頁

PS:先自定義一個序列化,而後在分頁程序中導入這個序列化程序,全部的原生分頁須要如今settings.py中定義一下每頁顯示幾條數據

REST_FRAMEWORK = {
    "PAGE_SIZE":2,
}
settings.py

A一、分頁,看第n頁,每頁顯示n條數據(原生)

from django.http import JsonResponse, HttpResponse
from rest_framework.pagination import PageNumberPagination
from rest_framework.views import APIView
from app.models import *
import hashlib
import time
from app.rest_utils.serializsers.pager import PagerSerialiser
from rest_framework.response import Response


class PageView(APIView):
    authentication_classes = []
    permission_classes = []
    throttle_classes = []
    def get(self,request,*args,**kwargs):
        roles = Role.objects.all()
        pg = PageNumberPagination()
        page_roles = pg.paginate_queryset(queryset=roles,request=request,view=self)
        ser = PagerSerialiser(instance=page_roles,many=True)
        # return Response(ser.data)
        return pg.get_paginated_response(ser.data)   #頁面上加上上下頁的url
views.py

A二、分頁,看第n頁,每頁顯示n條數據(自定義)

from django.http import JsonResponse, HttpResponse
from rest_framework.pagination import PageNumberPagination
from rest_framework.views import APIView
from app.models import *
import hashlib
import time
from app.rest_utils.serializsers.pager import PagerSerialiser
from rest_framework.response import Response


class MyPageNumberPagination(PageNumberPagination):
    page_size = 3    #默認每頁顯示個數
    page_query_param = 'page'    #get傳參表示第幾頁
    page_size_query_param = 'pagesize'    #get傳參表示每頁顯示幾個
    max_page_size = 5   #每頁最大顯示個數


class PageView(APIView):
    authentication_classes = []
    permission_classes = []
    throttle_classes = []
    def get(self,request,*args,**kwargs):
        roles = Role.objects.all()
        pg = MyPageNumberPagination()
        page_roles = pg.paginate_queryset(queryset=roles,request=request,view=self)
        ser = PagerSerialiser(instance=page_roles,many=True)
        # return Response(ser.data)
        return pg.get_paginated_response(ser.data)   #頁面上加上上下頁的url
views.py

B1. 分頁,在n個位置,向後查看n條數據(原生)

from django.http import JsonResponse, HttpResponse
from rest_framework.pagination import LimitOffsetPagination
from rest_framework.views import APIView
from app.models import *
import hashlib
import time
from app.rest_utils.serializsers.pager import PagerSerialiser
from rest_framework.response import Response


class PageView(APIView):
    authentication_classes = []
    permission_classes = []
    throttle_classes = []
    def get(self,request,*args,**kwargs):
        roles = Role.objects.all()
        pg = LimitOffsetPagination()
        page_roles = pg.paginate_queryset(queryset=roles,request=request,view=self)
        ser = PagerSerialiser(instance=page_roles,many=True)
        # return Response(ser.data)
        return pg.get_paginated_response(ser.data)   #頁面上加上上下頁的url
views.py

B2. 分頁,在n個位置,向後查看n條數據(自定義)

from django.http import JsonResponse, HttpResponse
from rest_framework.pagination import LimitOffsetPagination
from rest_framework.views import APIView
from app.models import *
import hashlib
import time
from app.rest_utils.serializsers.pager import PagerSerialiser
from rest_framework.response import Response



class MyLimitOffsetPagination(LimitOffsetPagination):
    default_limit = 3   #默認每頁顯示個數
    limit_query_param = 'limit'    #get傳參表示每頁顯示個數
    offset_query_param = 'offset'  #get傳參表示跳過幾個數據顯示
    max_limit = 6       #每頁最大顯示個數

class PageView(APIView):
    authentication_classes = []
    permission_classes = []
    throttle_classes = []
    def get(self,request,*args,**kwargs):
        roles = Role.objects.all()
        pg = MyLimitOffsetPagination()
        page_roles = pg.paginate_queryset(queryset=roles,request=request,view=self)
        ser = PagerSerialiser(instance=page_roles,many=True)
        # return Response(ser.data)
        return pg.get_paginated_response(ser.data)   #頁面上加上上下頁的url
views.py

C1.加密分頁,上一頁和下一頁(原生)

from django.http import JsonResponse, HttpResponse
from rest_framework.pagination import CursorPagination
from rest_framework.views import APIView
from app.models import *
import hashlib
import time
from app.rest_utils.serializsers.pager import PagerSerialiser
from rest_framework.response import Response

class PageView(APIView):
    authentication_classes = []
    permission_classes = []
    throttle_classes = []
    def get(self,request,*args,**kwargs):
        roles = Role.objects.all()
        pg = CursorPagination()
        page_roles = pg.paginate_queryset(queryset=roles,request=request,view=self)
        ser = PagerSerialiser(instance=page_roles,many=True)
        # return Response(ser.data)
        return pg.get_paginated_response(ser.data)   #頁面上加上上下頁的url
views.py

C2.加密分頁,上一頁和下一頁(自定義)

from django.http import JsonResponse, HttpResponse
from rest_framework.pagination import CursorPagination
from rest_framework.views import APIView
from app.models import *
import hashlib
import time
from app.rest_utils.serializsers.pager import PagerSerialiser
from rest_framework.response import Response


class MyCursorPagination(CursorPagination):
    cursor_query_param = 'wusir'  #get參數中肯定以什麼值爲key來接受下一頁的參數
    page_size = 3   #默認每頁顯示數目
    ordering = 'id'  #根據什麼字段來進行排序
    page_size_query_param = 'pagesize'   #get傳參表示每頁顯示幾個
    max_page_size = 5   #每頁最大顯示數目


class PageView(APIView):
    authentication_classes = []
    permission_classes = []
    throttle_classes = []
    def get(self,request,*args,**kwargs):
        roles = Role.objects.all()
        pg = MyCursorPagination()
        page_roles = pg.paginate_queryset(queryset=roles,request=request,view=self)
        ser = PagerSerialiser(instance=page_roles,many=True)
        # return Response(ser.data)
        return pg.get_paginated_response(ser.data)   #頁面上加上上下頁的url
views.py

8、視圖

  PS:View和APIView在此處就很少介紹

  GenericAPIView

from rest_framework.pagination import PageNumberPagination
from rest_framework.views import APIView
from app.models import *
from app.rest_utils.serializsers.pager import PagerSerialiser
from rest_framework.response import Response
from rest_framework.generics import GenericAPIView

class View1View(GenericAPIView):
    authentication_classes = []
    permission_classes = []
    throttle_classes = []
    queryset = Role.objects.all()
    serializer_class = PagerSerialiser
    pagination_class = PageNumberPagination
    def get(self,request,*args,**kwargs):
        # 獲取數據
        roles = self.get_queryset()
        # 分頁
        pager_roles = self.paginate_queryset(roles)
        # 序列化
        ser = self.get_serializer(instance=pager_roles,many=True)
        return Response(ser.data)
views.py

  GenericViewSet(as_view裏面能夠用字典的方式讓get,post等不一樣的方法對應執行不一樣名稱的函數)

from rest_framework.pagination import PageNumberPagination
from app.models import *
from rest_framework.response import Response
from app.rest_utils.serializsers.pager import PagerSerialiser
from rest_framework.viewsets import GenericViewSet

class View1View(GenericViewSet):
    authentication_classes = []
    permission_classes = []
    throttle_classes = []
    queryset = Role.objects.all()
    serializer_class = PagerSerialiser
    pagination_class = PageNumberPagination
    def list(self, request, *args, **kwargs):
        # 獲取數據
        roles = self.get_queryset()
        # 分頁
        pager_roles = self.paginate_queryset(roles)
        # 序列化
        ser = self.get_serializer(instance=pager_roles, many=True)
        return Response(ser.data)
views.py
from django.conf.urls import url
from app.views import *

urlpatterns = [
    url(r'^view1view/$',View1View.as_view({'get':'list'})),
]
urls.py

  mixins.CreateModelMixin,mixins.RetrieveModelMixin,mixins.UpdateModelMixin,mixins.DestroyModelMixin,mixins.ListModelMixin,

(任意舉兩個栗子)

from app.rest_utils.serializsers.pager import PagerSerialiser
from rest_framework.viewsets import GenericViewSet,ModelViewSet
from rest_framework.mixins import ListModelMixin,CreateModelMixin

class View1View(GenericViewSet,ListModelMixin,CreateModelMixin):
    authentication_classes = []
    permission_classes = []
    throttle_classes = []
    queryset = Role.objects.all()
    serializer_class = PagerSerialiser
    pagination_class = PageNumberPagination
views.py
from django.conf.urls import url
from app.views import *

urlpatterns = [
    url(r'^view1view/$',View1View.as_view({'get':'list','post':'create'})),
]
urls.py

  ModelViewSet

from app.rest_utils.serializsers.pager import PagerSerialiser
from rest_framework.viewsets import ModelViewSet

class View1View(ModelViewSet):
    authentication_classes = []
    permission_classes = []
    throttle_classes = []
    queryset = Role.objects.all()
    serializer_class = PagerSerialiser
    pagination_class = PageNumberPagination
views.py
from django.conf.urls import url
from app.views import *


urlpatterns = [
    url(r'^view1view/$',View1View.as_view({'get': 'list','post':'create'})),
    url(r'^view1view/(?P<pk>\d+)/$',View1View.as_view({'get': 'retrieve','delete':'destroy','put':'update','patch':'partial_update'})),
]
urls.py

繼承關係:APIView繼承了View,GenericAPIView繼承了APIView,GenericViewSet繼承了GenericAPIView和ViewSetMixin,ModelViewSet繼承了mixins.CreateModelMixin,mixins.RetrieveModelMixin,mixins.UpdateModelMixin,mixins.DestroyModelMixin,mixins.ListModelMixin和GenericViewSet。

9、路由系統

  當繼承了ModelViewSet以後,一個視圖對於的完整的路由就有如下四種

from django.conf.urls import url
from app.views import *

urlpatterns = [
    # http://127.0.0.1:8000/api/v1/v1/?format=json
    url(r'^(?P<version>[v1|v2]+)/view1view/$', View1View.as_view({'get': 'list','post':'create'})),
    # http://127.0.0.1:8000/api/v1/v1.json
    url(r'^(?P<version>[v1|v2]+)/view1view\.(?P<format>\w+)$', View1View.as_view({'get': 'list','post':'create'})),
    url(r'^(?P<version>[v1|v2]+)/view1view/(?P<pk>\d+)/$', View1View.as_view({'get': 'retrieve','delete':'destroy','put':'update','patch':'partial_update'})),
    url(r'^(?P<version>[v1|v2]+)/view1view/(?P<pk>\d+)\.(?P<format>\w+)$', View1View.as_view({'get': 'retrieve','delete':'destroy','put':'update','patch':'partial_update'})),
]
urls.py

  這時候咱們可使用rest_framework給咱們自動生成路由的方式來生成以上四種路由,效果相同

from django.conf.urls import url, include
from app.views import *
from rest_framework import routers
router = routers.DefaultRouter()
router.register(r'view1view',View1View)

urlpatterns = [
    url(r'^(?P<version>[v1|v2]+)/', include(router.urls)),
]
urls.py

10、渲染器

  只須要在配置文件中配置一下所須要的渲染器類型就能夠了

REST_FRAMEWORK = {
    "DEFAULT_RENDERER_CLASSES":[
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    ]
}
settings.py
相關文章
相關標籤/搜索