1 什么是RESTful

  • REST 指的是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。满足这些约束条件和原则的应用程序或设计就是 RESTful。

2 RESTful设计

  • API与用户的通信协议,总是使用HTTPS协议。

  • 域名

    • https://api.example.com 子域名的方式,会存在跨域问题

    • https://example.org/api/ 更简单(推荐)

  • 版本

    • URL,如:https://example.org/api/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 - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

    # 状态码可以自己自定义
    - return HttpResponse(json.dumps(ret),status=800) # 请求成功会返回800
    状态码和code结合的情况。
    更多看这里:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
  • 错误处理,状态码是4xx时,应返回错误信息,error当做key。

    {
    error: "Invalid API key"
    }
  • 返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范。

    GET /collection:返回资源对象的列表(数组)
    GET /collection/1/:返回单个资源对象
    POST /collection:返回新生成的资源对象
    PUT /collection/1/:返回完整的资源对象
    PATCH /collection/1/:返回完整的资源对象
    DELETE /collection/1/:返回一个空文档
  • 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"
    }}

3 基于Django Rest Framework框架实现

3.1 安装:

pip3 install djangorestframework

3.2 基本流程

  • View和APIView区别?

    请求和返回使用的DRF的Request、Response,而不是Django的request、HttpResponse;
    任何APIException异常都会被捕获到,并且处理成合适的响应信息;
    在进行dispatch()分发前,会对请求进行版本获取、身份认证、权限检查、流量控制等操作。
  • 先在django的settings.py中注册rest_framework

    INSTALLED_APPS = [
    ...
    'rest_framework',
    ]
  • urls.py

    from django.conf.urls import url, include
    from web.views.s1_api import TestView urlpatterns = [
    url(r'^test/', TestView.as_view()),
    ]
  • views.py

    from rest_framework.views import APIView
    from rest_framework.response import Response class TestView(APIView):
    def dispatch(self, request, *args, **kwargs):
    """
    请求到来之后,都要执行dispatch方法,dispatch方法根据请求方式不同触发 get/post/put等方法
    注意:APIView中的dispatch方法有好多好多的功能
    """
    return super().dispatch(request, *args, **kwargs) def get(self, request, *args, **kwargs):
    return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
    return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
    return Response('PUT请求,响应内容')
    def delete(self, request, *args, **kwargs):
    return Response('DELETE请求,响应内容')

3.3 认证

  • 用户url传入内容认证

    # CBV首先要走的dispatch方法,所以重写dispatch中的内容可以让代码执行前自定义执行某些内容。

    class MyAuthentication(object):
    def authenticate(self,request):
    username = request._request.GET.get("username")
    print(username)
    obj = models.UserInfo.objects.filter(user=username).first()
    if not obj:
    raise exceptions.AuthenticationFailed("用户认证失败")
    return (obj.user,None)
    def authenticate_header(self,val):
    pass

    class MyIndex(APIView):
    authentication_classes = [MyAuthentication] # 加了这个就必须先执行定义的类
    def get(self, request, *args, **kwargs):
    return Response({"get":"get请求"})
    def post(self, request, *args, **kwargs):
    return Response({"post":"post请求"})
    def put(self, request, *args, **kwargs):
    return Response({"put":"put请求"})
    def delete(self, request, *args, **kwargs):
    return Response({"delete":"delete请求"})
  • 基于token实现的用户认证

    # model
    from django.db import models

    class User(models.Model):
    username = models.CharField(max_length=12) class Usertoken(models.Model):
    user = models.OneToOneField(to="User")
    token = models.CharField(max_length=64)

    model

    from rest_framework import exceptions
    from rest_framework.response import Response
    from rest_framework.views import APIView
    from app01 import models
    class MyAuthentication(object):
    def authenticate(self,request):
    token = request._request.GET.get(token)
    token_obj = models.UserToken.objects.filter(token=token).first()
    if obj:
    return (token_obj.user,token_obj) # 将元组中user对象和token对象封装到request中
    else:
    raise exceptions.AuthenticationFailed("用户认证失败")
    def authenticate_header(self,val):
    pass
    class Order(APIView):
    authentication_classes = [MyAuthentication,]
    def get(self,request,*args,**kwargs):
    # reuqest.user==》token_obj.user
    # request.auth==》token_obj
    return Response({"ret":"请求成功"})
  • 认证有三种返回值,一种是返回元组,赋值分别赋值给user和auth封装到request中,一种是返回None,交给下一个认证处理,如果都返回None,那么返回的是一个匿名用户,如果出错则抛出异常。

  • 全局使用,在settings中做如下配置。

    # 在rest_framework的settings.py中,最上面有一段话,例如,项目的“settings.py”文件可能如下所示:

    REST_FRAMEWORK = {"DEFAULT_AUTHENTICATION_CLASSES":['xxx.xxx.MyAuthentication']}
    # 和默认DEFAULTS是一样的写法,只是在我们自己的配置文件中
    #此模块用于访问REST framework的配置,先检查用户设置,再检查默认设置DEFAULTS
  • 匿名用户

    REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES":['xxx.xxx'],
    "UNAUTHENTICATED_USER":None, # 匿名或者未登录reuqest.user=None
    "UNAUTHENTICATED_TOKEN":None, # 匿名或者未登录request.auth=None
    }
  • 内置认证类

    使用的时候都继承BaseAuthentication

    from rest_framework.authentication import BaseAuthentication
    class MyAuthentication(BaseAuthentication):
    def authenticate(self,request):
    token = request._request.GET.get(token)
    obj = models.UserToken.objects.filter(token=token).first()
    if obj:
    return (obj.user,obj) # 将元组中的数据封装到request中
    else:
    raise exceptions.AuthenticationFailed("用户认证失败")
    # 直接继承这个类不需要写authenticate_header方法了

    BasicAuthentication浏览器对用户名密码加密,放在请求头中发过去的

  • 源码解析

    1.django中的类先执行dispatch方法,Request()对象中封装了原始的self._request=request和authenticators(是一个列表,里面是一个个对象)。authenticators实际上是authentication_classes一个全局配置,使用列表推导式得到的。所以我们要将我们的认证类注册到authentication_classes中,写在我们需要认证的类中。这就是局部认证。全局认证需要我们在配置中按照源码中的配置进行相应的修改,写在我们的配置中。
    2.执行initial方法,执行perform_authentication就是用户认证
    3.执行request中的user属性
    4.执行self._authenticate()方法,循环authenticators,执行authenticate方法,这个方法是必须要写的,也就是我们写认证类时候必须要写的方法,还有一个方法是authenticate_header。需要返回一个元组,将返回值赋值给user和auth
    5._not_authenticated方法就是匿名用户
    6.实际上我们重写的就是BaseAuthentication类中的方法,所以我们要继承这个类。
    7.而其他继承BaseAuthentication这个类的子类,就是内置认证类。

3.4权限

  • 权限基本实现

    # model
    from django.db import models

    class User(models.Model):
    usertype_choice = (
    (1,"普通用户"),
    (2,"vip用户"),
    (3,"svip用户"),
    )
    usertype = models.IntegerField(choices=usertype_choice)
    user = models.CharField(max_length=12)

    model

    # 局部添加权限

    from rest_framework.permissions import BasePermission

    class Mypermission(BasePermission):
    message = "必须是超级VIP才能访问" # 这句话就是抛出异常的信息。
    def has_permission(self, request, view):
    if request.user.usertype != 3: # 不是svip的就没有权限
    return False # 没权限
    return True # 有权限

    class Order(APIView):
    permission_classes = [Mypermission,]
    def get(self,request,*args,**kwargs):
    return Response({"ret":"请求成功"})
  • 全局添加权限

    REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES":['xxx.xxx.MyAuthentication'], # 认证
    "DEFAULT_AUTHENTICATION_CLASSES":['xxx.xxx.Mypermission'], # 权限
    }

