RESTful


RESTful   api設計

接口:url

  • 根據method不同,進行不同操作
  • 面向資源編程       :所有的數據不過是通過網絡獲取的還是操作的數據,都是資源
  • 體現版本https
  • 體現是API
  • https  傳輸的是密文
  • 過濾條件,通過在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
    301
    302
    403
    404
    500
    更多看這里:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.htm

    錯誤信息

    {
        error: "Invalid API key"
    }
  • 處理結果  
    GET /collection:返回資源對象的列表(數組)  /collection/resource:返回單個資源對象
    POST /collection:返回新生成的資源對象
    PUT /collection/resource:返回完整的資源對象
    PATCH /collection/resource:返回完整的資源對象
    DELETE /collection/resource:返回一個空文檔

    HEAD
    OPTION
    TRACE
  • Hypermedia  API ,RESTful API 最好做到Hypermedia,即返回結果中提供鏈接-連向其他API方法,使得用戶不查文檔,也知道下一步應該做什么。
    {"link": {
      "rel":   "collection https://www.example.com/zoos",
      "href":  "https://api.example.com/zoos",
      "title": "List of zoos",
      "type":  "application/vnd.yourformat+json"
    }}  
http://www.ruanyifeng.com/blog/2014/05/restful_api.html
http://www.cnblogs.com/yuanchenqi/articles/8742684.html
http://www.cnblogs.com/wupeiqi/articles/7805382.html
https://www.cnblogs.com/liwenzhou/p/8543035.html
摘自

 

 

三 、 restful   知識點

pip install djangorestframework

request._request   -- 原request

request.GET=request._request.GET

request.data   # post不管以什么方式拼接的數據,都可以獲得 urlencode json 

 

1、登錄認證
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework.request import Request
from rest_framework import exceptions

token_list = [
    'sfsfss123kuf3j123',
    'asijnfowerkkf9812',
]


class TestAuthentication(BaseAuthentication):
    def authenticate(self, request):
        import base64
        auth = request.META.get('HTTP_AUTHORIZATION', b'')
        if auth:
            auth = auth.encode('utf-8')
        auth = auth.split()
        if not auth or auth[0].lower() != b'basic':
            raise exceptions.AuthenticationFailed('驗證失敗')
        if len(auth) != 2:
            raise exceptions.AuthenticationFailed('驗證失敗')
        username, part, password = base64.b64decode(auth[1]).decode('utf-8').partition(':')
        if username == 'alex' and password == '123':
            return ('登錄用戶', '用戶token')
        else:
            raise exceptions.AuthenticationFailed('用戶名或密碼錯誤')

    def authenticate_header(self, request):
        return 'Basic realm=api'


class TestView(APIView):
    authentication_classes = [TestAuthentication, ]
    def get(self, request, *args, **kwargs):
        print(request.user)
        print(request.auth)
        return Response('GET請求,響應內容')
請求頭認證 不推薦
import hashlib
import time
from rest_framework.views import APIView
from rest_framework.response import Response
from api.models import *


def get_random_str(user):
    """ 生成隨機 字符串 """
    ctime = str(time.time())
    md5 = hashlib.md5(bytes(user, encoding='utf-8'))
    md5.update(bytes(ctime, encoding="utf-8"))
    return md5.hexdigest()


class LoginView(APIView):
    def post(self, request):
        name = request.data.get("name")
        pwd = request.data.get("pwd")
        user = User.objects.filter(name=name, pwd=pwd).first()
        res = {"state_code": 1000, "msg": None}
        if user:
            random_str = get_random_str(user.name)
            Token.objects.update_or_create(user=user, defaults={"token": random_str})
            res["token"] = random_str
        else:
            res["state_code"] = 1001  # 錯誤狀態碼
            res["msg"] = "用戶名或密碼錯誤"

        # import json
        # from  django.shortcuts import HttpResponse
        # return HttpResponse(json.dumps(res,ensure_ascii=False))

        # from django.http import JsonResponse
        # return JsonResponse(res)

        return Response(res)
