其他分享
首页 > 其他分享> > 1.3 django restframework(上)

1.3 django restframework(上)

作者:互联网

1.3 django restframework(上)

django restframework(简称drf)本质上其实就是一个别人编写好的app,里面集成了很多编写restful API的功能功能,接下里咱们就来学习drf并用他来开发restful API。

drf内置了很多便捷的功能,在接下来的课程中会给大家依次讲解下面的内容:

 

1. 快速上手

 

其实drf框架是在django基础进行的扩展,所以上述执行过得底层实现流程(同django的CBV):

 

 

 

drf中重写了 as_viewdispatch方法,其实就是在原来django的功能基础上添加了一些功能,例如:

以前的token认证方法:(csrf 在post请求时要认证)

加了csrf后请求会默认将token值加上的:

 

 

drf token认证:(免除csrf认证)

2. 请求数据的封装

以前我们通过django开发项目时,视图中的request是 django.core.handlers.wsgi.WSGIRequest 类的对象,其中包含了请求相关的所有数据。

# Django FBV
def index(request):
request.method
request.POST
request.GET
request.body

# Django CBV
from django.views import View
class UserView(View):
def get(self,request):
       request.method
       request.POST
       request.GET
       request.body

 

而在使用drf框架时,视图中的request是rest_framework.request.Request类的对象,其是又对django的request进行了一次封装,包含了除django原request对象以外,还包含其他后期会使用的其他对象。

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


class UserView(APIView):
   def get(self, request, *args, **kwargs):
       # request,不再是django中的request,而是又被封装了一层,内部包含:django的request、认证、解析器等。
       return Response({"code": 1000, "data": "xxx"})

   def post(self, request, *args, **kwargs):
       return Response({"code": 1000, "data": "xxx"})
对象 = (request, 其他数据)
# rest_framework.request.Request 类

# 获取原django的request
# request = Request(django的request,)
# request._request.POST
# request._request.GET

class Request: # 封装
   """
  Wrapper allowing to enhance a standard `HttpRequest` instance.
  Kwargs:
      - request(HttpRequest). The original request instance. (django中的request)
      - parsers(list/tuple). The parsers to use for parsing the
        request content.
      - authenticators(list/tuple). The authenticators used to try
        authenticating the request's user.
  """

   def __init__(self, request, parsers=None, authenticators=None,negotiator=None, parser_context=None):
  self._request = request
       self.parsers = parsers or ()
       self.authenticators = authenticators or ()
       ...

   @property
   def query_params(self):
       """
      More semantically correct name for request.GET.
      """
       return self._request.GET

   @property
   def data(self):
       if not _hasattr(self, '_full_data'):
           self._load_data_and_files()
       return self._full_data
   
def __getattr__(self, attr):
       try:
           return getattr(self._request, attr) # self._request.method
       except AttributeError:
           return self.__getattribute__(attr)

所以,在使用drf框架开发时,视图中的request对象与原来的有些不同,例如:

from rest_framework.views import APIView
from rest_framework.response import Response
from django.views import View
from rest_framework.request import Request


class UserView(APIView):
   def get(self, request, *args, **kwargs):
       
       # 通过对象的嵌套直接找到原request,读取相关值
       request._request.method
       request._request.GET
       request._request.POST
       request._request.body
       
       # 举例(django数据处理):
      content-type: url-form-encoded  --请求头
      v1=123&v2=456&v3=999
          # django一旦读取到这个请求头之后,就会按照 {"v1":123,"v2":456,"v3":999}
           
           content-type: application/json --请求头
          {"v1":123,"v2":456}
           request._request.POST
           request._request.body
       # drf是对原来的获取请求方式的补充
       # 直接读取新request对象中的值,一般此处会对原始的数据进行一些处理,方便开发者在视图中使用。
       request.query_params  # 读get请求方式的 用这个内部本质上就是 request._request.GET
       request.data # 读post和其他方式的时候用这种drf 内部读取请求体中的数据,并进行处理,例如:请求者发来JSON格式,他的内部会对json字符串进行反序列化。
       
       # 通过 __getattr__ 去访问 request._request 中的值
       request.method
       
       

 

底层源码实现:

 

 

 

3. 版本管理

在restful规范中要去,后端的API中需要体现版本。

drf框架中支持5种版本的设置。常见就是前两种