3.5 访问频率的限制(节流)

  • 匿名用户,我们自己实现的

    # 通过ip限制
    from rest_framework.throttling import BaseThrottle

    VISIT_RECORD = {} # 最终放在缓存中
    LIMIT_TIME = 10
    FREQUENCY = 3

    class VisitThrottle(BaseThrottle):
    """LIMIT_TIME时间内只能访问FREQUENCY次"""
    def __init__(self):
    self.history = None

    def allow_request(self, request, view):
    # 获取访问用户的ip地址
    remote_addr = request.META.get('REMOTE_ADDR')
    print(remote_addr)
    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 ctime - history[-1] > LIMIT_TIME:
    history.pop()
    if len(history) < FREQUENCY:
    history.insert(0, ctime)
    return True
    # 再等多久之后就可以访问了,每次刷新都变化,动态的
    def wait(self):
    ctime = time.time()
    # 当当前时间和最早一次访问的时间差等于LIMIT_TIME时间的时候,就可以再次访问
    return LIMIT_TIME-(ctime-self.history[-1]) class Index(APIView):
    # 局部限制
    throttle_classes = [VisitThrottle,]

    def get(self, request, *args, **kwargs):
    print(request.user, request.auth, "******")
    return Response('GET请求,响应内容')
  • 全局配置

    REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES":['xxx.xxx.MyAuthentication'], # 认证
    "DEFAULT_AUTHENTICATION_CLASSES":['xxx.xxx.Mypermission'], # 权限
    "DEFAULT_THROTTLE_CLASSES":['xxx.xxx.VisitThrottle'], # 限流
    }
  • 内置访问频率限制

    SimpleRateThrottle

    # 源码
    class SimpleRateThrottle(BaseThrottle):
    """
    A simple cache implementation, that only requires `.get_cache_key()`
    to be overridden.

    The rate (requests / seconds) is set by a `rate` attribute on the View
    class. The attribute is a string of the form 'number_of_requests/period'.

    Period should be one of: ('s', 'sec', 'm', 'min', 'h', 'hour', 'd', 'day')

    Previous request information used for throttling is stored in the cache.
    """
    cache = default_cache
    timer = time.time
    cache_format = 'throttle_%(scope)s_%(ident)s'
    scope = None
    # 4.这个眼熟啊,就是我们rest_frame的配置项,我们就需要在我们的配置文件中添加DEFAULT_THROTTLE_RATES
    THROTTLE_RATES = api_settings.DEFAULT_THROTTLE_RATES

    def __init__(self):
    # 1.找rate,没有执行get_rate()
    if not getattr(self, 'rate', None):
    # 6.self.rate = "3/m"
    self.rate = self.get_rate()
    # 7.将"3/m"给到parse_rate方法
    # 12.从第11步拿到元组(3,60),分别赋值self.num_requests=3,self.duration=60
    self.num_requests, self.duration = self.parse_rate(self.rate)

    def get_cache_key(self, request, view):
    """
    Should return a unique cache-key which can be used for throttling.
    Must be overridden.

    May return `None` if the request should not be throttled.
    """
    raise NotImplementedError('.get_cache_key() must be overridden')

    def get_rate(self):
    """
    Determine the string representation of the allowed request rate.
    """
    # 2.又找scope,但是scope是None,会抛出异常,说你必须有一个rate或者scope,所以我们用这个类的时候必须给scope赋值
    if not getattr(self, 'scope', None):
    msg = ("You must set either `.scope` or `.rate` for '%s' throttle" %
    self.__class__.__name__)
    raise ImproperlyConfigured(msg)

    try:
    # 3.走到这以后让我们从THROTTLE_RATES取scope,THROTTLE_RATES是我们的类变量
    # 5.从我们设置的配置中获取到scope的值,返回给rate
    return self.THROTTLE_RATES[self.scope]
    except KeyError:
    msg = "No default throttle rate set for '%s' scope" % self.scope
    raise ImproperlyConfigured(msg)

    def parse_rate(self, rate):
    """
    Given the request rate string, return a two tuple of:
    <allowed number of requests>, <period of time in seconds>
    """
    if rate is None:
    return (None, None)
    # 8.rate = ‘3/m’,不为空进行字符串切割num = "3",period = "m"
    num, period = rate.split('/')
    # 9.num_requests = 3
    num_requests = int(num)
    # 10.从字典中取出period[0] = "m",即duration = 60
    duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
    # 11.将元组(3,60)返回给第7步
    return (num_requests, duration)
    # 13.通过dispatch方法,可以看出必须要有allow_request方法,执行allow_request方法
    def allow_request(self, request, view):
    """
    Implement the check to see if the request should be throttled.

    On success calls `throttle_success`.
    On failure calls `throttle_failure`.
    """
    if self.rate is None:
    return True
    # 14.执行get_cache_key方法,即该方法必须写,
    self.key = self.get_cache_key(request, view)
    if self.key is None:
    return True
    # 15.从django内置缓存中获取到key对应的值(我们将ip或者userid作为key,时间记录列表作为值,那么我们在写get_cache_key方法时就要返回一个ip或者userid,即父类的get_ident方法,就是返回的ip),就是访问时间的记录。
    self.history = self.cache.get(self.key, [])
    self.now = self.timer()

    # Drop any requests from the history which have now passed the
    # throttle duration
    # 16.当历史值存在,有访问记录,当第一次访问的时间小于等于当前时间-步骤11的60,证明不是duration时间内的记录,是很久之前的,就将他从列表中删除
    while self.history and self.history[-1] <= self.now - self.duration:
    self.history.pop()
    # 17.历史记录的长度大于等于我们设定的次数,执行throttle_failure方法,返回Flase
    if len(self.history) >= self.num_requests:
    return self.throttle_failure()
    # 18.长度比我们设定的次数小则将当前时间插入到列表第一个位置,并返回True
    return self.throttle_success()

    def throttle_success(self):
    """
    Inserts the current request's timestamp along with the key
    into the cache.
    """
    self.history.insert(0, self.now)
    self.cache.set(self.key, self.history, self.duration)
    return True

    def throttle_failure(self):
    """
    Called when a request to the API has failed due to throttling.
    """
    return False

    def wait(self):
    """
    Returns the recommended next request time in seconds.
    """
    if self.history:
    remaining_duration = self.duration - (self.now - self.history[-1])
    else:
    remaining_duration = self.duration

    available_requests = self.num_requests - len(self.history) + 1
    if available_requests <= 0:
    return None

    return remaining_duration / float(available_requests)

    匿名用户内置限流器

    # 内置限流器我们只需要实现scope这个类变量并重写get_cache_key方法返回self.get_ident(request),然后在配置中进行下面的配置即可。这是匿名用户的内置限流器
    class VisitThrottle(SimpleRateThrottle):
    scope = "love" # 这个名字可以自定义
    def get_cache_key(self, request, view):
    return self.get_ident(request) class Index(APIView):
    throttle_classes = [VisitThrottle,]

    def get(self, request, *args, **kwargs):
    print(request.user, request.auth, "******")
    return Response('GET请求,响应内容')

    配置文件

    REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES":['xxx.xxx.MyAuthentication'], # 认证
    "DEFAULT_AUTHENTICATION_CLASSES":['xxx.xxx.Mypermission'], # 权限
    "DEFAULT_THROTTLE_RATES":{"love":"3/m"}, # 匿名用户限流的
    }

    登录用户内置限流器

    # 登录的内置限流器,要继承UserRateThrottle,这个类继承的SimpleRateThrottle
    class UserThrottle(UserRateThrottle):
    scope = "user" # 使用用户名,或者用户id
    def get_cache_key(self, request, view):
    # 我们在认证的时候知道request中封装了user和auth,request.user就是user对象
    return request.user.username # 当前登录用户的用户名 class Index(APIView):
    throttle_classes = [UserThrottle,]

    def get(self, request, *args, **kwargs):
    print(request.user, request.auth, "******")
    return Response('GET请求,响应内容')

    配置文件

    REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES":['xxx.xxx.MyAuthentication'], # 认证
    "DEFAULT_AUTHENTICATION_CLASSES":['xxx.xxx.Mypermission'], # 权限
    "DEFAULT_THROTTLE_RATES":{"love":"3/m"}, # 匿名用户限流的,一分钟访问三次
    "DEFAULT_THROTTLE_RATES":{"user":"10/m"}, # 登录用户限流,一分钟访问10次
    }

    既有匿名用户也有登录用的情况

    # 登录的内置限流器,要继承UserRateThrottle,这个类继承的SimpleRateThrottle
    class VisitThrottle(SimpleRateThrottle):
    scope = "love" # 这个名字可以自定义
    def get_cache_key(self, request, view):
    return self.get_ident(request)

    class UserThrottle(UserRateThrottle):
    scope = "user"
    def get_cache_key(self, request, view):
    # 我们在认证的时候知道request中封装了user和auth,request.user就是user对象
    return request.user.username # 当前登录用户的用户名 class Index(APIView):
    authentication_classes = [MyAuthentication, ]
    throttle_classes = [VisitThrottle,]

    def get(self, request, *args, **kwargs):
    print(request.user, request.auth, "******")
    return Response('GET请求,响应内容')
    REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES":['xxx.xxx.MyAuthentication'], # 认证
    "DEFAULT_AUTHENTICATION_CLASSES":['xxx.xxx.Mypermission'], # 权限
    "DEFAULT_THROTTLE_CLASSES":['xxx.xxx.UserThrottle'], # 登录用户全局设置限流
    # 登录用户可以访问所有,对全局进行限制,匿名用户只能访问部分功能,只对部分功能做匿名用户限流即可,所有匿名用户使用局部方法,如果是匿名用户会优先选择局部的方法进行限流。
    "DEFAULT_THROTTLE_RATES":{"love":"3/m"}, # 匿名用户限流的,一分钟访问三次
    "DEFAULT_THROTTLE_RATES":{"user":"10/m"}, # 登录用户限流,一分钟访问10次
    }

    settings.py