隨機 字符串

 

前后端分離    用token
不分離  用cookies 、sessions

 

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework import exceptions
token_list = ['sfsfss123kuf3j123', ]
class TestAuthentication(BaseAuthentication):
    def authenticate(self, request):
        """
        None,表示跳過該驗證; 如果跳過了所有認證,默認用戶和Token和使用配置文件進行設置
        user,token表示驗證通過並設置用戶名和Token;
        AuthenticationFailed異常
        """
        val = request.query_params.get('token')
        if val not in token_list:
            raise exceptions.AuthenticationFailed("用戶認證失敗")
        return '登錄用戶', '用戶token'
    def authenticate_header(self, request):
        # 驗證失敗時,返回的響應頭WWW-Authenticate對應的值
        # return 123
        pass
class TestView(APIView):
    authentication_classes = [TestAuthentication, ]def get(self, request, *args, **kwargs):
        print(request.user)
        print(request.auth)
        return Response('GET請求,響應內容')

 

REST_FRAMEWORK = { # 登錄認證 "DEFAULT_AUTHENTICATION_CLASSES": [], # 權限控制 "DEFAULT_PERMISSION_CLASSES": [], # 頻率控制 "DEFAULT_THROTTLE_CLASSES": [] }

 

2、權限控制

 

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import BasePermission
class TestPermission(BasePermission):
    message = "權限驗證失敗"
    def has_permission(self, request, view):
        """
        from rest_framework.generics import GenericAPIView
        判斷是否有權限訪問當前請求
        :return: True有權限;False無權限
        """
        if request.user == "管理員":
            return True
  def wait(self):
    # 返回需要在等多久才能訪問
    pass
def has_object_permission(self, request, view, obj): """ GenericAPIView使用get_object時獲取對象時,觸發單獨對象權限驗證 True有權限;False無權限 """ if request.user == "管理員": return True class TestView(APIView): authentication_classes = [] permission_classes = [TestPermission, ] def get(self, request, *args, **kwargs): print(request.user) print(request.auth) return Response('GET請求,響應內容')

 

3、頻率限制
import time
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import exceptions
from rest_framework.throttling import BaseThrottle
from rest_framework.settings import api_settings
# 保存訪問記錄
RECORD = {
    '用戶IP': [12312139, 12312135, 12312133, ]
}
class TestThrottle(BaseThrottle):
    ctime = time.time
    def get_ident(self, request):
        """根據用戶IP和代理IP,當做請求者的唯一IP"""
        xff = request.META.get('HTTP_X_FORWARDED_FOR')
        remote_addr = request.META.get('REMOTE_ADDR')
        num_proxies = api_settings.NUM_PROXIES

        if num_proxies is not None:
            if num_proxies == 0 or xff is None:
                return remote_addr
            addrs = xff.split(',')
            client_addr = addrs[-min(num_proxies, len(addrs))]
            return client_addr.strip()
        return ''.join(xff.split()) if xff else remote_addr

    def allow_request(self, request, view):
        """
        是否仍然在允許范圍內     :return: True,表示可以通過;False表示已超過限制,不允許訪問
        """
        # 獲取用戶唯一標識(如:IP)
        # 允許一分鍾訪問10次
        num_request = 10
        time_request = 60
        now = self.ctime()
        ident = self.get_ident(request)
        self.ident = ident
        if ident not in RECORD:
            RECORD[ident] = [now, ]
            return True
        history = RECORD[ident]
        while history and history[-1] <= now - time_request:
            history.pop()
        if len(history) < num_request:
            history.insert(0, now)
            return True

    def wait(self):
        """
        多少秒后可以允許繼續訪問
        Optionally, return a recommended number of seconds to wait before
        the next request.
        """
        last_time = RECORD[self.ident][0]
        now = self.ctime()
        return int(60 + last_time - now)


class TestView(APIView):
    throttle_classes = [TestThrottle, ]

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

    def throttled(self, request, wait):
        """訪問次數被限制時,定制錯誤信息"""

        class Throttled(exceptions.Throttled):
            default_detail = '請求被限制.'
            extra_detail_singular = '請 {wait} 秒之后再重試.'
            extra_detail_plural = '請 {wait} 秒之后再重試.'
        raise Throttled(wait)

 基於用戶IP顯示訪問頻率(利於Django緩存)

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

from rest_framework import exceptions
from rest_framework.throttling import SimpleRateThrottle


class TestThrottle(SimpleRateThrottle):

    # 配置文件定義的顯示頻率的Key
    scope = "test_scope"

    def get_cache_key(self, request, view):
        if not request.user:
            ident = self.get_ident(request)
        else:
            ident = request.user
        return self.cache_format % {
            'scope': self.scope,
            'ident': ident
        }


class TestView(APIView):
    throttle_classes = [TestThrottle, ]

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

    def throttled(self, request, wait):
        """訪問次數被限制時,定制錯誤信息"""
        class Throttled(exceptions.Throttled):
            default_detail = '請求被限制.'
            extra_detail_singular = '請 {wait} 秒之后再重試.'
            extra_detail_plural = '請 {wait} 秒之后再重試.'
        raise Throttled(wait)
views.py
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES': {
        'test_scope': '10/m',
    },
}
settings

 view中限制請求頻率

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES': {
        'xxxxxx': '10/m',
    },
}
settings
from rest_framework.views import APIView
from rest_framework.response import Response

