Django过滤补充加异常处理

Django过滤补充加异常处理

内置认证类,权限类,频率类

内置认证类

BaseAuthentication  # 认证类基类
	BasicAuthentication  # 基于浏览器进行认证
	RemoteUserAuthentication  # 基于Django admin中的用户进行认证,这也是官网的示例
	SessionAuthentication:sessin认证  # 基于django的session进行认证
		如果前端呆着cookie过来,经过session的中间件,如果登陆了,那么在request.user中就有当前登录用户
		drf没有限制是否登录
		加了这个认证如果没有登录,就不允许往后访问了
	TokenAuthentication,jwt认证  # 基于drf内部的token认证

源码验证流程
1.在django(CBV)中,客户端的发来的请求会执行视图类的as_view方法,而as_view方法中会执行dispacth方法,然后在根据请求的类型(反射)执行相应的方法(get、post等)。

2.使用django rest framework中的视图类需要继承APIView,请求到达视图类会执行视图类的as_view方法,而OrderView中没有as_view()方法,所以执行APIView的as_view()方法,

3.从APIView源码中可以看到APIView中as_view又执行了父类的as_view方法,在看看APIView的父类是View类,这恰好是django中的view视图类,

4.从View源码可以看出View类的as_view()方法执行流程:验证请求方法--->返回view函数名称(view函数会执行dispatch方法),一旦有请求进来执行view函数-->执行dispatch方法

5.当APIView的as_view方法执行了父类的as_view方法以后,请求进来会执行view方法,view方法中会执行dispatch方法,而Oderview没有dispatch方法,所以执行父类(APIView)的dispatch方法,

6.从APIView源码分析,执行APIView的dispatch方法时候会执行self.initialize_request方法,会对django原始的request进行封装。

7.self.initialize_request()源码分析,实例化Request()类,封装原始的request,authenticators(认证),执行self.get_authenticators(),到了这里就开始django rest framework的认证流程

8.self.get_authenticators()源码分析,采用列表生成式,循环self.authentication_classes,实例化其中的每一个类,返回列表,不难发现authentication_classes属性正式我们在认证的时候用到认证类列表,这里会自动寻找该属性进行认证。倘若我们的视图类没有定义认证方法呢?,当然django rest framework 已经给我们加了默认配置,如果我们没有定义会自动使用settings中的DEFAULT_AUTHENTICATION_CLASSES作为默认(全局),

 9.继续分析APIView的dispatch方法,此时执行self.inital方法,并将封装过后的request对象(Reuqest)作为参数进行传递,

10.在self.inital方法中会执行self.perform_authentication方法,而self.perform_authentication方法用会执行request.user,此时的request是Request对象,所以需分析Request类中的user属性,

11.从源码分析,在Request对象中,user属性是一个属性方法,并会执行self._authentication方法,

12.从源码分析,Request对象的self._authentication中循环self.authenticators(该列表是由认证对象构成的[对象1,对象2]),并执行每一个对象中的authenticate方法返回tuple,同时对该过程其进行了异常捕捉,有异常将返回给用户,下面是异常验证逻辑:

如果有异常则执行self._not_authenticated()方法,继续向上抛异常。
如果有返回值必须是一个元组,分别赋值给self.user, self.auth(request.user和request.auth),并跳出循环。
如果返回None,则由下一个循环处理,如果都为None,则执行self._not_authenticated(),返回 (AnonymousUser,None)

13.当都没有返回值,就执行self._not_authenticated(),相当于匿名用户,没有通过认证,并且此时django会返回默认的匿名用户设置AnonymousUser,如需要单独设置匿名用户返回值,则编写需要写UNAUTHENTICATED_USER的返回值:

14.所以经过以上分析,我们需要进行认证时候,需要在每一个认证类中定义authenticate进行验证,并且需要返回元祖。

原文链接:https://juejin.cn/post/7027424194186985508

内置权限类

