zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    vue项目搭建过程2 -- 使用 vue cli 4.0 搭建 vue 项目
    vue项目搭建过程1 -- 环境搭建
    升级node.js版本
    git的初步了解
    期末总结
    四则运算的封装
    用户故事
    0~10的随机整数运算
    创业近一年在博客园总结一下,希望给来者一点借鉴
    PV与并发之间换算的算法换算公式
  • 原文地址:https://www.cnblogs.com/wusir66/p/10137124.html
Copyright © 2011-2022 走看看