from rest_framework import exceptions
from rest_framework.throttling import ScopedRateThrottle


# 繼承 ScopedRateThrottle
class TestThrottle(ScopedRateThrottle):

    def get_cache_key(self, request, view):
        if not request.user:
            ident = self.get_ident(request)
        else:
            ident = request.user

        return self.cache_format % {
            'scope': self.scope,
            'ident': ident
        }


class TestView(APIView):
    throttle_classes = [TestThrottle, ]

    # 在settings中獲取 xxxxxx 對應的頻率限制值
    throttle_scope = "xxxxxx"

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

    def throttled(self, request, wait):
        """
        訪問次數被限制時,定制錯誤信息
        """

        class Throttled(exceptions.Throttled):
            default_detail = '請求被限制.'
            extra_detail_singular = '請 {wait} 秒之后再重試.'
            extra_detail_plural = '請 {wait} 秒之后再重試.'

        raise Throttled(wait)
views.py

匿名時用IP限制+登錄時用Token限制

REST_FRAMEWORK = {
    'UNAUTHENTICATED_USER': None,
    'UNAUTHENTICATED_TOKEN': None,
    # 登錄認證
    "DEFAULT_AUTHENTICATION_CLASSES": [],
    # 權限控制
    "DEFAULT_PERMISSION_CLASSES": [],
    # 頻率控制
    "DEFAULT_THROTTLE_CLASSES": [],
    "DEFAULT_THROTTLE_RATES": {
        'test_scope': '10/m',
        'anon': '10/m',
        'user': '20/m',
    }
}
settings
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.throttling import SimpleRateThrottle


class LuffyAnonRateThrottle(SimpleRateThrottle):
    """匿名用戶,根據IP進行限制"""
    scope = "anon"

    def get_cache_key(self, request, view):
        # 用戶已登錄,則跳過 匿名頻率限制
        if request.user:
            return None

        return self.cache_format % {
            'scope': self.scope,
            'ident': self.get_ident(request)
        }


class LuffyUserRateThrottle(SimpleRateThrottle):
    """登錄用戶,根據用戶token限制"""
    scope = "user"

    def get_ident(self, request):
        """認證成功時:request.user是用戶對象;request.auth是token對象"""
        # return request.auth.token
        return "user_token"

    def get_cache_key(self, request, view):
        """獲取緩存key"""
        # 未登錄用戶,則跳過 Token限制
        if not request.user:
            return None

        return self.cache_format % {
            'scope': self.scope,
            'ident': self.get_ident(request)
        }


