65、django转换器 、配置文件作用、登陆功能 、 认证组件 、权限组件 、 频率组件

172 阅读8分钟

django转换器

1.django转换器:django 2.x以后,为了取代 re_path
  str:匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
  int:匹配正整数,包含0。
  slug:匹配字母、数字以及横杠、下划线组成的字符串。
  uuid:匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
  path:匹配任何非空字符串,包含了路径分隔符(/)
  
ps:path('books/<str:name>')   ---->/books/1----》name=1---》当参数传入视图类的方法中

配置文件作用

概要

1.djagno项目要运行,优先执行配置文件的内容,做一下配置加载工作
    -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django项目名.settings')
2.任何一个django项目都有两套配置:
		一套是项目自己的,自己有那个配置参数,优先用自己的
		一套是内置的(django内置的)--->from django import settings
 

配置参数的作用

1.项目的根路径

BASE_DIR = Path(__file__).resolve().parent.parent

2.密钥

djagno中涉及到加密的,大部分都会用这个密钥

SECRET_KEY = 'django-insecure-eh=o(kt_70%8wj4r+le-7*$7t+fn%_2rfh61f09(2^&3q-5vk)'

3.调试模式DEBUG

DEBUG = False  # 是否开启调试模式

ps:1.上线一定关闭
    2.只要是调试模式:访问的路径不存在,他会显示出所有能访问的路径,视图类出了异常,浏览器中能看到

4.ALLOWED_HOSTS

项目是要部署在某个服务器上,这个列表写,部署服务器的ip地址, * 表示任意地址都可以

ALLOWED_HOSTS = ['*']

5.INSTALLED_APPS

里面有内置的app和我们自己写的app

INSTALLED_APPS = [
    'django.contrib.admin',  # 后台管理--->很多表不是它的,是别的app的
    'django.contrib.auth',  # auth 模块,UsrInfo表---->有6个表
    'django.contrib.contenttypes',  # 有个django_content_type表是这个app的
    'django.contrib.sessions',  # session相关的
    'django.contrib.messages',  # 消息框架
    'django.contrib.staticfiles',  # 静态文件开启的app
    'app01.apps.App01Config',  # app01
    'rest_framework'  # drf
]


ps:想看字符串里面的内容:
  eg:'django.contrib.auth'
  from django.contrib import auth

6.中间件

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',  # session相关
    'django.middleware.common.CommonMiddleware',  # 公共
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

8.模版所在路径

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [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',
            ],
        },
    },
]

9.WSGI_APPLICATION项目上线

运行application

WSGI_APPLICATION = 'drf_day07.wsgi.application'

10.数据库配置

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

11.密码认证相关

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',
    },
]

12.做国际化

LANGUAGE_CODE = 'zh-hans'   --->语言
TIME_ZONE = 'Asia/Shanghai' --->时区
USE_I18N = True
USE_L10N = True
USE_TZ = True

"""
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
"""

13.静态文件

STATIC_URL = '/static/'

14.DEFAULT_AUTO_FIELD

表中,默认可以不写id,id主键自增,之前全是AutoField,长度很短

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

登陆功能

models.py

from django.db import models

# Create your models here.
class UserInfo(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=64)

class UserToken(models.Model):
    token = models.CharField(max_length=32)
    user = models.OneToOneField(to=UserInfo,on_delete=models.CASCADE)

views.py

from .models import UserInfo,UserToken
from rest_framework.response import Response
import uuid

from rest_framework.viewsets import ViewSet
class UserView(ViewSet):
    def login(self,request):
        username = request.data.get('name')
        password =request.data.get('pwd')
        print(request.data)
        print(username,password)
        user_obj = UserInfo.objects.filter(username=username,password=password).first()
        if not user_obj:
            return Response({'code':101,'msg':'用户名或密码错误'})
        else:
            # 有这个用户,就应该返回加密字符串,并且写到UserToken中
            # 用户在次登陆会更新UserToken中,该用户的token值,第一次登陆就会创建--->判断
            uuid_str = uuid.uuid4()
            # print(uuid_str,type(uuid_str)) # ffebb2ba-7048-475f-914e-3eda1ae07eee <class 'uuid.UUID'>

            # 方式一
            # if UserToken.objects.filter(user=user_obj).first():
            #     # 有这个用户更新token值
            #     UserToken.objects.filter(user=user_obj).update(token=str(uuid_str))
            # else:
            #     UserToken.objects.create(token=str(uuid_str), user=user_obj)

            # 方式二
            UserToken.objects.update_or_create(user_id = user_obj.id,defaults={'token':str(uuid_str)})
            return Response({"code":100,'msg':'登陆成功','token':uuid_str})
          
          