3.1 URL的GET参数传递(*)来传递版本执行流程

 

 

# settings.py

REST_FRAMEWORK = {
"VERSION_PARAM": "v", # 这个配置是说在get请求传入版本的时候用 v=v1
    "DEFAULT_VERSION": "v1", # 默认值传v1
"ALLOWED_VERSIONS": ["v1", "v2", "v3"], # 限制版本
"DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.QueryParameterVersioning" # 这个配置就是默认版本路径 不用在加上图中左边红色方框那句代码了(所有的视图类都需要版本的话配置一下方便)
}

源码执行流程:

 

 

读取配置文件类路径:"DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.QueryParameterVersioning"

 

 

3.2 URL路径传递(*)

3.3 请求头传递

 

 

3.4 二级域名传递

 

在使用二级域名这种模式时需要先做两个配置:

 

3.5 路由的namespace传递

 

 

 

以上就是drf中支持的5种版本管理的类的使用和配置。

 

 

全局配置

上述示例中,如果想要应用某种 版本 的形式,需要在每个视图类中定义类变量:

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


class UserView(APIView):
   versioning_class = QueryParameterVersioning
   ...

如果你项目比较大,需要些很多的视图类,在每一个类中都写一遍会比较麻烦,所有drf中也支持了全局配置。

# settings.py

REST_FRAMEWORK = {
   "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.QueryParameterVersioning",  # 处理版本的类的路径
   "VERSION_PARAM": "version",  # URL参数传参时的key,例如:xxxx?version=v1
   "ALLOWED_VERSIONS": ["v1", "v2", "v3"],  # 限制支持的版本,None表示无限制
   "DEFAULT_VERSION": "v1",  # 默认版本
}

 

 

访问URL:

http://127.0.0.1:8000/api/users/?version=v1
http://127.0.0.1:8000/api/users/?version=v2
http://127.0.0.1:8000/api/users/?version=v3

http://127.0.0.1:8000/api/admin/?version=v1
http://127.0.0.1:8000/api/admin/?version=v2
http://127.0.0.1:8000/api/admin/?version=v3

http://127.0.0.1:8000/api/v1/order/
http://127.0.0.1:8000/api/v2/order/
http://127.0.0.1:8000/api/v3/order/

 

底层源码实现

 

 

 

反向生成URL

在每个版本处理的类中还定义了reverse方法,他是用来反向生成URL并携带相关的的版本信息用的,例如:

 

 

 

 

 

小结

以后使用drf开发后端API接口时:

  1. 创建django程序

  2. 安装drf框架

  3. 创建一个app专门来处理用户的请求

  4. 注册APP

  5. 设置版本

  6. 编写视图类

 

4. 认证

 

 

在开发后端的API时,不同的功能会有不同的限制,例如:

 

在drf中也给我们提供了 认证组件 ,帮助我们快速实现认证相关的功能,例如:

# models.py

from django.db import models

class UserInfo(models.Model):
   username = models.CharField(verbose_name="用户名", max_length=32)
   password = models.CharField(verbose_name="密码", max_length=64)
   token = models.CharField(verbose_name="TOKEN", max_length=64, null=True, blank=True)

 

 

 

 

 

 

在视图类中设置类变量 authentication_classes的值为 认证类 MyAuthentication,表示此视图在执行内部功能之前需要先经过 认证。

认证类的内部就是去执行:authenticate方法,根据返回值来表示认证结果。

 

关于 ”返回None“

接下来说说 “返回None” 是咋回事。

在视图类的 authentication_classes 中定义认证类时,传入的是一个列表,支持定义多个认证类。

当出现多个认证类时,drf内部会按照列表的顺序,逐一执行认证类的 authenticate 方法,如果 返回元组 或 抛出异常 则会终止后续认证类的执行;如果返回None,则意味着继续执行后续的认证类。

如果所有的认证类authenticate都返回了None,则默认 request.user="AnonymousUser" 和 request.auth=None,也可以通过修改配置文件来修改默认值。

REST_FRAMEWORK = {
"UNAUTHENTICATED_USER": lambda: None,
"UNAUTHENTICATED_TOKEN": lambda: None,
}

 

”返回None“的应用场景:

当某个API,已认证 和 未认证 的用户都可以方法时,比如:

  • 已认证用户,访问API返回该用户的视频播放记录列表。

  • 未认证用户,访问API返回最新的的视频列表。

