Django_rest_framework

什么是restful

  • REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”
  • REST从资源的角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识,客户端应用通过URL来获取资源的表征,获得这些表征致使这些应用转变状态
  • 所有的数据,不过是通过网络获取的还是操作(增删改查)的数据,都是资源,将一切数据视为资源是REST区别与其他架构风格的最本质属性
  • 对于REST这种面向资源的架构风格,有人提出一种全新的结构理念,即:面向资源架构(ROA:Resource Oriented Architecture)

什么是API

1、什么是API?

答:API就是接口,提供的url。接口有两个用途:

  • - 为别人提供服务
  • - 前后端分离,一个写vue,一个写后端,他们之间都是通过ajax请求

restful API设计规范

  • API与用户的通信协议,总是使用HTTPS协议。
  • 域名 
    • https://api.example.com                         尽量将API部署在专用域名(会存在跨域问题)
    • https://example.org/api/                        API很简单
  • 版本
    • URL,如:https://api.example.com/v1/
    • 请求头                                                  跨域时,引发发送多次请求
  • 路径,视网络上任何东西都是资源,均使用名词表示(可复数)
    • https://api.example.com/v1/zoos
    • https://api.example.com/v1/animals
    • https://api.example.com/v1/employees
  • method
    • GET      :从服务器取出资源(一项或多项)
    • POST    :在服务器新建一个资源
    • PUT      :在服务器更新资源(客户端提供改变后的完整资源)
    • PATCH  :在服务器更新资源(客户端提供改变的属性)
    • DELETE :从服务器删除资源
  • 过滤,通过在url上传参的形式传递搜索条件
    • https://api.example.com/v1/zoos?limit=10:指定返回记录的数量
    • https://api.example.com/v1/zoos?offset=10:指定返回记录的开始位置
    • https://api.example.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数
    • https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序
    • https://api.example.com/v1/zoos?animal_type_id=1:指定筛选条件
  • 状态码
200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
204 NO CONTENT - [DELETE]:用户删除数据成功。
400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
View Code
  • 错误处理,状态码是4xx时,应返回错误信息,error当做key。
    {
        error: "Invalid API key"
    }
  • 返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范。
    GET /collection:返回资源对象的列表(数组)
    GET /collection/resource:返回单个资源对象
    POST /collection:返回新生成的资源对象
    PUT /collection/resource:返回完整的资源对象
    PATCH /collection/resource:返回完整的资源对象
    DELETE /collection/resource:返回一个空文档
  • Hypermedia API,RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。

基于Django实现restful api

参考博客:https://www.cnblogs.com/wusir66/p/10016584.html

基于Django Rest Framework框架实现

PS:以下介绍的使用方式都是一些常用的,还有一些方式使用较少,在此不做介绍。

一、自定义用户认证

  在models.py的UserInfo表中创建一些数据

from django.db import models

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

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

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

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

#利用token值来进行认证
class OrderView(APIView):
    def get(self,request,*args,**kwargs):
        ret = {'code':1000,'msg':None,'data':None}
        order = {'goods':'food'}
        try:
            ret['data'] = order
        except Exception as e:
            ret['msg'] = '有问题'
        return JsonResponse(ret)
view.py

  在app目录下创建一个rest_utils的文件夹

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

class MyAuthentication(BaseAuthentication):
    def authenticate(self, request):
        token = request._request.GET.get('token')
        obj = UserToken.objects.filter(token=token).first()
        if not obj:
            raise exceptions.AuthenticationFailed('用户未认证')
        return (obj.user,obj)
    def authenticate_header(self, request):
        pass
auth.py(具体认证代码)

  配置文件

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

如果有某些类不需要使用认证,可以在类中加上以下代码

authentication_classes = []

源码大致流程

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

        try:
            #第二步:
                #处理版权信息
                #认证
                #权限
                #请求用户进行访问频率的限制
            self.initial(request, *args, **kwargs)

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

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

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

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

源码具体流程

  请求来了先走dispatch方法做分发

1、对request进行加工(添加数据)