BasePermission  # 权限类基类
	AllowAny  # 允许所有
	IsAuthenticated  # 是否登录,登陆了才有权限,基于django的认证权限,官方示例
		# 认证类,如果登陆了,返回(当前用户,None),如果没有等领域则返回None
	IsAdminUser  # auth的user表中的is_staff字段是否为True,对后台有没有权限,基于django admin权限控制
	IsAuthenticatedOrReadOnly  # 基于django admin
    
源码验证流程
1.同样请求到达视图时候,先执行APIView的dispatch方法,以下源码是我们在认证篇已经解读过了

2.执行inital方法,initial方法中执行perform_authentication则开始进行认证

3.当执行完perform_authentication方法认证通过时候,这时候就进入了本篇文章主题--权限(check_permissions方法),下面是check_permissions方法源码

4.从上源码中我们可以看出,perform_authentication方法中循环get_permissions结果,并逐一判断权限,所以需要分析get_permissions方法返回结果,以下是get_permissions方法源码

5.get_permissions方法中寻找权限类是通过self.permission_class字段寻找,和认证类一样默认该字段在全局也有配置,如果我们视图类中已经定义,则使用我们自己定义的类。

6.承接check_permissions方法,当认证类中的has_permission()方法返回false时(也就是认证不通过),则执行self.permission_denied(),以下是self.permission_denied()源码

7.认证不通过,则至此django rest framework的权限源码到此结束,相对于认证源码简单一些。

原文链接:https://juejin.cn/post/7027424194186985508

内置频率类

BaseThrottle  # 频率类基类
	SimpleRateThrottle  # 我们写的频率类都要继承他
	AnonRateThrottle  # 如果你想按ip地址限制,只需要在配置文件中配置
      'DEFAULT_THROTTLE_RATES': {
        'pig': '5/m',
    	}
	UserRateThrottle  # 如果你想按用户id限制,只需要在配置文件中配置
      'DEFAULT_THROTTLE_RATES': {
        'pig': '5/m',
    	}
	ScopedRateThrottle
    
    
源码解析
1.dispatch()

2.执行inital方法,initial方法中执行check_throttles则开始频率控制

3.下面是check_throttles源码,与认证、权限一样采用列表对象方式,通过判断allow_request方法返回值判断频率是否通过

4.get_throttles方法,采用列表生成式生成频率控制对象,与认证、权限一直

5.self.throttle_classes属性获取

6.通过以上分析,知道了频率控制是通过判断每个类中的allow_request放法的返回值来判断频率是否通过,下面我们来看看我们所使用的SimpleRateThrottle怎么实现的,分析部分请看注解

原文链接:https://juejin.cn/post/7027424194186985508

Django各配置作用

所有的配置文件都必须是大写,Django项目要启动,都需要先加载配置文件,如果配置文件报错那么项目就启动不起来,我们在给配置文件加东西的时候千万要注意,不要调用一些需要项目启动之后才可以运行的程序,否则会直接报错
from pathlib import Path  # 首先是自动导入的path模块
import os  # 有时候找不到os模块的话可以自行导入

# 项目的根路径
BASE_DIR = Path(__file__).resolve().parent.parent

# 密匙,自动生成的,很复杂,在Django中只要有加密那么都会使用它,没有的话会直接报错,我们有时候在导入的时候如果有需要导入的不小心将该导入放到密匙前,那么系统就会自动触发没有密匙所以一定要注意
SECRET_KEY = 'django-insecure-t9uaxxg4sg=htdon^*q!gs_dqn^msqdg55qzx@t49+b_1(avmn'

# DEBUG模式也就是测试模式,如果是True那么该项目就还处于调试中,好处是如果抛异常那么在浏览器就可以直接能看到,如果路径不存在的话,那么也会提示哪些路径是可以访问的路径
DEBUG = True

# 允许项目部署的地址(后期项目上线,这里写服务器地址),DEBUG是False的情况那么这个就必须加,不加就会报错。
ALLOWED_HOSTS = []
# ALLOWED_HOSTS = ['*']  如果测试可以使用这个包含全路径