注意:不同于之前的案例,之前案例是:必须认证成功后才能访问,而此案例则是已认证和未认证均可访问。

 

 

 

 

 

关于多个认证类

一般情况下,编写一个认证类足矣。

当项目中可能存在多种认证方式时,就可以写多个认证类。例如,项目认证支持:

此时,就可以编写多个认证类,并按照需要应用在相应的视图中,例如:

 

 

 

注意:此示例后续在视图中读取的 request.user 的值为None时,表示未认证成功;不为None时,则表示认证成功。

 

全局配置

在每个视图类的类变量 authentication_classes 中可以定义,其实在配置文件中也可以进行全局配置,例如:

REST_FRAMEWORK = {
  "UNAUTHENTICATED_USER": lambda: None,
  "UNAUTHENTICATED_TOKEN": lambda: None,
  "DEFAULT_AUTHENTICATION_CLASSES":["xxxx.xxxx.xx.类名","xxxx.xxxx.xx.类名",]
}

 

底层源码实现:

 

 

 

5. 权限

认证,根据用户携带的 token/其他 获取当前用户信息。

权限,读取认证中获取的用户信息,判断当前用户是否有权限访问,例如:普通用户、管理员、超级用户,不同用户具有不同的权限。

class UserInfo(models.Model):
   
   role_choices = ((1, "普通用户"), (2, "管理员"), (3, "超级管理员"),)
   role = models.IntegerField(verbose_name="角色", choices=role_choices, default=1)
   
   username = models.CharField(verbose_name="用户名", max_length=32)
   password = models.CharField(verbose_name="密码", max_length=64)
   token = models.CharField(verbose_name="TOKEN", max_length=64, null=True, blank=True)

 

 

import uuid
from rest_framework.views import APIView
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework.permissions import BasePermission
from rest_framework.exceptions import AuthenticationFailed

from app01 import models


class AuthView(APIView):
   """ 用户登录认证 """
   authentication_classes = []
   permission_classes = []

   def post(self, request, *args, **kwargs):
       print(request.data)  # {"username": "wupeiqi", "password": "123"}
       username = request.data.get('username')
       password = request.data.get('password')

       user_object = models.UserInfo.objects.filter(username=username, password=password).first()
       if not user_object:
           return Response({"code": 1000, "data": "用户名或密码错误"})

       token = str(uuid.uuid4())

       user_object.token = token
       user_object.save()

       return Response({"code": 0, "data": {"token": token, "name": username}})


class TokenAuthentication(BaseAuthentication):
   # 认证组件
   def authenticate(self, request):
       token = request.query_params.get("token")
       if not token:
           raise AuthenticationFailed({"code": 1002, "data": "认证失败"})
       user_object = models.UserInfo.objects.filter(token=token).first()
       if not user_object:
           raise AuthenticationFailed({"code": 1002, "data": "认证失败"})
       return user_object, token

   def authenticate_header(self, request):
       return 'Bearer realm="API"'


class PermissionA(BasePermission):
   # 权限组件 自定义一个类继承BasePermission
   message = {"code": 1003, 'data': "无权访问"} # 无权返回的信息
# 固定写法has_permission 判定当前用户是否有权限
   def has_permission(self, request, view):
       if request.user.role == 2: # 角色1代表普通用户2代表管理员
           return True
       return False

   # 暂时先这么写
   def has_object_permission(self, request, view, obj):
       return True


class OrderView(APIView):
   # 此视图要用的认证
   authentication_classes = [TokenAuthentication, ]
# 此视图应用这个权限
   permission_classes = [PermissionA,]

   def get(self, request, *args, **kwargs):
       print(request.user)
       return Response({"code": 0, "data": {"user": None, 'list': [1, 2, 3]}})


class PayView(APIView):
   authentication_classes = [TokenAuthentication, ]
   permission_classes = [PermissionA, ]

   def get(self, request, *args, **kwargs):
       print(request.user)
       return Response({"code": 0, "data": "数据..."})

 

 

 

关于多个权限类

当开发过程中需要用户同时具备多个权限(缺一不可)时,可以用多个权限类来实现。

权限组件内部处理机制:按照列表的顺序逐一执行 has_permission 方法,如果返回True,则继续执行后续的权限类;如果返回None或False,则抛出权限异常并停止后续权限类的执行。