3.6 drf中的request

  • 通过我们前几个组件可以看出drf中的request对象并不是我们django中以前使用的request,drf中的request对象封装了我们以前用的request。封装到了_request中,drf通过request._request才是我们以前用的request,所以drf中很多方法属性都要通过request._request获取,但是其实不需要,我们依然可以直接使用request,我们看一下drf源码

    from rest_framework.request import Request  # 从Request中找到下面方法
    # drf中的__getattr__方法
    def __getattr__(self, attr):
    """
    If an attribute does not exist on this instance, then we also attempt
    to proxy it to the underlying HttpRequest object.
    """
    try:
    # 如果执行我们drf中request没有的方法或属性,就会执行__getattr__方法,然后去request._request中去找对应的方法,即我们原始的request对象中去找
    return getattr(self._request, attr)
    except AttributeError:
    return self.__getattribute__(attr)
  • 当然drf中也封装了很多常用的方法和属性,示例

    from rest_framework.request import Request  # 从Request中找到下面属性

    @property
    def query_params(self):
    """
    More semantically correct name for request.GET.
    """
    return self._request.GET
    通过request.query_params ===> request._request.GET
    request.query_params.get("xxx") # 取url上的参数

3.7 版本组件

  • 版本通过url上传参?version=v1

    自定义版本获取

    # ?version=v1
    class MyVersion(object):
    def determine_version(self, request, *args, **kwargs):
    request.query_params.get("version")
    return version
    class User(APIView):
    # 局部视图使用
    versioning_class = MyVersion
    def get(self, request, *args, **kwargs):
    print(request.version, "******")
    return Response('GET请求,响应内容')

    内置类版本获取

    from rest_framework.versioning import QueryParameterVersioning

    class User(APIView):
    # 局部视图使用
    versioning_class = QueryParameterVersioning
    def get(self, request, *args, **kwargs):
    print(request.version, "******")
    return Response('GET请求,响应内容') # 配置文件中
    REST_FRAMEWORK = {
    "DEFAULT_VERSION":"v1",# 默认版本
    "ALLOWED_VERSIONS":['v1','v2'], # 允许的版本
    "VERSION_PARAM":"version",# 表示的参数的键是什么,比如?version=v1,设置的就是version
    }

    局部示例

    # 全局使用

    class User(APIView):
    def get(self, request, *args, **kwargs):
    print(request.version, "******")
    return Response('GET请求,响应内容') # 配置文件中
    REST_FRAMEWORK = {
    "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.QueryParameterVersioning",
    "DEFAULT_VERSION":"v1",# 默认版本
    "ALLOWED_VERSIONS":['v1','v2'], # 允许的版本
    "VERSION_PARAM":"version",# 表示的参数的键是什么,比如?version=v1,设置的就是version
    }

    全局配置应用

  • 版本通过url上路径传参 /v1/user/,推荐这种

    内置版本类

    # urls.py要进行修改
    # url上要对应改变
    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views

    urlpatterns = [
    path('admin/', admin.site.urls),
    url(r'^(?P<version>[v1|v2]+)/user/$', views.User.as_view(), name='users-list'),
    ]

    # 视图函数中.py
    from rest_framework.versioning import URLPathVersioning
    class User(APIView):
    # 局部视图使用
    versioning_class = URLPathVersioning
    def get(self, request, *args, **kwargs):
    print(request.version, "******")
    return Response({"ret":'GET请求,响应内容'}) # settings.py
    REST_FRAMEWORK = {
    "DEFAULT_VERSION":"v1",# 默认版本
    "ALLOWED_VERSIONS":['v1','v2'], # 允许的版本
    "VERSION_PARAM":"version",# 表示的参数的键是什么,比如?version=v1,设置的就是version
    }

    局部示例

    # 全局配置
    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views

    urlpatterns = [
    path('admin/', admin.site.urls),
    url(r'^(?P<version>[v1|v2]+)/user/$', views.User.as_view(), name='users-list'),
    ]

    # 视图函数中.py
    class User(APIView):
    def get(self, request, *args, **kwargs):
    print(request.version, "******")
    return Response({"ret":'GET请求,响应内容'}) # settings.py
    REST_FRAMEWORK = {
    "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning"
    "DEFAULT_VERSION":"v1",# 默认版本
    "ALLOWED_VERSIONS":['v1','v2'], # 允许的版本
    "VERSION_PARAM":"version",# 表示的参数的键是什么,比如?version=v1,设置的就是version
    }

    全局配置应用

  • 源码流程

    1.先执行dispatch方法,执行其中的initial方法
    2.initial方法中部分源码
    # Determine the API version, if versioning is in use.
    # 获取到版本,和处理版本的对象
    version, scheme = self.determine_version(request, *args, **kwargs)
    # 并将版本号和对象赋值到request,从request中就可以拿到version和处理版本的对象scheme
    request.version, request.versioning_scheme = version, scheme 3.determine_version方法就是去实例化我们处理版本的类,并但会版本和处理版本的对象。
    4.现在drf执行流程,先进行版本处理--》认证--》权限--》限流
    # 在URLPathVersioning或者QueryParameterVersioning中有一个reserve方法,用于反向解析找到url的
    # urls.py
    urlpatterns = [
    path('admin/', admin.site.urls),
    url(r'^(?P<version>[v1|v2]+)/user/$', views.User.as_view(), name='users-list'),
    ]
    # 视图函数中.py
    class User(APIView):
    def get(self, request, *args, **kwargs):
    print(request.version, "******")
    # request.versioning_scheme就是我们处理按本的对象,按照我们自己写的反向解析,传参要将url上正则的分组命名的关键字参数传入,但是使用drf的reserve不需要再传了,我们只需要传个request即可,request里面有个version,自己会加上。没有指定版本会生成当前url的版本
    u1 = request.versioning_scheme.reserve(viewname="users-list",request=request)
    print(u1) # django的reserve
    from django.urls import reverse
    reserve(viewname="users-list",kwargs={"version":"v1"})
    return Response({"ret":'GET请求,响应内容'}) 

    内置的版本处理有哪些方式?

    QueryParameterVersioning基于url参数做版本处理 如:GET /something/?version=0.1 HTTP/1.1
    HostNameVersioning基于域名做的版本处理,如:v1.example.com
    NamespaceVersioning基于命名空间做的版本处理,如:
    urlpatterns = [
    url(r'^v1/', include('users.urls', namespace='v1')),
    url(r'^v2/', include('users.urls', namespace='v2'))
    ]
    URLPathVersioning基于url路径做的版本处理,如:/v1/user/ ,推荐使用
    AcceptHeaderVersioning基于请求头处理的,如:Accept: application/json; version=1.0
  • 总结

    1.进行配置
    REST_FRAMEWORK = {
    "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning"
    "DEFAULT_VERSION":"v1",# 默认版本
    "ALLOWED_VERSIONS":['v1','v2'], # 允许的版本
    "VERSION_PARAM":"version",# 表示的参数的键是什么,比如?version=v1,设置的就是version
    }
    2.urls.py,如果有路由分发再使用路由分发
    urlpatterns = [
    path('admin/', admin.site.urls),
    url(r'^(?P<version>[v1|v2]+)/user/$', views.User.as_view(), name='users-list'),
    ]
    3.视图中使用
    class User(APIView):
    def get(self, request, *args, **kwargs):
    # 1.获取版本
    print(request.version, "******")
    # 2.获取版本处理的对象
    print(request.versioning_scheme)
    # 3.通过drf中的reserve使用版本处理对象进行反向解析
    u1 = request.versioning_scheme.reserve(viewname="users-list",request=request)
    print(u1) # 4.使用django提供的reserve
    from django.urls import reverse
    reserve(viewname="users-list",kwargs={"version":"v1"})
    return Response({"ret":'GET请求,响应内容'})