# 所有的app都需要在这里配置,Django大而全,很大一部分原因就是因为它给我们提供了很多的内置app
INSTALLED_APPS = [
    'django.contrib.admin',  # 后天管理app,admin
    'django.contrib.auth',  # 权限,六个表
    'django.contrib.contenttypes',  # 可以跟踪Django项目中所有的模型(model),为我们提供更高级的模型接口。默认情况下,它已经在sessions中,如果没有需要手动添加
    'django.contrib.sessions',  # session认证相关,django_session表
    'django.contrib.messages',  # 消息框架,Django内置的消息传递应用
    'django.contrib.staticfiles',  # 静态文件相关,帮助开发者管理渲染页面所需要的JS,CSS等 静态文件
    'app01.apps.App01Config',  # zpp01,我们在创建Django项目时创建的app
    'rest_framework'  # rest_framework,app我们所用到的浏览器相关的rest_framework
]

# 七大中间件
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',  # 
    'django.contrib.sessions.middleware.SessionMiddleware',  # session相关中间件
    'django.middleware.common.CommonMiddleware',  # 把路径跟路由匹配,如果匹配不成功把路径加上'/'再进行一次匹配,如果匹配成功那么,让该请求重定向到带有/的地址
    'django.middleware.csrf.CsrfViewMiddleware',  # csrf相关
    'django.contrib.auth.middleware.AuthenticationMiddleware',  # 认证相关
    'django.contrib.messages.middleware.MessageMiddleware',  # 消息框架爱,学到flask,闪现相同的功能
    'django.middleware.clickjacking.XFrameOptionsMiddleware',  # 给resposne添加X-Frame-Options属性,DENY: 浏览器拒绝当前页面加载任何frame页面,SAMEORIGIN: frame页面的地址只能为同源域名下的页面,ALLOW-FROM: 允许frame加载的页面地址
]

# 根路由,所有请求进来,先去根路由匹配,内部可能有路由分发
ROOT_URLCONF = 'drf_day8.urls'

# 模板相关
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')]
        ,
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

# 后期项目上线,uwsgi运行这个application测试阶段使用manage.py运行项目
WSGI_APPLICATION = 'drf_day8.wsgi.application'

# 数据库相关配置(可以使用多数据库) 
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

# auth的认证相关
AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

# 做Django的国际化
LANGUAGE_CODE = 'zh-hans'  # 文字
TIME_ZONE = 'Asia/Shanghai'  # 时区(东八区时间)
USE_I18N = True  # 启用翻译系统
USE_L10N = True  # 	数据的本地化格式显示数字和时间
USE_TZ = False  # 避免models存储数据时发生时间不一致的问题

# 静态文件相关
STATIC_URL = 'static/'

# 所有的自增主键都用BigAutoField
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

过滤类的其他使用

在内置的过滤类中,只能通过search=搜索条件,这种方式进行搜索,条件一=xxx&条件二=xxx

第三方过滤模块:django-filter
1.下载模块pip3 install django-filter,一定要注意,在下载第三方模块时,与其有关联的模块可能会跟着一起更新,所以需要检查一下,是否被自动更新至最新版本

2.导入该模块
from django_filters.rest_framework import DjangoFilterBackend

3.配置在试图类中
filter_backends = [DjangoFilterBackend,]

4.配置搜索的字段
filterset_fields = ['搜索条件一', '搜索条件二']

5.支持的搜索方式
http://127.0.0.1:8000/访问视图/?搜索条件一=xxx&搜索条件二=xxx

6.搜索模式
精准搜索,条件是并且的关系,两个搜索必须精确多一少一都不可,两个只要有一个错误那么就搜索不到

手搓,自定义过滤类
1.自己写一个类方法,但是必须要继承BaseFilterBackend

2.重写某个方法
from rest_framework.filters import BaseFilterBackend

