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