class TestView(APIView):
    throttle_classes = [LuffyUserRateThrottle, LuffyAnonRateThrottle, ]

    def get(self, request, *args, **kwargs):
        return Response('GET請求,響應內容')
views.py

 

 request.META包含請求信息

 {'ALLUSERSPROFILE': 'C:\\ProgramData',
        'APPDATA': 'C:\\Users\\Administrator\\AppData\\Roaming',
        'COMMONPROGRAMFILES': 'C:\\Program Files\\Common Files',
        'COMMONPROGRAMFILES(X86)': 'C:\\Program Files (x86)\\Common Files',
        'COMMONPROGRAMW6432': 'C:\\Program Files\\Common Files',
        'COMPUTERNAME': 'PC201712041709',
        'COMSPEC': 'C:\\Windows\\system32\\cmd.exe',
        'DJANGO_SETTINGS_MODULE': 'restdemo.settings',
        'FP_NO_HOST_CHECK': 'NO', 'HOMEDRIVE': 'C:',
        'HOMEPATH': '\\Users\\Administrator',
        'LOCALAPPDATA': 'C:\\Users\\Administrator\\AppData\\Local',
        'LOGONSERVER': '\\\\PC201712041709',
        'NUMBER_OF_PROCESSORS': '4', 'OS': 'Windows_NT',
        'PATH': 'C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python36;C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python36\\Scripts;C:\\Python27;E:\\MySQL Server 5.6\\bin;C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python36\\Scripts\\;C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python36\\;C:\\Users\\Administrator\\AppData\\Local\\atom\\bin',
        'PATHEXT': '.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC',
        'PROCESSOR_ARCHITECTURE': 'AMD64',
        'PROCESSOR_IDENTIFIER': 'Intel64 Family 6 Model 60 Stepping 3, GenuineIntel',
        'PROCESSOR_LEVEL': '6', 'PROCESSOR_REVISION': '3c03',
        'PROGRAMDATA': 'C:\\ProgramData',
        'PROGRAMFILES': 'C:\\Program Files',
        'PROGRAMFILES(X86)': 'C:\\Program Files (x86)',
        'PROGRAMW6432': 'C:\\Program Files',
        'PSMODULEPATH': 'C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\Modules\\',
        'PUBLIC': 'C:\\Users\\Public', 'PYCHARM_HOSTED': '1', 'PYTHONIOENCODING': 'UTF-8',
        'PYTHONPATH': 'C:\\Users\\Administrator\\PycharmProjects\\s9\\restdemo', 'PYTHONUNBUFFERED': '1',
        'SESSIONNAME': 'Console', 'SYSTEMDRIVE': 'C:', 'SYSTEMROOT': 'C:\\Windows',
        'TEMP': 'C:\\Users\\ADMINI~1\\AppData\\Local\\Temp', 'TMP': 'C:\\Users\\ADMINI~1\\AppData\\Local\\Temp',
        'USERDOMAIN': 'PC201712041709',
        'USERNAME': 'Administrator',
        'USERPROFILE': 'C:\\Users\\Administrator',
        'WINDIR': 'C:\\Windows', 'WINDOWS_TRACING_FLAGS': '3',
        'WINDOWS_TRACING_LOGFILE': 'C:\\BVTBin\\Tests\\installpackage\\csilogfile.log',
        'RUN_MAIN': 'true', 'SERVER_NAME': 'PC201712041709',
        'GATEWAY_INTERFACE': 'CGI/1.1', 'SERVER_PORT': '8000',
        'REMOTE_HOST': '',
        'CONTENT_LENGTH': '',
        'SCRIPT_NAME': '',
        'SERVER_PROTOCOL': 'HTTP/1.1',
        'SERVER_SOFTWARE': 'WSGIServer/0.2',
        'REQUEST_METHOD': 'GET',
        'PATH_INFO': '/authors/',
        'QUERY_STRING': 'token=8204b8e3ac40bf59ae480d17c146b51a',
        'REMOTE_ADDR': '127.0.0.1',
        'CONTENT_TYPE': 'text/plain',
        'HTTP_HOST': '127.0.0.1:8000',
        'HTTP_CONNECTION': 'keep-alive',
        'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',
        'HTTP_UPGRADE_INSECURE_REQUESTS': '1',
        'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
        'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br', 'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.9', 'HTTP_COOKIE': 'csrftoken=jtus3l4GJEc9TFXWYCWxkBIZprcOv7C1vFMIyOHs7Zkxt015FwVZ2KEEeDV6LOyN', 'wsgi.input': <_io.BufferedReader name=832>, 'wsgi.errors': <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>, 'wsgi.version': (1, 0), 'wsgi.run_once': False, 'wsgi.url_scheme': 'http', 'wsgi.multithread': True, 'wsgi.multiprocess': False, 'wsgi.file_wrapper': <class 'wsgiref.util.FileWrapper'>}