class AsBaseFilterBackend(BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        name = request.query_params.get('name',None)
        publish = request.query_params.get('publish', None)
        queryset = queryset.filter(name__icontains=name,publish__icontains=publish)
        return queryset

3.配置在视图类上
filter_backends = [AsBaseFilterBackend,]
多个过滤类和排序类可以共用,filter_backends=[],可以配置多个,执行顺序是从做往右,所以,放在最左侧的尽量先过滤掉大部分数据


全局异常处理

drf中无论是三大认证还是视图类的方法中执行,只要报错(主动抛异常),都能执行一个函数(异常处理的函数)
只要除了异常在APIView的dispatch中捕获了执行了配置文件中的配置REST_FRAMEWORK = {
'EXCEPTION_HANDLER':'app01.exctptions.common_exception_handler'
}


异常捕获
from rest_framework.views import exception_handler
from rest_framework.filters import SearchFilter
from rest_framework.response import Response
import time
import datetime
def common_exception_handler(exc,context):
    # 我们之后如果写代码的话,需要记录日志,只要进入了这里那么就一定是出现了异常,只要出现异常我们就可以到日志中寻找是哪里出现异常,也方便了纠错
    print('裂开来')
    print(exc)  # 查看exc是什么错误
    print(context)  # 查看内容
    request = context.get('request')  # 获取当前请求的request
    # 因为我们只能捕获外面的异常如果这里面有异常是捕获不到的,所以我们应该在内部也有异常捕获
    try:
        # 获取username
        username = request.user.username
    except:
        username = '没有登录'
    ctime = time.time()
    path = request.path
    method_type = request.method
    # 我们提示错误信息
    print(f'{username}用户,在{ctime}时间,访问{path}接口,通过{method_type}请求访问,除了错误:{str(exc)}')
    # 内置的这个异常处理函数只能处理drf的异常(继承了APIException的异常)
    response = exception_handler(exc, context)
    # 如果response有值,说明错误被处理了(Http404,PermissionDenied,APIException)
    if response:
        return Response({'code':701, 'msg':'drf错误,错误原因:%s'%response.data.get('detail','未知错误')})
    else:  # 如果没有值,这个错误就没有被处理,说明不是drf的错误,而是django的错误
        return Response({'code': 601, 'msg': '系统错误,错误原因:%s'% str(exc)})


接口文档

1.我们之后的工作 中,都需要写接口文档给前端人员使用,所以学习接口文档的使用
	请求地址
	请求方式
	支持的编码格式
	请求参数(get,post参数)
	返回格式示例

2.文档书写方式:
	方式一 直接使用word或者markdown写
	方式二 使用接口文档平台,在接口文档平台录入(Yapi(百度开源的接口问题),第三方平台(需要收费),自己公司开发的接口文档平台)基本都大差不差
	方式三 项目自动生成的接口文档:swagger,coreapi
		1.下载:pip3 install coreapi
		2.路由中配置 
			from rest_framework.documentation import include_docs_urls
			urlpatterns = [
    path('docs/', include_docs_urls(title='站点页面标题'))
]
		3.在视图类中加注释
		4.在配置文件中配置
			REST_FRAMEWORK = {'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',}
		5.直接在试图类中写注释:在序列化类上写help_text

练习

from rest_framework.filters import BaseFilterBackend
from django_filters import FilterSet
from django.db.models import Q
class AsBaseFilterBackend(BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        name = request.query_params.get('name',None)
        publish = request.query_params.get('publish', None)
        if name and publish:
            queryset = queryset.filter(Q(name__icontains=name,publish__icontains=publish))
            return queryset
        elif name:
            queryset = queryset.filter(name__icontains=name)
            return queryset
        elif publish:
            queryset = queryset.filter(publish__icontains=publish)
            return queryset
        else:
            return queryset
posted @ 2022-10-11 22:11  Joseph-bright  阅读(68)  评论(0编辑  收藏  举报