a、首先  request = self.initialize_request(request, *args, **kwargs)点进去,会发现:在Request里面多加了四个,如下

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

        return Request(
            request,
            parsers=self.get_parsers(),  #解析数据,默认的有三种方式,可点进去看
            #self.get_authenticator优先找自己的,没有就找父类的
            authenticators=self.get_authenticators(), #获取认证相关的所有类并实例化,传入request对象供Request使用
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )
View Code

b、获取认证相关的类的具体   authenticators=self.get_authenticators()

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

c、查看认证的类:self.authentication_classes

authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES  #默认的,如果自己有会优先执行自己的
View Code

d、接着走进api_settings

api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)  #点击继承的DEFAULTS类


DEFAULTS类
DEFAULTS = {
    # Base API policies
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.SessionAuthentication',   #这时候就找到了他默认认证的类了,可以导入看看
        'rest_framework.authentication.BasicAuthentication'
    ),
View Code

e、导入了类看看类里面具体干了什么

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

f、看到里面有个authenticate方法和authenticate_header方法

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

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

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

具体处理认证,从headers里面能获取用户名和密码

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

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

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

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

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

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

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

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

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

        return (user, None)

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

g、当然restfulframework默认定义了两个类。我们也可以自定制类,自己有就用自己的了,自己没有就去找父类的了,但是里面必须实现authenticate方法,不然会报错。

2、加工完request之后的操作

  • 处理版权信息
  • 认证
  • 权限
  • 请求用户进行访问频率的限制

认证流程

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

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

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

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

        # Ensure that the incoming request is permitted
        #2.2 认证
        self.perform_authentication(request)
        # 2.3 权限
        self.check_permissions(request)
        # 2.4 请求用户进行访问频率的限制
        self.check_throttles(request)
View Code

 b、我们先来看认证,self.perform_authentication(request) 具体干了什么,按住ctrl点击进去

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

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

c、那么我们可以从视图里面导入一下Request,找到request对象的user方法

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

d、执行self._authenticate() 开始用户认证,如果验证成功后返回元组: (用户,用户Token)

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

            if user_auth_tuple is not None:
                self._authenticator = authenticator
                self.user, self.auth = user_auth_tuple  #返回一个元组,user,和auth,赋给了self,
                # 只要实例化Request,就会有一个request对象,就可以request.user,request.auth了
                return

        self._not_authenticated()
View Code

e、在user_auth_tuple = authenticator.authenticate(self) 进行验证,如果验证成功,执行类里的authenticatie方法 

f、如果用户没有认证成功:self._not_authenticated()

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

        Defaults are None, AnonymousUser & None.
        """
        #如果跳过了所有认证,默认用户和Token和使用配置文件进行设置
        self._authenticator = None  #

        if api_settings.UNAUTHENTICATED_USER:
            self.user = api_settings.UNAUTHENTICATED_USER() # 默认值为:匿名用户AnonymousUser
        else:
            self.user = None  # None 表示跳过该认证

        if api_settings.UNAUTHENTICATED_TOKEN:
            self.auth = api_settings.UNAUTHENTICATED_TOKEN()  # 默认值为:None
        else:
            self.auth = None

    # (user, token)
    # 表示验证通过并设置用户名和Token;
    # AuthenticationFailed异常
View Code

3、执行get/post/delete等方法

4、对返回结果在进行加工

二、自定义权限

  在app目录下创建一个rest_utils的文件夹

from rest_framework.permissions import BasePermission

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

  配置文件

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

  如果有某些类不需要使用权限,可以在类中加上以下代码

    permission_classes = []

三、自定义节流

   在app目录下创建一个rest_utils的文件夹

from rest_framework.throttling import BaseThrottle

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

    def __init__(self):
        self.history = None

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

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

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

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

        # return True    # 表示可以继续访问
        # return False # 表示访问频率太高,被限制

    def wait(self):
        # 还需要等多少秒才能访问
        ctime = time.time()
        return 60 - (ctime - self.history[-1])
throttle.py

  配置文件

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

  如果有某些类不需要使用节流,可以在类中加上以下代码

throttle_classes = []

四、版本

  配置文件

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

  路由

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

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

  视图

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

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

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

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

五、解析器

  配置文件

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

}
settings.py

  视图函数中可以直接通过request.data拿到解析之后的数据

六、序列化

序列化共有两个功能:1、数据序列化  2、请求数据校验

  数据序列化

from django.db import models

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


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

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


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


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

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

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

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

        # 方式三 ,序列化单个数据 [obj]
        res = Role.objects.all().first()
        ser = RolesSerializers(instance=res,many=False)
        res = json.dumps(ser.data,ensure_ascii=False)
        return HttpResponse(res)
# =========================================

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

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

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

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

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

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

# GroupSerializers和GroupViews配合自定义序列化类五生成Hypermedia API后,点击url可以看详情
class GroupSerializers(serializers.ModelSerializer):
    class Meta:
        model = UserGroup
        fields = '__all__'

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

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

  请求数据校验

class XXValidator(object):
    def __init__(self, base):
        self.base = base
    def __call__(self, value):
        if not value.startswith(self.base):
            message = '标题必须以 %s 为开头。' % self.base
            raise serializers.ValidationError(message)
    def set_context(self, serializer_field):
        pass


class UserGroupSerializer(serializers.Serializer):
    title = serializers.CharField(error_messages={'required': '标题不能为空'},validators=[XXValidator('wusir'),])
    
# required为错误时的提示信息,validators为自定义验证器,可以自定义功能,也可以不写,不写的话只能校验数据是否为空

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

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

七、分页

PS:先自定义一个序列化,然后在分页程序中导入这个序列化程序,所有的原生分页需要现在settings.py中定义一下每页显示几条数据

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

A1、分页,看第n页,每页显示n条数据(原生)

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


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

A2、分页,看第n页,每页显示n条数据(自定义)

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


class MyPageNumberPagination(PageNumberPagination):
    page_size = 3    #默认每页显示个数
    page_query_param = 'page'    #get传参表示第几页
    page_size_query_param = 'pagesize'    #get传参表示每页显示几个
    max_page_size = 5   #每页最大显示个数


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

B1. 分页,在n个位置,向后查看n条数据(原生)

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


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

B2. 分页,在n个位置,向后查看n条数据(自定义)

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



class MyLimitOffsetPagination(LimitOffsetPagination):
    default_limit = 3   #默认每页显示个数
    limit_query_param = 'limit'    #get传参表示每页显示个数
    offset_query_param = 'offset'  #get传参表示跳过几个数据显示
    max_limit = 6       #每页最大显示个数

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

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

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

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

C2.加密分页,上一页和下一页(自定义)

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


class MyCursorPagination(CursorPagination):
    cursor_query_param = 'wusir'  #get参数中确定以什么值为key来接受下一页的参数
    page_size = 3   #默认每页显示数目
    ordering = 'id'  #根据什么字段来进行排序
    page_size_query_param = 'pagesize'   #get传参表示每页显示几个
    max_page_size = 5   #每页最大显示数目


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

八、视图

  PS:View和APIView在此处就不多介绍

  GenericAPIView

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

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

  GenericViewSet(as_view里面可以用字典的方式让get,post等不同的方法对应执行不同名称的函数)

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

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

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

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

(任意举两个栗子)

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

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

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

  ModelViewSet

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

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


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

继承关系:APIView继承了View,GenericAPIView继承了APIView,GenericViewSet继承了GenericAPIView和ViewSetMixin,ModelViewSet继承了mixins.CreateModelMixin,mixins.RetrieveModelMixin,mixins.UpdateModelMixin,mixins.DestroyModelMixin,mixins.ListModelMixin和GenericViewSet。

九、路由系统

  当继承了ModelViewSet之后,一个视图对于的完整的路由就有以下四种

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

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

  这时候我们可以使用rest_framework给我们自动生成路由的方式来生成以上四种路由,效果相同

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

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

十、渲染器

  只需要在配置文件中配置一下所需要的渲染器类型就可以了

REST_FRAMEWORK = {
    "DEFAULT_RENDERER_CLASSES":[
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    ]
}
settings.py
posted @ 2018-12-26 11:03  WuSir_ZJ  阅读(261)  评论(0编辑  收藏  举报