3.8 request.body和request.POST

  • request.body中有值,request.POST中不一定有值,想要request.POST中有值,需要满足以下两个条件:

    1.请求头要求
    Content-Type:application/x-www-form-urlencoded,此时request.POST才可能有值(从request.body解析)
    2.数据格式的要求
    name=xiaoqi&age=18&gender=女
  • 示例

    1.form表单,默认状态
    <form action="" method="post" >
    <input type="text" value="" name="name"> //会自动转换成我们需要的数据格式,携带者请求头
    <input type="submit" value="提交">
    </form>

    2.ajax请求
    $.ajax({
    url:"",
    type:"post",
    data:{"name":"xiaoqi"} //会自动转换成我们需要的数据格式,携带者请求头
    })

    $.ajax({
    url:"",
    type:"post",
    headers:{"Content-Type":"application/json"}, //请求头不符合,body有值,post无值
    data:{"name":"xiaoqi"}, //会自动转换成我们需要的数据格式
    success:function(ret){
    ...
    }
    })

    $.ajax({
    url:"",
    type:"post",
    headers:{"Content-Type":"application/json"}, //请求头不符合,body有值,post无值
    data:JSON.stringfy({"name":"xiaoqi"}), //数据格式不符合
    success:function(ret){
    ...
    }
    })

3.9 解析器

  • 解析器:对用户请求体中的数据进行解析,依靠请求头content-type对请求体中的数据进行解析,解析到request.data中。

  • 解析器的使用,

    from rest_framework.parsers import JSONParser,FormParser

    class MyParse(APIView):
    """
    JSONParser:只支持Content-Type:application/json格式的
    FormParser:只支持Content-Type:application/x-www-form-urlencoded格式的
    """
    parser_classes = [JSONParser,FormParser] # 表示符合里面那个解析器的请求头,就让谁去处理
    def post(self,request,*args,**kwargs):
    """
    1.获取用户的请求头
    2.获取用户的请求体
    3.根据用户的请求头,和parser_classes中支持的请求头进行比较
    4.谁支持,就让谁处理请求体中内容
    5.处理完成之后将结果交给request.data
    """
    ret = request.data # 使用request.data取值,内部帮助你反序列化了。不使用它取值,解析器虽然配置上了,但是并没有用
    print(ret)

    return HttpResponse("请求成功了")
  • 全局使用

    from rest_framework.parsers import FileUploadParser
    class MyParse(APIView):
    parser_classes = [FileUploadParser,] # 表示仅上传文件,不支持全局的
    def post(self,request,*args,**kwargs):
    """
    1.获取用户的请求头
    2.获取用户的请求体
    3.根据用户的请求头,和parser_classes中支持的请求头进行比较
    4.谁支持,就让谁处理请求体中内容
    5.处理完成之后将结果交给request.data
    """
    ret = request.data
    print(ret)
    return HttpResponse("请求成功了")

    views.py

    # 在settings.py全局配置
    REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES': [
    'rest_framework.parsers.JSONParser',
    'rest_framework.parsers.FormParser',
    ],
    }

    settings.py

  • 源码解析

    1.先将所有的解析器封装进request对象中,先执行dispatch方法,parsers=self.get_parsers(),封装在Resquest对象中,parsers = parser_classes(我们写的列表)通过列表推导式得到的对象列表。
    2.request.data可以获取到解析后的数据,先执行_load_data_and_files()方法,
    3.执行_parse方法,从请求头中获取到content-type的值赋值给media_type
    4.执行select_parser(self, self.parsers)方法,其中self.parsers就是我们第1部得到的对象列表
    5.循环出parsers中的对象列表,逐一使用media_type_matches方法,让media_type(请求头中的content-type的值)和我们定义的解析器对象parser的media_type类变量做匹配,匹配成功返回对应的解析器对象。
    6.然后执行解析器的parse()方法,里面进行了反序列化。

3.10 序列化器