View Code

 根據URL傳入的后綴,決定數據如何渲染到頁面上

4、版本控制

1、添加配置

2、設置路由

3、獲取版本  

 

REST_FRAMEWORK = {
    'DEFAULT_VERSION': 'v1',            # 默認版本
    'ALLOWED_VERSIONS': ['v1', 'v2'],   # 允許的版本
    'VERSION_PARAM': 'version'          # URL中獲取值的key
}
--------------------------------------------------------------------------
urlpatterns = [
    path('admin/', admin.site.urls),
    re_path("test/", views.TestView.as_view(), name="test")
]
--------------------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import QueryParameterVersioning


class TestView(APIView):
    versioning_class = QueryParameterVersioning

    def get(self, request, *args, **kwargs):
        # 獲取版本
        print(request.version)
        # 獲取版本管理的類
        print(request.versioning_scheme)
        # 反向生成URL
        reverse_url = request.versioning_scheme.reverse('test', request=request)
        print(reverse_url)
        return Response('GET請求,響應內容')
基於url的get傳參方式
REST_FRAMEWORK = {
    'DEFAULT_VERSION': 'v1',            # 默認版本
    'ALLOWED_VERSIONS': ['v1', 'v2'],   # 允許的版本
    'VERSION_PARAM': 'version'          # URL中獲取值的key
}
--------------------------------------------------------------------------
urlpatterns = [
    path('admin/', admin.site.urls),
    re_path("test/(?P<version>[v1|v2]+)/", views.TestView.as_view(), name="test")
]
--------------------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import URLPathVersioning


class TestView(APIView):
    versioning_class = URLPathVersioning

    def get(self, request, *args, **kwargs):
        # 版本
        print(request.version)
        # 獲取版本管理的類
        print(request.versioning_scheme)
        # 反向生成URL
        reverse_url = request.versioning_scheme.reverse('test', request=request)
        print(reverse_url)
        return Response('GET請求,響應內容')
基於url的正則方式
如:Accept: application/json; version=1.0
--------------------------------------------------------------------------
REST_FRAMEWORK = {
    'DEFAULT_VERSION': 'v1',            # 默認版本
    'ALLOWED_VERSIONS': ['v1', 'v2'],   # 允許的版本
    'VERSION_PARAM': 'version'          # URL中獲取值的key
}
--------------------------------------------------------------------------
urlpatterns = [
    path('admin/', admin.site.urls),
    re_path("test/", views.TestView.as_view(), name="test")
]
--------------------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import AcceptHeaderVersioning


class TestView(APIView):
    versioning_class = AcceptHeaderVersioning

    def get(self, request, *args, **kwargs):
        # 獲取版本 HTTP_ACCEPT頭
        print(request.version)
        # 獲取版本管理的類
        print(request.versioning_scheme)
        # 反向生成URL
        reverse_url = request.versioning_scheme.reverse('test', request=request)
        print(reverse_url)

        return Response('GET請求,響應內容')