ps:update_or_create(self, defaults=None, **kwargs):
		
            """
        def update_or_create(self, defaults=None, **kwargs):
            '''
            Look up an object with the given kwargs, updating one with defaults
            if it exists, otherwise create a new one.
            Return a tuple (object, created), where created is a boolean
            specifying whether an object was created.
            '''

            """

urls.py

from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/',views.UserView.as_view({'post':'login'}) ),
]

认证组件

简介

1.APIView执行流程
	1.在视图视图类的方法之前,执行了三大认证
  2.顺序:认证-->权限-->频率 	
       
2.认证:如登录认证
		登录认证--->控制,某个接口必须登录后才能访问

案例

需求:让查询所有接口,必须登录后才能访问

auth.py

from rest_framework.authentication import BaseAuthentication
from .models import UserToken
from rest_framework.exceptions import AuthenticationFailed

class LoginAuth(BaseAuthentication):
    def authenticate(self, request): # 父类中有,一定要重写
        """
        校验用户是否登陆-->请求中携带token就登陆了
        token携带的地方-->是接口规定的-->可以是在请求地址中,也可以是在请求头中
        """
        print(request.query_params)
        token = request.query_params.get('token')
        # 拿着token去UserToken表中去找
        token_obj =UserToken.objects.filter(token=token).first()
        if token_obj:
            user= token_obj.user   # 当前登录用户就是user
            return user,token

        else:
            raise AuthenticationFailed('请登陆')

views.py

# 如何算登陆-->登录成功后,返回给前端一个token随机字符串,只要携带我给的随机字符串,我就能定位到是谁,就是这个人登录了

from .auth import LoginAuth
from rest_framework.views import APIView
from rest_framework.response import Response
class BookView(APIView):
    authentication_classes = [LoginAuth]
    def get(self,request):
        return Response({'code':100,'msg':'查到了好多的书集信息'})

urls.py

urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/',views.UserView.as_view({'post':'login'}) ),
    path('books/', views.BookView.as_view()),
]

ps:访问:http://127.0.0.1:8000/books/?token=2c3cacb4-30f4-4760-962f-42a73c544b50

认证组件使用步骤(固定用法)

1.写一个类
  1.这个类继承BaseAuthentication
  2.在类中写:authenticate方法authenticate
  3.在方法中,完成登陆认证,没有通过抛异常,通过就返回(user, token)-->登陆用户和tiken
2.1在视图类中,使用认证类(局部使用)
    class BookView(APIView):
    	authentication_classes = [LoginAuth, ] 
     
2.2 全局使用:--->必须局部禁用
    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': [
        'app01.auth.LoginAuth'
        ],
    }
    ps: 1.不要在配置文件中,导入莫名其妙的包
  		   2.全局使用后,局部禁用
      class UserView(ViewSet):
          authentication_classes = []
    		
3.认证类的使用顺序
	1.优先用视图类配置的
  2.其次用项目配置文件
  3.最后用drf默认的配置
        
        
ps:一旦通过认证,在request中就有当前登录用户-->request.user
    def get(self, request):
        # 一旦通过认证,在request中就有当前登录用户
        print(request.user.name,'访问了接口')

权限组件

分析

1.登陆认证通过后,但有的功能(接口),只有超级管理员能做,有的功能所有登录用户都能做---->这就涉及到权限的设计了
2.权限设计:比较复杂--->有acl,rbac,abac

案例

需求:查询所有图书:所有登录用户都能访问(普通用户和超级管理员);删除图书,只有超级管理员能访问

permission.py

from rest_framework.permissions import BasePermission

class AdminPermission(BasePermission):
    def has_permission(self, request, view):
        """
        如果有权限,就返回True,没有权限,就返回False
        判断user_type是不是1,根据当前用户-->request.user
        """
        print(request.user,type(request.user))

        if request.user.user_type == 1:
            return True
        else:
            # 错误信息
            self.message = '你好:%s,你没有权限'%request.user.username
            return False

views.py

from .permission import AdminPermission
class BookDetailView(APIView):
    permission_classes = [AdminPermission]
    def delete(self,request,**kwargs):
        return Response({'code':100,'msg':'删除成功'})

urls.py

from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/',views.UserView.as_view({'post':'login'}) ),
    path('books/', views.BookView.as_view()),
    path('books/<int:pk>/', views.BookDetailView.as_view()),
]


访问:http://127.0.0.1:8000/books/1/?token=2c3cacb4-30f4-4760-962f-42a73c544b50

权限类的使用步骤

1.写一个类,
	1.继承BasePermission
  2.在类中写方法:has_permission
    1.如果有权限,就返回True
    2.如果没有权限,就返回False
    3.错误信息是self.message='字符串'
        
2.1 局部使用
    class BookDetailView(APIView):
      	permission_classes = [AdminPermission, ]
2.2 全局使用
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'app01.permission.AdminPermission',
    ],
}
3.局部禁用
    class BookView(APIView):
    	permission_classes = []

频率组件

分析

1.频率组件:限制访问频次
	1.比如某个接口,一分钟只能访问5次,超过了就得等
2.限制方式
  1.按IP地址限制
  2.按用户id限制

案例

需求:同一ip不能够在一分钟以内访问三次以上

throttling.py

from rest_framework.throttling import SimpleRateThrottle

class MyThrottle(SimpleRateThrottle):
    scope = 'nana'
    def get_cache_key(self, request, view):
        # 返回客户端ip地址
        print(request.META)
        ip = request.META.get('REMOTE_ADDR')
        print('客户端的ip是:',ip)
        return ip

项目.settings.py

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES': {
    'nana':'3/m'
    },
}

views.py

from .auth import LoginAuth
from rest_framework.views import APIView
from rest_framework.response import Response
from .throttling import MyThrottle
class BookView(APIView):
    authentication_classes = [LoginAuth]
    permission_classes = []
    throttle_classes = [MyThrottle]

urls.py

from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/',views.UserView.as_view({'post':'login'}) ),
    path('books/', views.BookView.as_view()),
    path('books/<int:pk>/', views.BookDetailView.as_view()),
]

频率类的使用步骤

1.写个类
	1.继承:SimpleRateThrottle
  2.重写某个方法:get_cache_key
    	1.可以返回ip或用户id
    			ps:ip:request.META.get('REMOTE_ADDR')
    	2.返回什么,就以什么做频率限制
        
	3.写一个类属性,随意命名一个名
    	scope = 'nana'
4.在配置文件中配置:

    REST_FRAMEWORK = {

       'DEFAULT_THROTTLE_RATES': {
          'nana': '3/m' # 一分钟访问3次--->前面的key值是scope的值
      },
  }
        
4.全局用
    REST_FRAMEWORK = {
        'DEFAULT_THROTTLE_RATES': {
        'nana':'3/m'
        },
        'DEFAULT_THROTTLE_CLASSES': ['app01.throttling.MyThrottle'],
    }
5.局部用
    class BookView(APIView):
        throttle_classes = [MyThrottle]

作业

0 认证类的token从请求头中取
1 登录接口,编写图书的5个接口
	-不登录就能查询所有图书
	-查询单条,新增,修改,删除,都需要登录后才访问
	-删除和修改要超级管理员才能操作
	-所有接口,一秒钟只能访问2次

-----一般人写不出来----
2 使用django中间件实现,按ip地址,一分钟只能访问5次的限制

认证类的token从请求头中取

authentication.py

from rest_framework.authentication import BaseAuthentication
from app01 import models
from rest_framework.exceptions import AuthenticationFailed

class LoginAuthentiction(BaseAuthentication):
    def authenticate(self, request):
        # 获取token值--->token值在请求头中
        print(request.META)
        token = request.META.get('HTTP_TOKEN')
        # print(token)

        # 根据token在usertoken表中查找
        token_obj = models.UserToken.objects.filter(token=token).first()
        print(token_obj)
        # 如果有token,返回token user,没有抛异常
        if token_obj:
            user = token_obj.user
            return token,user
        else:
            raise AuthenticationFailed('请登陆哦')
            

views.py

rom rest_framework.views import APIView
from app01 import models
from rest_framework.response import Response
from .authentication import LoginAuthentiction

class BookView(APIView):
    authentication_classes = [LoginAuthentiction]

    def get(self,request):
        # print(request.user)
        return Response({'code':100,'msg':'查询了很多数据'})

注意

1.在Django中处理具体请求的时候,使用request.META.get(key)方法去获取header中key对应的value值,得注意的是官方文档里面有说明 Django获取自定义header信息的方法:

  1.所有header名大写,将连接符“-”改为下划线“_”
  2.除了 CONTENT_LENGTH 和 CONTENT_TYPE,其他的头部在META字典中的key值都会被加上“HTTP_”的前缀

登录接口,编写图书的5个接口

1.不登录就能查询所有图书
2.查询单条,新增,修改,删除,都需要登录后才访问
3.删除和修改要超级管理员才能操作
4.所有接口,一秒钟只能访问2