[Django高级之Auth模块、importlib模块、settings源码]
[Django高级之Auth模块、importlib模块、settings源码]
auth模块 ←详情点击查看
1、Auth模块是什么
Auth模块是Django自带的用户认证模块:
我们在开发一个网站的时候,无可避免的需要设计实现网站的用户系统。此时我们需要实现包括用户注册、用户登录、用户认证、注销、修改密码等功能,这还真是个麻烦的事情呢。
Django作为一个完美主义者的终极框架,当然也会想到用户的这些痛点。它内置了强大的用户认证系统–auth,它默认使用 auth_user 表来存储用户数据。
1 django提供的用户认证,创建,修改密码。。。用户相关操作
2 不需要创建用户表了,默认带了
3 插入数据(创建用户):
python3 manage.py createsuperuser # 创建超级用户 通常只有一到两个超级用户
2、auth模块常用方法
from django.contrib import auth # 导入auth模块
urls.py
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^login/',views.MyLogin.as_view()), # 登录界面
url(r'^aa/',views.aa,name='aa'),
url(r'^set_password/',views.set_password,) # 修改密码
]
views.py
from django.contrib import auth # 导入auth模块
# 登录界面
def login(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
# 返回一个对象 即验证用户名以及密码是否正确,一般需要username 、password两个关键字参数。request不是必填参数
user_obj = auth.authenticate(request,username=username,password=password)
# print(user_obj)
# print(user_obj.username)
# print(user_obj.password)
if user_obj: # 返回值有可能为假 所以需要判断是否为真
auth.login(request,user_obj) # 为真的话 保存用户状态 用auth.login()方法
'''只要执行了login方法、那么在任意位置都可以通过request.user获取当前对象'''
return render(request,'login.html',locals())
def aa(request):
'''只要执行了login方法、那么在任意位置都可以通过request.user获取当前对象'''
print(request.user) # 获取用户对象 如果没有登录就是匿名用户
# print(request.user.username)
# print(request.user.password)
print(request.user.is_authenticated()) # 判断用户是否登录
return HttpResponse('哈哈')
----------------------------------------------------------------------
# 修改密码
# 验证是否登录装饰器 导入模块↓
from django.contrib.auth.decorators import login_required
# @login_required(login_url='dl') # 验证是否登录装饰器
@login_required # 全局验证是否登录装饰器
def set_password(request):
old_password = request.POST.get('old_password') # 老密码
new_password = request.POST.get('new_password') # 新密码
# 校验原密码是否相同 得到一个返回时 True or false
# 用 request.user.check_password(老密码) 可以 自动校验
is_right = request.user.check_password(old_password)
# print(is_right)
if is_right:
# 修改新密码 使用 request.user.set_password(新密码)方法
request.user.set_password(new_password)
# 一定要保存 不保存 修改无效
request.user.save()
return render(request,'set_password.html',locals())
-----------------------------------------------------------------------------------
from django.contrib.auth.models import AbstractUser,User
# 注册
def register(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
email = request.POST.get('email')
# 操作auth_user写入数据不能使用create方法 密码不会自动加密
# User.objects.create(username=username,password=password,email=email)
# 创建普通用户
User.objects.create_user(username=username,password=password,email=email)
# 创建超级用户
User.objects.create_superuser(username=username, password=password, email=email)
return render(request,'register.html')
----------------------------------------------------------------------------
# 注销
@login_required
def mylogout(request):
auth.logout(request)
# 清除浏览器客户端的保存的session;即为清除session保存的用户登录信息
return HttpResponse('注销成功')
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<h1>用户登录</h1>
<form action="" method="post">
{% csrf_token %}
<p>username:<input type="text" name="username"></p>
<p>password:<input type="text" name="password"></p>
<button>提交</button>
</form>
</body>
</html>
set_password.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<h1>密码修改</h1>
<form action="" method="post">
{% csrf_token %}
<p>username:<input type="text" name="username" value="{{ request.user.username }}" disabled></p>
<p>old_password:<input type="text" name="old_password"></p> # 输入老密码
<p>new_password:<input type="text" name="new_password"></p> # 输入新密码
<input type="submit">
</form>
</body>
</html>
register.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<h1>注册界面</h1>
<form action="" method="post">
{% csrf_token %}
<p>username:<input type="text" name="username"></p>
<p>password:<input type="text" name="password"></p>
<p>Email:<input type="email" name="email"></p>
<p><input type="submit"></p>
</form>
</body>
</html>
总结操作:
from django.contrib import auth
# 校验用户名密码是否正确
auth.authenticate(request,username=username,password=password)
# 保存用户状态
auth.login(request,user_obj)
# 查看用户是否登录
request.user.is_authenticated()
# 获取用户对象
request.user
# 校验原密码是否正确
request.user.check_password()
# 修改密码
request.user.set_password()
request.user.save()
# 用户注册
from django.contrib.auth.models import AbstractUser,User
# 操作auth_user写入数据不能使用create方法 密码不会自动加密
# User.objects.create(username=username,password=password,email=email)
# 创建普通用户
User.objects.create_user(username=username,password=password,email=email)
# 创建超级用户
User.objects.create_superuser(username=username, password=password, email=email)
# 用户注销
auth.logout(request)
# 校验是否登录装饰器
from django.contrib.auth.decorators import login_required
"""
跳转全局配置
只需在settings配置文件下写入
LOGIN_URL = '/lg/'
直接在你想要装饰的对象上写入@login_required即可
跳转局部配置
@login_required(login_url='/lg/')
"""
User对象的属性
User对象属性:username, password
is_staff : 用户是否拥有网站的管理权限.
is_active : 是否允许用户登录, 设置为 False,可以在不删除用户的前提下禁止用户登录。
扩展默认的auth_user表
这内置的认证系统这么好用,但是auth_user表字段都是固定的那几个,我在项目中没法拿来直接使用啊!
比如,我想要加一个存储用户手机号的字段,怎么办?
聪明的你可能会想到新建另外一张表然后通过一对一和内置的auth_user表关联,这样虽然能满足要求但是有没有更好的实现方式呢?
答案是当然有了。
# 第一种方案:创建一对一关联表
from django.contrib.auth.models import User
class user_detail(models.Model):
user=models.OneToOneField(to=User)
phone=models.CharField(max_length=32)
我们还可以通过继承内置的 AbstractUser 类,来定义一个自己的Model类。
这样既能根据项目需求灵活的设计用户表,又能使用Django强大的认证系统了。
# 第二种方案,继承AbstractUser类来扩写
# 使用步骤
"""
大前提是auth_user表没有创建之前干*******
写一个类,继承AbstractUser
在类中扩写字段(可以重写原来有的字段)
"""
from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):
username=models.CharField(max_length=12)
phone = models.CharField(max_length=11, null=True, unique=True)
def __str__(self):
return self.username
注意:
按上面的方案二扩展了内置的auth_user表之后,一定要在settings.py中告诉Django,我现在使用我新定义的UserInfo表来做用户认证。写法如下:
# 在配置文件中配置*******
# 引用Django自带的User表,继承使用时需要设置
AUTH_USER_MODEL = "app名.UserInfo"
再次注意:
一旦我们指定了新的认证系统所使用的表,我们就需要重新在数据库中创建该表,而不能继续使用原来默认的auth_user表了。
# 如果auth_user表已经有了,还想扩写
-删库
-清空项目中所有migration的记录
-清空源码中admin,auth俩app的migration的记录
在auth_user表的基础之上还想增加额外的字段 并且还可以使用auth模块所有的功能
# 配合文件配置
# 告诉django使用我们自己定义的表来取代auth_user表
AUTH_USER_MODEL = 'app01.Userinfo' # 应用名.表名
# 一对一表关联(了解)
# 面向对象继承(掌握)
from django.contrib.auth.models import AbstractUser
class Userinfo(AbstractUser): # 必须继承AbstractUser 继承之后我们就有了auth的所有属性
# 扩展AbstractUser表中没有的字段
phone = models.BigIntegerField()
info = models.CharField(max_length=255)
importlib模块
用字符串的形式导入模块
from b import c
print(c) # <module 'b.c' from 'D:\\day54_1\\b\\c.py'>
import importlib
res = 'b.c'
# 下面操作相当于 from b import c 把字符串按照. 来切割成下面形式
ret = importlib.import_module(res) # from b import c
print(ret) # <module 'b.c' from 'D:\\day54_1\\b\\c.py'>
例如:
# settings.py
NOTIFI_LIST = [
'notify.email.Email',
'notify.qq.QQ',
'notify.weixin.WeiXin',
]
---------------------------------------------------------------------------
# notify 包
# 包下有 __init__方法 以及三个应用 weixin.py qq.py email.py
# __init__下
import settings
import importlib
def send_all(content):
for path_str in settings.NOTIFI_LIST:
module_path,class_name = path_str.rsplit('.',maxsplit=1)
# print(module_path)
# 1、利用字符串导入模块
module = importlib.import_module(module_path) # from notify import email,qq,weixin
# 2、利用反射获取类名
cls = getattr(module,class_name) # email qq weixin
# 3、生成类的对象
obj = cls()
# 4、利用鸭子类型直接send方法
obj.send(content)
---------------------------------------------------------------
# 应用
class WeiXin(object):
def __init__(self):
pass
def send(self,content):
print('微信通知:%s'%content)
class QQ(object):
def __init__(self):
pass
def send(self, content):
print('QQ通知:%s' % content)
class Email(object):
def __init__(self):
pass
def send(self, content):
print('Email通知:%s' % content)
-------------------------------------------------------------------------------
# start.py 启动文件
import notify
notify.send_all('开学了')
# 只需要在里面写相应的通知即可发送到全应用
# 拓展性强
# 哪个不用了只需要到settings中注释即可
settings源码
django有两个配置文件
一个是暴露给用户自定义的配置
一个是项目默认的配置
用户没有配置的情况下使用的是项目默认的配置
用户配置了的情况下则使用用户配置的
如何实现
先读取项目默认配置
再读取自定义配置
# 字典的键存在则替换 不存在则创建
from django.conf import settings
class LazySettings(...):
def _setup(self, name=None):
settings_module = os.environ.get(ENVIRONMENT_VARIABLE) # 'day53.settings'
self._wrapped = Settings(settings_module) # Settings('day53.settings')
class Settings(object):
def __init__(self, settings_module): # 'day53.settings'
for setting in dir(global_settings): # 获取全局配置文件可以点的变量名
if setting.isupper(): # 校验配置必须全大写
setattr(self, setting, getattr(global_settings, setting))
# setting指代的是所有全大写的变量名
# getattr(global_settings, setting)获取全大写变量名对应的值
# 给对象设置属性 可以简单的理解为 给字典添加键值对
self.SETTINGS_MODULE = settings_module # 'day53.settings'
mod = importlib.import_module(self.SETTINGS_MODULE)
settings = LazySettings(...)
for setting in dir(mod): # 获取
if setting.isupper():
setting_value = getattr(mod, setting)
setattr(self, setting, setting_value)
基于源码实现插拔式设计
- conf # 文件夹
- settings.py # 用户默认配置
- lib # 文件夹
-conf # 文件夹
- __init__.py
- global_settings.py # 系统默认配置
- start.py # 启动文件
start.py
import os
import sys
BASE_DIR = os.path.dirname(__file__)
sys.path.append(BASE_DIR) # 加入环境变量
# 将自定义配置文件路径添加到全局环境大字典
os.environ.setdefault('xxx','conf.settings')
from lib.conf import settings # 导入实例化的对象
if __name__ == '__main__':
print(settings.NAME) 对象点变量名 就可以拿到对象的值
------------------------------------------------------------------------------------
settings.py
NAME = '我是自定义的配置'
-----------------------------------------------------------------------------------
globel_settings.py
NAME = '我是系统默认的配置'
-------------------------------------------------------------------------------------
__init__.py
from lib.conf import global_settings # 导入系统配置文件
import os
import importlib
# from conf import settings
class Settings(object):
def __init__(self):
for i in dir(global_settings): # 拿到系统配置里面的所有变量名
# print(dir(global_settings))
# print(i)
if i.isupper(): # 判断是否大写
setattr(self,i,getattr(global_settings,i))
# i 是拿到的变量名
# 而getattr(global_settings,i) 利用反射拿到的是配置文件里变量名对应的值
# 之后利用反射把系统配置里面的变量名添加到当前对象
# 给对象设置属性 可以简单的理解为 给字典添加键值对
ooo = os.environ.get('xxx') # 获取start.py 文件里自定义配置文件路径
# os.environ.setdefault('xxx','conf.settings') 这是自定义配置文件的路径
# 相当于 from conf import settings
path_name = importlib.import_module(ooo)
# path_name 拿到的就是一个文件路径
for x in dir(path_name): # 拿这个路径下settings.pyp配置文件中的所有可以点的变量名
if x.isupper():
key = x
value = getattr(path_name,key)
setattr(self,key,value)
# 利用反射把用户配置里面的变量名添加到当前对象
# 如果有重复的变量名,则覆盖掉
# 这就是为什么我们既可以拿到用户配置里面的变量名
# 也可以拿到系统配置里面的变量名
# 因为这里把两个配置文件的变量名都整合到一起
# 用户配置有自定义的就用用户自己的,没有的话用系统配置
# 这就是 有则修改 无则添加
settings = Settings() # 实例化得到一个对象 之后在start.py 中导入

浙公网安备 33010602011771号