1.基于django中间件的拷贝思想
需求:实现三种通知功能
#方式一:
#notify.py
def send_email(content):
print('邮箱通知:%s'% content)
def send_msg(content):
print('短信通知:%s'% content)
def send_wechat(content):
print('微信通知:%s'% content)
#start.py
from first.notify import *
def send_all(content):
send_msg(content)
send_email(content)
send_wechat(content)
if __name__ == '__main__':
send_all('后天是周末')
#方式二:
#lib文件夹
#bbb.py
name='from bbb'
#aaa.py
import importlib
res='lib.bbb'
#利用字符串形式导入模块
md=importlib.import_module(res) #这句相当于 from lib import bbb
print(md) #该模块字符串最小单位只能到文件名
#settings.py
NOTIFY_LIST=[
'notify.email.Email',
'notify.msg.Msg',
'notify.qq.Qq',
'notify.WeChat.WeChat',
]
#start.py
from notify import *
send_all('周末了,哈哈哈哈')
#notify 包文件
#email.py
class Email(object):
def __init__(self):
pass
def send(self,content):
print('邮件通知:%s'%content)
#msg.py
class Msg(object):
def __init__(self):
pass
def send(self, content):
print('短信通知:%s' % content)
#qq.py
class Qq(object):
def __init__(self):
pass
def send(self, content):
print('qq通知:%s' % content)
#wechat.py
class WeChat(object):
def __init__(self):
pass
def send(self, content):
print('微信通知:%s' % content)
#__init__.py
import settings
import importlib
def send_all(content):
for path in settings.NOTIFY_LIST: #拿到一个个的路径字符串 'notify.email.Email'
module_path,cls_name=path.rsplit('.',maxsplit=1)
#字符串'notify.email' 类cls_name='Email'
md=importlib.import_module(module_path) #from notify import email
cls=getattr(md,cls_name) #获取到文件夹中类的名字
obj=cls() #产生一个个类对象
obj.send(content) #鸭子类型
2.跨站请求伪造csrf
- 如何实现钓鱼网站
- 你在写form表单的时候 让用户填写的账户input并没有name属性,是你自己在内部偷偷隐藏了一个具有name属性的input框,并且value值是你自己的账户,然后将该标签隐藏了
- 模拟该现象的产生 创建两个django项目
#正经网站
#transfer.py
def transfer(request):
if request.method=='POST':
username=request.POST.get('username')
target_user=request.POST.get('target_user')
money=request.POST.get('money')
print('%s 给 %s 转了 %s钱'%(username,target_user,money))
return render(request,'transfer.html')
#transfer.html
<p>这是正经网站</p>
<form action="" method="post">
<p>username:<input type="text" name="username"></p>
<p>target_account:<input type="text" name="target_user"></p>
<p>money:<input type="text" name="money"></p>
<input type="submit">
#钓鱼网站
#transfer.py
def transfer(request):
return render(request,'transfer.html')
#transfer.html
<p>这是假冒的网站</p>
<form action="http://127.0.0.1:8000/transfer/" method="post">
<p>username:<input type="text" name="username"></p>
<p>target_account:
<input type="text">
<input type="text" name="target_user" value="ldd" style="display: none"></p>
<p>money:<input type="text" name="money"></p>
<input type="submit">
- 如何解决钓鱼网站这种问题:只处理本网站提供的数据
- 那么如何判断当前请求是本网站发出的呢?
- 解决方法:网站在返回给用户一个form表单的时候 会自动在该表单隐藏一个input框,这个框
的value是一个随机字符串 但是网站能够记住每一个浏览器发送的随机字符串
以后在写form表单的时候 只需要在表单中写一个{% csrf_token %}
<p>这是正经网站</p>
<form action="" method="post">
{% csrf_token %}
<p>username:<input type="text" name="username"></p>
<p>target_account:<input type="text" name="target_user"></p>
<p>money:<input type="text" name="money"></p>
<input type="submit">
- 为了防止钓鱼网站恶意拦截,正常网站都只要接收到post请求,都会做csrf校验
3.ajax的post请求如何用csrf解决
- 方式一:较为繁琐
- 先在页面任意位置上书写{% csrf_token %}
- 然后在发送ajax请求的时候 通过标签查找获取随机字符串,添加到data自定义对象即可
- 方式二:较为简单
- 方式三:官网提供的文件 最通用的一种方式,用于前后端分离
- 直接新建js文件,放在static文件夹里,记得settings里面配一下。拷贝代码 导入即可
- 不需要做任何的csrf相关的代码书写
{% csrf_token %}
<button id="d1">发送ajax</button>
<script src="/static/setup.js"></script> //js文件相关
<script>
$('#d1').click(function () {
$.ajax({
url:'',
type:'post',
// 第一种
data:{'username':'jason','csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val()},
// 第二种
data:{'username':'jason','csrfmiddlewaretoken':'{{ csrf_token }}'},
// 第三种 利用脚本文件
data:{'username':'jason'},
success:function (data) {
alert(data)
}
})
})
</script>
静态文件夹里的js文件:
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
将这些代码的文件配置到你的Django项目的静态文件中,在html页面上通过导入该文件即可自动帮我们解决ajax提交post数据时校验csrf_token的问题,(导入该配置文件之前,需要先导入jQuery,因为这个配置文件内的内容是基于jQuery来实现的)
4.csrf相关的装饰器
from django.views.decorators.csrf import csrf_exempt,csrf_protect
# @csrf_exempt # 不校验 csrf
def index(request):
return HttpResponse('index')
@csrf_protect # 校验
def login(request):
return HttpResponse('login')
- 这两个装饰器在CBV上有何异同
# @method_decorator(csrf_exempt,name='post') # csrf_exempt不支持该方法
@method_decorator(csrf_exempt,name='dispatch') # csrf_exempt
class MyIndex(views.View):
# @method_decorator(csrf_exempt) # 可以
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request,*args,**kwargs)
def get(self,request):
return render(request,'transfer.html')
# @method_decorator(csrf_exempt,name='post') # csrf_exempt不支持该方法
def post(self,request):
return HttpResponse('OK')
# csrf_exempt这个装饰器只能给dispatch装才能生效
"""
csrf_protect方式全都可以 跟你普通的装饰器装饰CBV一致
"""
# @method_decorator(csrf_protect,name='post') # 可以
class MyIndex(views.View):
@method_decorator(csrf_protect)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request,*args,**kwargs)
def get(self,request):
return render(request,'transfer.html')
# @method_decorator(csrf_protect) # 可以
def post(self,request):
return HttpResponse('OK')
5.django源码剖析
- django有两个配置文件,一个是暴露给用户可以配置的,一个是内部全局的(用户配置了就用用户的,用户没有配置就用自己的)
- 先加载全局配置 给对象设值;然后加载局部配置 再给对象设值;一旦有重复的项,后者覆盖前者。
from django.conf import global_settings
from django.conf import settings
6.auth模块
使用auth模块的时候,要用就用它的全套
如何创建超级用户(root)
python manage.py createsuperuser
- auth模块常用方法:
1.创建用户
from django.contrib.auth.models import User
# User.objects.create(username=username,password=password) # 不可用 密码不是加密的
# User.objects.create_user(username=username,password=password) # 创建普通用户 密码自动加密
#User.objects.create_superuser(username=username,password=password,email='123@qq.com') # 创建超级用户 需要邮箱数据
2.校验用户名和密码是否正确
from django.contrib import auth
user_obj = auth.authenticate(request,username=username,password=password)
# 必须传用户名和密码两个参数缺一不能
3.保存用户登录状态
auth.login(request,user_obj)
# 只要这句话执行了 后面在任意位置 只要你能拿到request你就可以通过request.user获取到当前登录的用户对象
4.判断当前用户是否登录
request.user.is_authenticated()
5.校验原密码是否正确
request.user.check_password(old_password)
6.修改密码
request.user.set_password(new_password)
request.user.save() # 千万不要忘了
7.注销
auth.logout(request)
8.校验用户是否登录装饰器
from django.contrib.auth.decorators import login_required
局部配置
@login_required(login_url='/login/')
def index(request):
pass
全局配置
settings配置文件中 直接配置
LOGIN_URL = '/login/'
@login_required
def index(request):
pass
# 如果全局配置了 局部也配置 以局部的为准
7.扩展auth_user表字段
方式1
利用一对一外键字段关系
class UserDetail(models.Model):
phone = models.BigIntegerField()
user = models.OneToOneField(to='User')
方式2
利用继承关系
from django.contrib.auth.models import AbstractUser
class Userinfo(AbstractUser):
phone = models.BigIntegerField()
register_time = models.DateField(auto_now_add=True)
# 一定要注意 还需要去配置文件中配置
AUTH_USER_MODEL = 'app01.Userinfo' # 应用名.表名
#之前所有的auth模块功能也全适用于扩展好的表