3.10.1 继承Serializer

  • 简单使用

    from rest_framework import serializers
    class RoleSerializer(serializers.Serializer):
    # 使我们数据库中的字段
    id = serializers.IntegerField()
    title = serializers.CharField()

    class RolesView(APIView):
    def get(self,request,*args,**kwargs):
    obj_set = models.Role.objects.all()
    # json.dumps只能序列化python中的数据类型,queryset是django中的对象,不能直接序列化
    # 需要使用序列化器,queryset中有多条数据,需要添加many参数
    ser = RoleSerializer(instance=obj_set,many=True)
    # ser.data是序列化之后的结果,是一个有序字典

    return Response(ser.data)
  • 含选择字段,外键和多对多关系,自定制内容

    # model类
    from django.db import models

    class User(models.Model):
    usertype_choice = (
    (1,"普通用户"),
    (2,"vip用户"),
    (3,"svip用户"),
    )
    usertype = models.IntegerField(choices=usertype_choice)
    user = models.CharField(max_length=12)
    group = models.ForeignKey("UserGroup",on_delete=models.CASCADE)
    roles = models.ManyToManyField("Role")

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

    class UserGroup(models.Model):
    group = models.CharField(max_length=12)

    models.py

    from rest_framework import serializers
    class UserSerializer(serializers.Serializer):
    user = serializers.CharField() # 普通字段
    # choice选择,可通过get_usertype_display获取对应的选择中文值
    utp = serializers.CharField(source="get_usertype_display")
    # 外键
    gp = serializers.CharField(source="group.group")
    # 多对多字段,SerializerMethodField自定义显示
    rol = serializers.SerializerMethodField()
    def get_rol(self,row):
    role_obj_list = row.roles.all()
    ret = []
    for role_obj in role_obj_list:
    ret.append({"id":role_obj.id,"title":role_obj.title})
    return ret

    class UserView(APIView):
    def get(self,request,*args,**kwargs):
    obj_set = models.User.objects.all()

    ser = UserSerializer(instance=obj_set,many=True)
    print(ser.data,type(ser.data))
    return Response(ser.data)

    views.py

3.10.2 继承ModelSerializer

  • 示例

    # model类
    from django.db import models

    class User(models.Model):
    usertype_choice = (
    (1,"普通用户"),
    (2,"vip用户"),
    (3,"svip用户"),
    )
    usertype = models.IntegerField(choices=usertype_choice)
    user = models.CharField(max_length=12)
    group = models.ForeignKey("UserGroup",on_delete=models.CASCADE)
    roles = models.ManyToManyField("Role")

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

    class UserGroup(models.Model):
    group = models.CharField(max_length=12)

    models.py

    from rest_framework import serializers
    class UserModelSerializer(serializers.ModelSerializer):
    # choice选择,可通过get_usertype_display获取对应的选择中文值
    utp = serializers.CharField(source="get_usertype_display") # 不需要手动加括号,内部自动实现
    # 外键
    gp = serializers.CharField(source="group.group")
    # 多对多字段,SerializerMethodField自定义显示
    rol = serializers.SerializerMethodField()
    def get_rol(self,row): # row就是对应model的对象,函数名必须是get_xxx,xxx是显示的字段名
    role_obj_list = row.roles.all()
    ret = []
    for role_obj in role_obj_list:
    ret.append({"id":role_obj.id,"title":role_obj.title})
    return ret class Meta:
    model = models.User
    # fields = "__all__" # 获取所有字段
    fields = ["id","user","utp","gp","rol"] # 自定制显示字段

    class UserView(APIView):

    def get(self,request,*args,**kwargs):
    obj_set = models.User.objects.all()

    ser = UserModelSerializer(instance=obj_set,many=True)
    print(ser.data,type(ser.data))
    return Response(ser.data)

    views.py

  • 自定制类

    from rest_framework import serializers

    # 自定制类方式,对数据库的字段进行修改显示
    class MyMethod(serializers.CharField):
    def to_representation(self, value):
    # value就是我们去到的user
    return f"叫爸爸,{value}"

    class UserModelSerializer(serializers.ModelSerializer):
    # choice选择,可通过get_usertype_display获取对应的选择中文值
    utp = serializers.CharField(source="get_usertype_display") # 不需要手动加括号,内部自动实现
    # 外键
    gp = serializers.CharField(source="group.group")
    # 多对多字段,SerializerMethodField自定义显示
    rol = serializers.SerializerMethodField()

    def get_rol(self, row): # row就是对应model的对象,函数名必须是get_xxx,xxx是显示的字段名
    role_obj_list = row.roles.all()
    ret = []
    for role_obj in role_obj_list:
    ret.append({"id": role_obj.id, "title": role_obj.title})
    return ret
    # 自定制类的显示
    xxx = MyMethod(source="user")

    class Meta:
    model = models.User
    # fields = "__all__" # 获取所有字段
    fields = ["id", "user", "utp", "gp", "rol","xxx"] # 自定制显示字段

    class UserView(APIView):

    def get(self, request, *args, **kwargs):
    obj_set = models.User.objects.all()

    ser = UserModelSerializer(instance=obj_set, many=True)
    print(ser.data, type(ser.data))
    return Response(ser.data)
  • 自定制字段,添加model之外的字段,必须定义get_字段名()方法,用于返回字段的值

    class PublisherSerializer(serializers.ModelSerializer):
    social_uid = serializers.CharField(source='original_social_uid', read_only=True)
    social_username = serializers.CharField(source='original_social_username', read_only=True)
    promote = serializers.SerializerMethodField() # 自定义model中没有的字段

    class Meta:
    model = Publisher
    fields = ('social_uid', 'social_username', 'promote')
    read_only_fields = fields
    writable_fields = []

    def get_promote(self):
    promote = "hello 我是新增的"
    return promote

3.10.3 自动序列化连表操作

  • 牛逼的几个字符,完成3.10.2功能

    from rest_framework import serializers

    class UserModelSerializer(serializers.ModelSerializer):
    # choice选择,可通过get_usertype_display获取对应的选择中文值
    utp = serializers.CharField(source="get_usertype_display") # 不需要手动加括号,内部自动实现
    class Meta:
    model = models.User
    fields = ["id","user","utp","group","roles"]
    depth = 1 # 深度,即连表查询的深度,官方建议0-10层,不要超过10层,层数越多越慢

    class UserView(APIView):

    def get(self, request, *args, **kwargs):
    obj_set = models.User.objects.all()

    ser = UserModelSerializer(instance=obj_set, many=True)
    print(ser.data, type(ser.data))
    return Response(ser.data)

3.10.4生成链接,一对多/多对多

  • urls.py

    urlpatterns = [
    path('admin/', admin.site.urls), url(r'^userview/$', views.UserView.as_view(),name="user"),
    url(r'^group/(?P<pk>\d+)/$', views.GroupView.as_view(),name="gp"),
    url(r'^role/(?P<pk>\d+)/$', views.RoleView.as_view(),name="rol"),
    ]

    urls.py

  • 视图函数

    class UserModelSerializer(serializers.ModelSerializer):
    # choice选择,可通过get_usertype_display获取对应的选择中文值
    utp = serializers.CharField(source="get_usertype_display") # 不需要手动加括号,内部自动实现
    # 针对group字段,反向生成url,
    # view_name表示反向解析地址的路由别名,
    # lookup_field配置路由的Pk,
    # lookup_url_kwarg:反向解析地址路由中分组的名字
    group = serializers.HyperlinkedIdentityField(view_name="gp",lookup_field="group_id",lookup_url_kwarg="pk")
    # 处理多对多关系,亲测有效
    roles = serializers.HyperlinkedIdentityField(view_name="rol",lookup_field="pk",lookup_url_kwarg="pk",many=True)
    class Meta:
    model = models.User
    fields = ["id","user","utp","group","roles"]
    depth = 0

    class UserView(APIView):

    def get(self, request, *args, **kwargs):
    obj_set = models.User.objects.all()
    # 使用HyperlinkedIdentityField必须要实例化序列化器的时候传入context={'request': request}
    ser = UserModelSerializer(instance=obj_set, many=True,context={'request': request})
    print(ser.data, type(ser.data))
    return Response(ser.data)

    class GroupSerializer(serializers.ModelSerializer):
    class Meta:
    model = models.UserGroup
    fields = "__all__"

    class GroupView(APIView):
    def get(self, request, *args, **kwargs):
    pk = kwargs.get("pk") # 取关键字参数
    obj = models.UserGroup.objects.get(pk=pk)
    ser = GroupSerializer(instance=obj)
    return Response(ser.data) class RoleSerializer(serializers.ModelSerializer):
    class Meta:
    model = models.Role
    fields = "__all__"

    class RoleView(APIView):
    def get(self, request, *args, **kwargs):
    pk = kwargs.get("pk") # 取关键字参数
    obj = models.Role.objects.get(pk=pk)
    ser = RoleSerializer(instance=obj)

    return Response(ser.data)

    views.py

  • 源码流程

    类实例化先执行__new__方法,执行BaseSerializer类的__new__方法,如果参数传了many=True,为Queryset类型
    对象,通过Serializer类处理
    Queryset,通过ListSerializer类处理
    查看源码从ser.data往后看
    1.执行data方法,会执行to_representation方法,这个方法先去我们自己写的序列化器中找,没有向上找。
    2.从继承的Serializer中可以找到。
    3.to_representation中定义了一个有序字典,循环每个字段执行get_attribute方法,字段是通过serializers.CharField()等类的实例化得到的对象,查看CharField类,找到get_attribute方法。
    4.CharField()没有,到父类中去找。
    5.return get_attribute(instance, self.source_attrs),instance使我们传入的对象可通过源码流程确认,source_attrs就是我们写的source通过分隔后得到的列表。
    6.进入get_attribute中循环attrs即source_attrs,比如外键:
    source="group.group"
    source_attrs = ["group","group"]
    7.通过反射去instance即我们查询出的对象中找到group,并赋值给instance,此时instance是group对象,循环后最终instance就是最后的group。
    8.下一步判断能否执行,可执行就加括号执行,不可执行跳过,最后返回instance