基於 accept 請求頭方式
如:v1.example.com
------------------------------------------------------------------
ALLOWED_HOSTS = ['*']
REST_FRAMEWORK = {
    'DEFAULT_VERSION': 'v1',  # 默認版本
    'ALLOWED_VERSIONS': ['v1', 'v2'],  # 允許的版本
    'VERSION_PARAM': 'version'  # URL中獲取值的key
}
-------------------------------------------------------------------
urlpatterns = [
    path('admin/', admin.site.urls),
    re_path("test/", views.TestView.as_view(), name="test")
]
-------------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import HostNameVersioning


class TestView(APIView):
    versioning_class = HostNameVersioning

    def get(self, request, *args, **kwargs):
        # 獲取版本
        print(request.version)
        # 獲取版本管理的類
        print(request.versioning_scheme)
        # 反向生成URL
        reverse_url = request.versioning_scheme.reverse('test', request=request)
        print(reverse_url)

        return Response('GET請求,響應內容')
基於主機名方法
如:example.com/v1/users/
----------------------------------------------------------------------
REST_FRAMEWORK = {
    'DEFAULT_VERSION': 'v1',  # 默認版本
    'ALLOWED_VERSIONS': ['v1', 'v2'],  # 允許的版本
    'VERSION_PARAM': 'version'  # URL中獲取值的key
}
----------------------------------------------------------------------
from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'^v1/', ([
                      url(r'test/', TestView.as_view(), name='test'),
                  ], None, 'v1')),
    url(r'^v2/', ([
                      url(r'test/', TestView.as_view(), name='test'),
                  ], None, 'v2')),

]
----------------------------------------------------------------------
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import NamespaceVersioning


class TestView(APIView):
    versioning_class = NamespaceVersioning

    def get(self, request, *args, **kwargs):
        # 獲取版本
        print(request.version)
        # 獲取版本管理的類
        print(request.versioning_scheme)
        # 反向生成URL
        reverse_url = request.versioning_scheme.reverse('test', request=request)
        print(reverse_url)

        return Response('GET請求,響應內容')
基於django路由系統的namespace
REST_FRAMEWORK = {
    'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.URLPathVersioning",  # url正則
    'DEFAULT_VERSION': 'v1',
    'ALLOWED_VERSIONS': ['v1', 'v2'],
    'VERSION_PARAM': 'version'
}

 

5、解析器

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

application/x-www-form-urlencoded   
multipart/form-data

 

    'DEFAULT_PARSER_CLASSES': (
        'rest_framework.parsers.JSONParser',
        'rest_framework.parsers.FormParser',
        'rest_framework.parsers.MultiPartParser'
    )

  

 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'),
]
urls.py
#!/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請求,響應內容')
views.py

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'),
]
urls.py
#!/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請求,響應內容')
views.py

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'),
]
urls.py
#!/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請求,響應內容')
views.py
<!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>
upload.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'),
]

 
urls.py
#!/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請求,響應內容')
views.py
upload.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'),
]
urls.py
#!/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請求,響應內容')
views.py

f. 全局使用

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

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

urlpatterns = [
    url(r'test/', TestView.as_view(), name='test'),
]
urls.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
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請求,響應內容')
views.py

注意:個別特殊的值可以通過Django的request對象 request._request 來進行獲取

6、路由
path("books/", views.BookView.as_view(), name="books"), re_path('books/(?P<pk>\d+)/', views.BookDetailView.as_view(), name="detail_book")

簡單點---->

from blog import views
from rest_framework import routers
routers = routers.DefaultRouter() routers.register("authors",views.AuthorModelView)
            # 不能有$符
urlpatterns=[
    path("",include("routers.urls")),# 直接分發到routers下
] 
urlpatterns +=routers.urls

效果:

7、 視圖

 restful是基於CBV操作的,而每一種請求都對應一個函數,

 

而restful  提供了五個類

from rest_framework import mixins