# models.py

from django.db import models


class Role(models.Model):
   """ 角色表 """
   title = models.CharField(verbose_name="名称", max_length=32)


class UserInfo(models.Model):
   """ 用户表 """
   username = models.CharField(verbose_name="用户名", max_length=32)
   password = models.CharField(verbose_name="密码", max_length=64)
   token = models.CharField(verbose_name="TOKEN", max_length=64, null=True, blank=True)

   roles = models.ManyToManyField(verbose_name="角色", to="Role")

 

 

# urls.py

from django.urls import path, re_path, include
from app01 import views

urlpatterns = [
   path('api/auth/', views.AuthView.as_view()),
   path('api/order/', views.OrderView.as_view()),
]

 

# views.py

import uuid
from rest_framework.views import APIView
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework.permissions import BasePermission
from rest_framework.exceptions import AuthenticationFailed

from app01 import models


class AuthView(APIView):
   """ 用户登录认证 """

   def post(self, request, *args, **kwargs):
       print(request.data)  # {"username": "wupeiqi", "password": "123"}
       username = request.data.get('username')
       password = request.data.get('password')

       user_object = models.UserInfo.objects.filter(username=username, password=password).first()
       if not user_object:
           return Response({"code": 1000, "data": "用户名或密码错误"})

       token = str(uuid.uuid4())

       user_object.token = token
       user_object.save()

       return Response({"code": 0, "data": {"token": token, "name": username}})


class TokenAuthentication(BaseAuthentication):
   def authenticate(self, request):
       token = request.query_params.get("token")
       if not token:
           raise AuthenticationFailed({"code": 1002, "data": "认证失败"})
       user_object = models.UserInfo.objects.filter(token=token).first()
       if not user_object:
           raise AuthenticationFailed({"code": 1002, "data": "认证失败"})
       return user_object, token

   def authenticate_header(self, request):
       return 'Bearer realm="API"'


class PermissionA(BasePermission):
   message = {"code": 1003, 'data': "无权访问"}

   def has_permission(self, request, view):
       exists = request.user.roles.filter(title="员工").exists()
       if exists:
           return True
       return False

   def has_object_permission(self, request, view, obj):
       return True


class PermissionB(BasePermission):
   message = {"code": 1003, 'data': "无权访问"}

   def has_permission(self, request, view):
       exists = request.user.roles.filter(title="主管").exists()
       if exists:
           return True
       return False

   def has_object_permission(self, request, view, obj):
       return True

class OrderView(APIView):
   authentication_classes = [TokenAuthentication, ]
   permission_classes = [PermissionA, PermissionA]

   def get(self, request, *args, **kwargs):
       return Response({"code": 0, "data": {"user": None, 'list': [1, 2, 3]}})


class PayView(APIView):
   authentication_classes = [TokenAuthentication, ]
   permission_classes = [PermissionA, ]

   def get(self, request, *args, **kwargs):
       return Response({"code": 0, "data": "数据..."})

 

 

关于 has_object_permission【欠】

当我们使用drf来编写 视图类时,如果是继承 APIView,则 has_object_permission不会被执行(没用),例如:

from rest_framework.views import APIView

class PayView(APIView):
   authentication_classes = [TokenAuthentication, ]
   permission_classes = [PermissionA, ]

   def get(self, request, *args, **kwargs):
       return Response({"code": 0, "data": "数据..."})

 

但是,当我们后期学习了 视图类的各种骚操作之后,发现视图也可以继承 GenericAPIView,此时 有可能 会执行 has_object_permission 用于判断是否有权限访问某个特定ID的对象(学完视图后,再细讲)。

 

 

 

调用 self.get_object 方法时,会按照 permission_classes中权限组件的顺序,依次执行他们的 has_object_permission 方法。

self.get_object其实就根据用户传入的 pk,搜索并获取某个对象的过程。

 

全局配置

REST_FRAMEWORK = {
   "DEFAULT_PERMISSION_CLASSES":["xxxx.xxxx.xx.类名","xxxx.xxxx.xx.类名",]
}

 

 

底层源码实现

 

 

 

小结

 

 

标签:1.3,self,request,认证,token,restframework,import,django,data
来源: https://www.cnblogs.com/erhuoyuan/p/16383687.html