3.10.5 请求数据校验

  • 内置校验

    class DepartmentSerializer(serializers.Serializer):
    # error_messages属性可以定义内置验证的错误提示
    depart = serializers.CharField(error_messages={"required":"该字段不能为空"},)

    class DepartmentView(APIView):

    def post(self,request,*args,**kwargs):
    # 解析器解析后的数据放到了request.data中
    ser = DepartmentSerializer(data=request.data)
    if ser.is_valid():
    # validated_data清洗后的数据
    print(ser.validated_data)
    return Response(ser.validated_data)
    else:
    # errors错误字典
    print(ser.errors)
    return Response(ser.errors)
  • 自定义校验器

    class MyValidator(object):
    def __call__(self, value):
    if "健人" in value:
    message = "内容存在敏感字符"
    raise serializers.ValidationError(message)

    class DepartmentSerializer(serializers.Serializer):
    # error_messages属性可以定义内置验证的错误提示
    # validators自定义验证规则,将我们自定的验证规则,填入到列表中
    depart = serializers.CharField(error_messages={"required": "该字段不能为空"}, validators=[MyValidator(),])

    class DepartmentView(APIView):

    def post(self, request, *args, **kwargs):
    # 解析器解析后的数据放到了request.data中
    ser = DepartmentSerializer(data=request.data)
    if ser.is_valid():
    print(ser.validated_data)
    return Response(ser.validated_data)
    else:
    print(ser.errors)
    return Response(ser.errors) """
    如果我们输入{"depart":"健人就是贱人"}
    立马就会提示:
    {
    "depart": [
    "内容存在敏感字符"
    ]
    }
    """
  • 钩子函数

    class DepartmentSerializer(serializers.Serializer):
    # error_messages属性可以定义内置验证的错误提示
    # validators自定义验证规则,将我们自定的验证规则,填入到列表中
    depart = serializers.CharField(error_messages={"required": "该字段不能为空"}, validators=[MyValidator(), ]) # 局部钩子校验
    def validate_depart(self, value):
    print(value, "******")
    if value == "xiaoqi":
    return value
    raise serializers.ValidationError("未通过校验")
    # 全局钩子校验
    def validate(self, attrs):
    print(attrs, "-----------")
    if len(attrs.get("depart")) < 5:
    return attrs
    raise serializers.ValidationError("长度超过限制")

    class DepartmentView(APIView):

    def post(self, request, *args, **kwargs):
    # 解析器解析后的数据放到了request.data中
    ser = DepartmentSerializer(data=request.data)
    if ser.is_valid():
    return Response(ser.validated_data)
    else:
    print(ser.errors)
    return Response(ser.errors)
  • 校验流程

    1.执行is_valid()进行校验
    2.执行序列化器对象的run_validation()方法进行校验,然后执行to_internal_value()方法,先定义错误字典和数据字典。
    3.循环每个字段进行内置校验和自定义校验,执行字段对象的run_validation()方法,执行字段对象中的run_validators(value)方法,进行自定义校验器的校验,循环自定义校验器列表中的每个对象,加括号执行,自动执行__call__方法,即自定义校验器是在__call__方法中进行校验。
    4.validate_method()进行局部钩子校验,通过后
    5.执行序列化器对象的validate(self, attrs)方法,进行全局钩子校验。
    6.最终将通过校验的值赋给validated_data

3.11 分页

3.11.1 看第几页,每页显示n条数据

  • 简单分页

    from rest_framework.pagination import PageNumberPagination

    class DepartmentSerializer(serializers.ModelSerializer):
    class Meta:
    model = models.Department
    fields = "__all__"

    class PageView(APIView):
    def get(self, request, *args, **kwargs):
    obj_list = models.Department.objects.all()
    print(obj_list)
    # 实例化分页对象
    pg = PageNumberPagination()
    # 在数据库中获取分页数据
    pg_obj_list = pg.paginate_queryset(queryset=obj_list, request=request, view=self)
    # 对数据进行序列化
    ser = DepartmentSerializer(instance=pg_obj_list, many=True)

    return Response(ser.data)

    views.py

    # 配置中需要设置每页显示几条数据
    REST_FRAMEWORK = {
    "PAGE_SIZE":2,
    }

    settings.py

  • 自定制内容

    from rest_framework.pagination import PageNumberPagination

    class DepartmentSerializer(serializers.ModelSerializer):
    class Meta:
    model = models.Department
    fields = "__all__"
    class MyPageNumberPagination(PageNumberPagination):
    page_size = 2 # size配置成2,每页显示2条数据
    # 表示还可以传一个size参数,size我们定义的是2,还可以自定制一页显示多少条数据
    page_size_query_param = 'size'
    # 每页最多能够显示数据条数
    max_page_size = 5
    # 就是url页码参数的名字
    page_query_param = 'page'

    class PageView(APIView):
    def get(self, request, *args, **kwargs):
    obj_list = models.Department.objects.all()
    print(obj_list)
    # 实例化分页对象
    mpg = MyPageNumberPagination()
    # 在数据库中获取分页数据
    pg_obj_list = mpg.paginate_queryset(queryset=obj_list, request=request, view=self)
    ser = DepartmentSerializer(instance=pg_obj_list, many=True)
    # 可以给我们返回一个response,样式如下
    ret = mpg.get_paginated_response(ser.data)
    return ret
    # return Response(ser.data)
    # 使用mpg.get_paginated_response(ser.data)样式:可以提供上一页下一页的url,和数据条数
    {
    "count": 10,
    "next": "http://127.0.0.1:8000/depart/?page=3",
    "previous": "http://127.0.0.1:8000/depart/",
    "results": [
    {
    "id": 3,
    "depart": "运维"
    },
    {
    "id": 4,
    "depart": "销售"
    }
    ]
    }

3.11.2 在n的索引位置,向后查看n条数据

  • 分页实现

    from rest_framework.pagination import LimitOffsetPagination

    class DepartmentSerializer(serializers.ModelSerializer):
    class Meta:
    model = models.Department
    fields = "__all__"
    class MyLimitOffsetPagination(LimitOffsetPagination):
    default_limit = 2 # 默认每页显示多少条数据
    offset_query_param = 'offset' # 从索引为几的数据开始查看,0是第一条
    limit_query_param = 'limit' # 可以设定显示条数
    max_limit = 5 # 最大显示多少条

    class PageView(APIView):
    def get(self, request, *args, **kwargs):
    obj_list = models.Department.objects.all()
    print(obj_list)
    # 实例化分页对象
    pg = MyLimitOffsetPagination()
    # 在数据库中获取分页数据
    pg_obj_list = pg.paginate_queryset(queryset=obj_list, request=request, view=self)
    ser = DepartmentSerializer(instance=pg_obj_list, many=True)
    # 可以给我们返回一个response
    # ret = pg.get_paginated_response(ser.data)
    # return ret
    return Response(ser.data)

3.11.3 加密分页,只有上一页下一页

  • 分页实现,这种方式实际上是记住当前页的最大值和最小值,下一页时就按照最大值向后读n条数据,上一页则按照最小值向前读n条数据。

    from rest_framework.pagination import CursorPagination

    class DepartmentSerializer(serializers.ModelSerializer):
    class Meta:
    model = models.Department
    fields = "__all__"
    class MyCursorPagination(CursorPagination):
    # url参数名称
    cursor_query_param = 'cursor'
    page_size = 2 # 一页显示2条数据
    # 排序规则,按照什么排序
    ordering = 'id'
    # 可以设定显示条数,如在url上添加?page_size=4,每页显示4条数据
    page_size_query_param = "page_size"
    # 最多显示多少条数据
    max_page_size = 5

    class PageView(APIView):
    def get(self, request, *args, **kwargs):
    obj_list = models.Department.objects.all()
    print(obj_list)
    # 实例化分页对象
    pg = MyCursorPagination()
    # 在数据库中获取分页数据
    pg_obj_list = pg.paginate_queryset(queryset=obj_list, request=request, view=self)
    ser = DepartmentSerializer(instance=pg_obj_list, many=True)
    # 可以给我们返回一个response,page经过加密了,我们无法直接在url输入页码,只能以这种形式返回
    ret = pg.get_paginated_response(ser.data)
    return ret

3.12 视图

  • GenericAPIView继承APIView,APIView继承View

    GenericAPIView作用实际只是通过内部一些方法,帮我们调用类变量中的配置的数据对象,分页器和序列化器。

    # url(r'^books/(?P<pk>\d+)/$', views.TestView.as_view()),

    from rest_framework.generics import GenericAPIView

    class UserSerializer(serializers.Serializer):
    user = serializers.CharField() # 普通字段
    # choice选择,可通过get_usertype_display获取对应的选择中文值
    utp = serializers.CharField(source="get_usertype_display")
    # 外键
    gp = serializers.CharField(source="group.group")
    # 多对多字段,SerializerMethodField自定义显示
    rol = serializers.SerializerMethodField()

    def get_rol(self, row):
    role_obj_list = row.roles.all()
    ret = []
    for role_obj in role_obj_list:
    ret.append({"id": role_obj.id, "title": role_obj.title})
    return ret

    class TestView(GenericAPIView):
    queryset = UserInfo.objects.all()
    serializer_class = UserSerializer
    pagination_class = PageNumberPagination def get(self, request, pk):
    # 获取数据,get_queryset()方法实际上就是去获取类变量queryset的值
    query_set = self.get_queryset()
    # 分页,获取类变量serializer_class实例化后的对象
    pg = self.paginate_queryset(query_set)
    # 序列化,获取类变量pagination_class实例化后的对象
    ser = self.get_serializer(instance=pg,many=True)
    return Response(ser.data)
  • 在权限位置有一个方法has_object_permission(),只有执行GenericAPIView中的get_object方法是会调用

    # 流程
    GenericAPIView
    1.执行get_object方法
    2.会执行check_object_permissions方法
    3.会循环执行has_object_permission方法,此时使用权限组件的时候必须写has_object_permission方法
  • GenericViewSet(ViewSetMixin, generics.GenericAPIView)

    主要的作用不在GenericAPIView,而是继承的ViewSetMixin,ViewSetMixin的作用主要是重写as_view()方法,可以在路由上自定义绑定方法名。

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

    urlpatterns = [
    # 路由中修改了方法的对应关系,ViewSetMixin的主要作用
    url(r'test/', TestView.as_view({'get':'list'}), name='test'),
    ]

    urls.py

    from rest_framework.response import Response
    from rest_framework.viewsets import GenericViewSet
    from app01 import models

    class UserSerializer(serializers.Serializer):
    user = serializers.CharField() # 普通字段
    # choice选择,可通过get_usertype_display获取对应的选择中文值
    utp = serializers.CharField(source="get_usertype_display")
    # 外键
    gp = serializers.CharField(source="group.group")
    # 多对多字段,SerializerMethodField自定义显示
    rol = serializers.SerializerMethodField()

    def get_rol(self, row):
    role_obj_list = row.roles.all()
    ret = []
    for role_obj in role_obj_list:
    ret.append({"id": role_obj.id, "title": role_obj.title})
    return ret

    class TestView(GenericViewSet):
    queryset = models.UserInfo.objects.all()
    serializer_class = UserSerializer
    pagination_class = PageNumberPagination # 如果有自定义部分可以使用自己自定义的 def list(self, request, *args, **kwargs):
    # 获取数据,get_queryset()方法实际上就是去获取类变量queryset的值
    query_set = self.get_queryset()
    # 分页,获取类变量serializer_class实例化后的对象
    pg = self.paginate_queryset(query_set)
    # 序列化,获取类变量pagination_class实例化后的对象
    ser = self.get_serializer(instance=pg,many=True)

    return Response(ser.data)

    views.py

  • ModelViewSet

    主要作用帮助我们完成增删改(包含局部修改)查功能

    # ModelViewSet的继承关系
    class ModelViewSet(mixins.CreateModelMixin,
    mixins.RetrieveModelMixin,
    mixins.UpdateModelMixin,
    mixins.DestroyModelMixin,
    mixins.ListModelMixin,
    GenericViewSet):
    1.mixins.CreateModelMixin:
    create():帮助我们完成了新增--》post
    2.mixins.RetrieveModelMixin:
    retrieve():帮助我们完成了获取单条数据--》get,需要id
    3.mixins.UpdateModelMixin:
    update():帮助我们完成了全部更新--》put
    partial_update():帮助我们完成了局部更新--》patch
    4.mixins.DestroyModelMixin:
    destroy():帮助我们完成了删除--》delete,需要id
    5.mixins.ListModelMixin:
    list():帮助我们完成了获取多条数据--》get
    6.GenericViewSet

    ModelViewSet自定义url

    # urls.py
    from django.conf.urls import url, include
    from app01 import views

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

    urls.py

    # views.py
    from rest_framework.viewsets import ModelViewSet
    from rest_framework.pagination import PageNumberPagination
    from rest_framework import serializers
    from app01 import models

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

    class UserViewSet(ModelViewSet):
    queryset = models.UserInfo.objects.all()
    serializer_class = UserSerializer
    pagination_class = PageNumberPagination

    views.py

  • 总结

    1.只完成增删改查:ModelViewSet
    2.增删:CreateModelMixin,DestroyModelMixin,GenericViewSet
    3.复杂逻辑:GenericViewSet,APIView