CreateModelMixin
ListModelMixin
RetrieveModelMixin
UpdateModelMixin
DestroyModelMixin     
 
        
get---------------->  list/retrieve   # 查看全部/查看單條(pk=?)
post--------------->  create       # 添加一條數據
delete------------->  destroy      # 刪除一條數據(pk=?)
put---------------->  update       # 更新局部數據(pk=?)
from rest_framework import generics 對上面方法進行了組合,generics下有很多類都有多個方法。都繼承了GenericAPIView

 

指定方式對應的函數

AutherModelView.as_view({"get":lest})

但是相對應的類應該繼承  viewsets.ModelViewSet

 

8、序列化

對queryset序列化以及對請求數據格式校驗

--------------------------------------------
from django.shortcuts  import HttpResponse
HttpResponse 只能返回字符串和列表數字,復雜的數據結構不能序列化
如果是一個queryset    先list()
--------------------------------------------
自己構造結構
-------------------------------------------
from django.core  import serializers
publish_list = Publish.objects.all()
ret = serializers.serialize("json",publish_list)
return HttpResponse(ret)
--------------------------------------------
from rest_framework import serializers  # 推薦


class BookModelSerializers(serializers.ModelSerializer):
    title = serializers.CharField(source="course.title")
    level = serializers.CharField(source="course.get_level_display")
    authors = serializers.SerializerMethodField()
    # 顯示超鏈接 view_name: 鏈接的別名
    publish = serializers.HyperlinkedIdentityField(view_name="publish_detail",
                                                   lookup_field="publish_id",
                                                   lookup_url_kwarg="pk")

    def get_authors(self, book_obj):
        temp = []
        for obj in book_obj.authors.all():
            temp.append(obj.name)
        return temp

    class Meta:
        model = Book
        # fields = "__all__"

        fields = ["course", "title", "level", "publish", "authors"]
        depth = 1  # 當涉及到外鍵關聯時,會在查找一層,不再是只有一個id,默認為0
     extra_kwargs = {'category':{'write_only':True}}
required=False    // 提交的數據不需要校驗
read_only=True    // 只在序列化的時候有用;校驗的時候無用
write_only=True    // 只在校驗的時候有用;序列化的時候無用
    -    category = serializers.ChoiceField(choices=CHOICES,source="get_category_display",read_only=False)        # 只在序列化的時候有用
    -    w_category = serializers.ChoiceField(choices=CHOICES,write_only=True)                                    # 只在校驗的時候有用
    -    author_list = serializers.ListField(write_only=True)                                                    # 
    

 

url(^"publishers/(?P<pk>\d+)$",views.PublishDetailViewSet.as_view(),name="publish_detail")
def get(*args):
    book_obj = Book.objects.all()
    ret = BookModelSerializers(book_list, many=True)  # 是一個OrderdDict類型
    # many默認是False,如果序列化單個對象,many=false
    return HttpResponse(ret.data)  # 序列化后的數據都在。data里
 
[
    {
        "id": 1,
        "title": "",
        "price": 55,
        "pub_date": null,
        "publish": 1,
        "authors": [
            1,
            2,
            3
        ]
    },
    {
        "id": 2,
        "title": "",
        "price": 3,
        "pub_date": null,
        "publish": 1,
        "authors": [
            1,
            2
        ]
    },
    {
        "id": 3,
        "title": "",
        "price": 21,
        "pub_date": null,
        "publish": 2,
        "authors": []
    }
]


class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.IntegerField()
    pub_date = models.DateField()
    publish = models.ForeignKey("Publish", on_delete=models.CASCADE)
    authors = models.ManyToManyField("Author")

    def __str__(self):
        return self.title
數據展示
添加數據  時------->也可以進行校驗(modelform一樣)
bs = BookModelSerializers(data = request.data)
if bs.is_valid():
    # # 驗證后的數據在.validated_data
    # # 錯誤信息在.errors
    bs.save()    

validated_字段名(self,value)
validated(self,attrs)  這兩個自定義驗證
  serializers.ValidationError('xxxxxxx')