3.13 路由

  • 自定义路由系统

    from django.conf.urls import url, include
    from app01 import views
    # 4中路由方式
    urlpatterns = [
    url(r'^test/$', views.TestView.as_view()),
    url(r'^test\.(?P<format>[a-z0-9]+)$', views.TestView.as_view()),
    url(r'^test/(?P<pk>[^/.]+)/$', views.TestView.as_view()),
    url(r'^test/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)$', views.TestView.as_view())
    ]
  • 半自定义路由系统

    # urls.py
    from django.conf.urls import url, include
    from app01 import views

    urlpatterns = [
    url(r'^test/$', views.UserViewSet.as_view({'get': 'list', 'post': 'create'})),
    url(r'^test/(?P<pk>\d+)/$', views.UserViewSet.as_view(
    {'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'})),
    ]
  • 自动生成路由

    from django.conf.urls import url, include
    from rest_framework import routers
    from app01 import views


    router = routers.DefaultRouter()
    router.register(r'users', views.UserViewSet)

    urlpatterns = [
    url(r'^', include(router.urls)),
    ]

    urls.py

    # views.py
    from rest_framework.viewsets import ModelViewSet
    from rest_framework.pagination import PageNumberPagination
    from rest_framework import serializers
    from app01 import models

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

    class UserViewSet(ModelViewSet):
    queryset = models.UserInfo.objects.all()
    serializer_class = UserSerializer
    pagination_class = PageNumberPagination

3.14 渲染器

根据 用户请求URL 或 用户可接受的类型,筛选出合适的 渲染组件。 用户请求URL,可通过url变化来选择:

用户请求头:

  • Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8

a. json格式返回数据

访问URL:

from django.conf.urls import url, include
from app01 import views

urlpatterns = [
url(r'^test/$', views.TestView.as_view()),
url(r'^test\.(?P<format>[a-z0-9]+)', views.TestView.as_view()),
]

urls.py

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework.renderers import JSONRenderer
from .. import models

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

class TestView(APIView):
# json格式
renderer_classes = [JSONRenderer, ]

def get(self, request, *args, **kwargs):
user_list = models.UserInfo.objects.all()
ser = TestSerializer(instance=user_list, many=True)
return Response(ser.data)

views.py

b. 表格形式返回数据

访问URL:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework.renderers import AdminRenderer
from .. import models

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

class TestView(APIView):
# 显示表格形式
renderer_classes = [AdminRenderer, ]

def get(self, request, *args, **kwargs):
user_list = models.UserInfo.objects.all()
ser = TestSerializer(instance=user_list, many=True)
return Response(ser.data)

c. Form表单形式返回数据

访问URL:

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 AdminRenderer
from rest_framework.renderers import HTMLFormRenderer
from .. import models

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

class TestView(APIView):
# form表单形式
renderer_classes = [HTMLFormRenderer, ]

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

d. 自定义显示模板

访问URL:

# urls.py
from django.conf.urls import url, include
from app01 import views

urlpatterns = [
url(r'^test/$', views.TestView.as_view()),
url(r'^test\.(?P<format>[a-z0-9]+)', views.TestView.as_view()),
]

urls.py

# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework.renderers import TemplateHTMLRenderer
from .. import models

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

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

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='custom.html')

views.py

<!--custom.html-->

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{{ user }}
{{ pwd }}
{{ ut }}
</body>
</html>

html

e. 浏览器格式API+JSON

访问URL:

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')

注意:如果同时多个存在时,自动根据URL后缀来选择渲染器。

参考:http://www.cnblogs.com/wupeiqi/

Django Rest Framewoek的更多相关文章

  1. 异步任务队列Celery在Django中的使用

    前段时间在Django Web平台开发中,碰到一些请求执行的任务时间较长(几分钟),为了加快用户的响应时间,因此决定采用异步任务的方式在后台执行这些任务.在同事的指引下接触了Celery这个异步任务队 ...

  2. 《Django By Example》第四章 中文 翻译 (个人学习,渣翻)

    书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:祝大家新年快乐,这次带来<D ...

  3. django server之间通过remote user 相互调用

    首先,场景是这样的:存在两个django web应用,并且两个应用存在一定的联系.某些情况下彼此需要获取对方的数据. 但是我们的应用肯经都会有对应的鉴权机制.不会让人家随随便便就访问的对吧.好比上车要 ...

  4. Mysql事务探索及其在Django中的实践(二)

    继上一篇<Mysql事务探索及其在Django中的实践(一)>交代完问题的背景和Mysql事务基础后,这一篇主要想介绍一下事务在Django中的使用以及实际应用给我们带来的效率提升. 首先 ...

  5. Mysql事务探索及其在Django中的实践(一)

    前言 很早就有想开始写博客的想法,一方面是对自己近期所学知识的一些总结.沉淀,方便以后对过去的知识进行梳理.追溯,一方面也希望能通过博客来认识更多相同技术圈的朋友.所幸近期通过了博客园的申请,那么今天 ...

  6. 《Django By Example》第三章 中文 翻译 (个人学习,渣翻)

    书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:第三章滚烫出炉,大家请不要吐槽文中 ...

  7. 《Django By Example》第二章 中文 翻译 (个人学习,渣翻)

    书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:翻译完第一章后,发现翻译第二章的速 ...

  8. 《Django By Example》第一章 中文 翻译 (个人学习,渣翻)

    书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:本人目前在杭州某家互联网公司工作, ...

  9. Django

    一.Django 简介 Django 是一个由 Python 写成的开放源代码的 Web 应用框架.它最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内容为主的网站的,即是 CMS(内容管理系统) ...

随机推荐

  1. 2018-8-10-win10-uwp-读写XML

    title author date CreateTime categories win10 uwp 读写XML lindexi 2018-08-10 19:16:51 +0800 2018-2-13 ...

  2. openstack stein部署手册 9. neutron

    # 安装程序包 yum -y install openstack-neutron-linuxbridge ebtables ipset # 变更配置文件 mv /etc/neutron/neutron ...

  3. basename 显示文件名或目录名

    1. 命令功能 basename 显示文件名或目录名,不显示文件的全路径文件名 2. 语法格式 basename  文件路径名 3. 使用范例 [root@localhost data]# basen ...

  4. Java 逻辑运算符相关解析

    问:定简单说说 Java 中 & 与 && 有什么区别?| 与 || 呢? 答:& 是位运算符,&& 是布尔逻辑运算符,| 与 || 类似同理.在进行逻 ...

  5. 程序员要注意!现在是RSS复兴的时候了

    一般来说,现代网络不乏恐怖,从无所不在的网络黑客到所有信息平台,再到各大平台的评论系统.不幸的是,我们建立的这个互联网并没有什么灵丹妙药.但任何人都厌倦了黑箱算法,控制你在网上看到的东西,一直存在但始 ...

  6. django之子应用中开发视图函数

    一:修改视图函数 报错:ImportError: No module named 'django' 原因是:pycharm中的解释器未选择虚拟环境里面的python3 from django.shor ...

  7. element-ui 里面el-checkbox多选框,实现全选单选

    data里面定义了 data:[],        actionids:[],//选择的那个actionid        num1:0,//没选择的计数        num2:0,//选中的计数  ...

  8. linux运维、架构之路-Nginx提高

    一.虚拟主机搭建 1.基于域名的虚拟主机 [root@web01 html]# cat nginx.conf worker_processes ; events { worker_connection ...

  9. 5 October

    POJ2676 Sudoku 位运算 + 搜索.更好的优化方法:方案数最小的空格先填. 把某一位 置为 0:a &=~ (1<<n) 把某一位 置为 1:a |= (1<&l ...

  10. leetcode 189. 旋转数组(python)

    给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数. 示例 1: 输入: [1,2,3,4,5,6,7] 和 k = 3输出: [5,6,7,1,2,3,4]解释:向右旋转 1 步: ...