在字段聲明時,定義驗證
serializers.ChoiceField(validators=[func_name,]) # 權重比上面的高
def func_name(value):
  和上面的校驗一樣
   更新操作 時------>進行校驗 model_obj=.... bs = BookModelSerializers(instance=model_obj,data=request.data) if bs.is_valid(): bs.save() # update()  

 

# 當繼承的是普通 的Serializers類時
# 必須重寫update 序列化類里 def update(self,instance,valitdated_data):   instance.xx
= validated_data.get('',instance.xx)   if validtated_data.get('xxxx'):     instance.xx.set([]) instace.save()   return instance
# create()必須重寫這個方法

def create(self,validated_data):
  book = .....create
  book.author.add(*args)
  return book

 

authors = validated_data.pop("authors")

obj = Book.objects.create(**validated_data)

obj.authors.add(*authors)

 

9、分頁
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination,CursorPagination
 
        
from rest_framework.paginatio
class MyPagination(CursorPagination):
    # - 根據加密:http: // www.luffycity.com / api / v1 / student /?cursor = erd8
    cursor_query_param = 'cursor'   #  根據什么參數取數據?cursor=1
    ordering = '-created'
    page_size = 2

 

n import PageNumberPagination,LimitOffsetPagination
class MyPagination(PageNumberPagination):
  # 根據頁碼分頁 page_size
= 2 # 限制每頁有幾條數據 page_query_param = "page" # 根據什么參數取數據?page=1 page_size_query_param = "size" # 當存在size參數時,可以臨時修改每頁獲得數據個數 max_page_size = 10 # 當有參數size時,臨時獲得數據也不能超過的數據限制 class MyPagination(LimitOffsetPagination): default_limit=2 # 限制取多少數據
  # /?offset=60&limit=10  #從60開始取10個    游標
book_list
= Book.objects.all() pnp = MyPagination() book_page = pnp.paginate_queryset(book_list,request,self) ret = BookModelSerializers(book_page,many=True) return HttpResponse(ret.data) # return pnp.get_paginated_response(ret.data)# 調用了Response
from rest_framework import viewsets

class AuthorModelView(viewsets.ModelViewSet):
    queryset = Author.objects.all()
    serializer_class = AuthorModelSerializers

    pagination_class = MyPagination
- 根據頁碼:http://www.luffycity.com/api/v1/student/?page=1&size=10
- 根據索引:http://www.luffycity.com/api/v1/student/?offset=60&limit=10
- 根據加密:http://www.luffycity.com/api/v1/student/?page=erd8

 

頁碼越大速度越慢,為什么如何解決:

  原因:頁碼越大向后需要掃描的行數越多,因為每次都是從0開始掃描

  解決:

      - 限制顯示的頁數  前端展示的頁數 就到page=200

      - where id>10000000000 limit 10  在前端cookies保留當前頁碼最大id,最小id

      - 將page頁碼  加密    /?page=erd8

10、渲染器

對於瀏覽器會給一個頁面用來顯示信息。

應用中間件要有  ‘rest_framework’

json

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

表格

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表單

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

自定義顯示模板

http://127.0.0.1:8000/test/?format=html
http://127.0.0.1:8000/test.html
http://127.0.0.1:8000/test/ 

瀏覽器格式API+JSON

http://127.0.0.1:8000/test/?format=api
http://127.0.0.1:8000/test.api
http://127.0.0.1:8000/test/ 
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers

from rest_framework.renderers import JSONRenderer
from rest_framework.renderers import BrowsableAPIRenderer

from .. import models


class TestSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = "__all__"


class CustomBrowsableAPIRenderer(BrowsableAPIRenderer):
    def get_default_renderer(self, view):
        return JSONRenderer()


class TestView(APIView):
    renderer_classes = [CustomBrowsableAPIRenderer, ]

    def get(self, request, *args, **kwargs):
        user_list = models.UserInfo.objects.all().first()
        ser = TestSerializer(instance=user_list, many=False)
        return Response(ser.data, template_name='user_detail.html')

views.py
views.py

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM