一、django请求生命周期图
二、django中间件介绍
1、django默认中间件
django中有七个默认的中间件,我们可以在settings中查看,这里其实中间件也是一个个个的类,我们也可以查看只需要通过from 中间件(除最后一个字段)import 中间件名字
那除了django默认的几个中间件之外,我们也还可以自定义中间件,那如果你想要自定义中间件,接下来就得要知道五个自定义中间件的方法
2、自定义中间件五个方法
1、process_request(self, request)
2、process_response(self, request, response)
3、process_view(self, request,view_func, view_args,view_kwargs)
4、process_template_response(self, request, response)
5、process_exception(self, requet, response)
这五个方法,我们需要掌握前面两个,其他的了解即可,这里我们新建一个自定义名为mymiddleware的文件夹,然后新建一个py文件用来书写自定义中间件
- process_request(掌握)
- urls.py
from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/', views.index), ]
- mymiddleware
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse class Mymd1(MiddlewareMixin): def process_request(self, request): print('我是第一个自定义中间价里面的process_request方法') return HttpResponse('我是第一个中间件返回的HttpResponse对象') class Mymd2(MiddlewareMixin): def process_request(self, request): print('我是第二个自定义中间价里面的process_request方法')
- views.py
def index(request): print('我是视图函数index') return HttpResponse('index')
请求的执行顺序:中间件(注册的顺序)--->视图函数
注意:一旦自定义的中间件中返回了HttpResponse,那么就不会执行视图函数中的HttpResponse
- process_response(掌握)
响应返回的时候会按照配置文件中注册的中间件从下往上的顺序依次执行每一个中间件里面的process_response方法,值得注意的是该方法必须要传两个形参,并且需要将response的形参返回出去(一般带response的形参我们都需要把它返回出去)
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse class Mymd1(MiddlewareMixin): def process_request(self, request): print('我是第一个自定义中间价里面的process_request方法') # return HttpResponse('我是第一个中间件返回的HttpResponse对象') def process_response(self, request, response): print('我是第一个中间件里面的process_response') return response # 注意一定要返回形参response,该response就是后端返回给前端浏览器的响应数据 class Mymd2(MiddlewareMixin): def process_request(self, request): print('我是第二个自定义中间价里面的process_request方法') def process_response(self, request, response): print('我是第二个中间件里面的process_response') #return response # 注意一定要返回形参response,该response就是后端返回给前端浏览器的响应数据
return HttpResponse('2020 你好啊')
注意:如果你内部自己返回了HttpResponse对象 会将原本视图函数中返回给用户浏览器的内容替换你自己的内容
- django同级别返回
补充:不同于django,flask是从底部返回的响应及返回所有的process_response
- process_view(了解)
process_view在路由匹配成功,视图函数执行之前会触发
- process_template_response(了解)
- process_template_response(了解)
三、跨站请求伪造csrf
1、钓鱼网站模拟
- 真正网站
"""day57 URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/1.11/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.conf.urls import url, include 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) """ from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/', views.index), url(r'^transfer/', views.transfer), ]
from django.shortcuts import render, HttpResponse, redirect # Create your views here. def index(request): print('我是视图函数index') def render(): return HttpResponse('你好啊, 我是index里面的render函数') obj = HttpResponse('index') obj.render = render return obj 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')
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> </head> <body> <h2>我是真正的网站</h2> <form action="" method="post"> <p>username:<input type="text" name="username"></p> <p>target_user:<input type="text" name="target_user"></p> <p>money<input type="text" name="money"></p> <input type="submit"> </form> </body> </html>
- 钓鱼网站(端口记得要改一下)
"""ab_fish URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/1.11/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.conf.urls import url, include 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) """ from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^transfer/', views.transfer), ]
from django.shortcuts import render # Create your views here. 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')
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> </head> <body> <h2>我是钓鱼网站</h2> <form action="http://127.0.0.1:8000/transfer/" method="post"> # 地址放的是真正网站的地址 <p>username:<input type="text" name="username"></p> <p>target_user:<input type="text"></p> <input type="text" name="target_user" style="display: none" value="yafeng"> # 加了个隐藏框,设置了默认值 <p>money<input type="text" name="money"></p> <input type="submit"> </form> </body> </html>
2、form表单如何通过csrf校验
还记得之前我们在朝前端发送有关post请求的时候我们都需要把中间件中的有关csrf的哪一行注释掉才可以访问,那么其实这就是因为有csrf_token在做校验,只有校验通过了,才允许我们访问发送请求。对于刚才的问题我们只需要在前端加一行代码{%csrf_token%}就可以把之前注释的哪一行中间件以后就可以不用注释,也可以解决刚才钓鱼网站的问题
<h2>我是真正的网站</h2> <form action="" method="post"> {% csrf_token %} <p>username:<input type="text" name="username"></p> <p>target_user:<input type="text" name="target_user"></p> <p>money<input type="text" name="money"></p> <input type="submit">
3、ajax如何通过csrf校验
<button id="d1">发送ajax请求</button> <script> $('#d1').click(function () { $.ajax({ url:'', type:'post', data:{'username': 'yafeng'}, success: function (data) { alert(data) } }) })
1、第一种方式(自己手写 不推荐)
<button id="d1">发送ajax请求</button> <script> $('#d1').click(function () { $.ajax({ url:'', type:'post', {#data:{'username': 'yafeng'},#} //第一种方式(不推荐) 自己手动获取 data:{'username':'yafeng', 'csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val()}, success: function (data) { alert(data) } }) })
2、第二种方式(模板语法 本地使用方便)
<button id="d1">发送ajax请求</button> <script> $('#d1').click(function () { $.ajax({ url:'', type:'post', {#data:{'username': 'yafeng'},#} //第一种方式(不推荐) 自己手动获取 {#data:{'username':'yafeng', 'csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val()},#} //第二种方式 利用模板语法 data:{'username':'yafeng', 'csrfmiddlewaretoken':'{{ csrf_token }}'}, success: function (data) { alert(data) } }) }) </script>
2、第三种方式(引用外部js文件 推荐使用)
- js文件(不需要你手写,只需要会CV即可)
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 csrfSafeMethod(method) { // these HTTP methods do not require CSRF protection return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); } $.ajaxSetup({ beforeSend: function (xhr, settings) { if (!csrfSafeMethod(settings.type) && !this.crossDomain) { xhr.setRequestHeader("X-CSRFToken", csrftoken); } } });
<button id="d1">发送ajax请求</button> {% load static %} <script src="{% static 'myset.js' %}"></script>//外部引用js <script> $('#d1').click(function () { $.ajax({ url:'', type:'post', {#data:{'username': 'yafeng'},#} //第一种方式(不推荐) 自己手动获取 {#data:{'username':'yafeng', 'csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val()},#} //第二种方式 利用模板语法 {#data:{'username':'yafeng', 'csrfmiddlewaretoken':'{{ csrf_token }}'},#} // 第三种 通用方式 引入外部js文件 data:{'username':'yafeng'}, //只需要自己写数据 success: function (data) { alert(data) } }) }) </script>
四、csrf 相关装饰器
比如:当我们网站整体都校验csrf的时候,我想让某几个视图函数不校验
当我们网站整体都不校验csrf的时候,我想让某几个视图函数检验
在校验之前我们需要先导入模块
1、视图函数
from django.views.decorators.csrf import csrf_exempt, csrf_protect
# csrf装饰器在使用之前需要先导入 csrf_exempt不校验 csrf_protect校验
from django.views.decorators.csrf import csrf_exempt, csrf_protect
@csrf_exempt # 这句话意思是不校验index的csrf def index(request): print('我是视图函数index') def render(): return HttpResponse('你好啊, 我是index里面的render函数') obj = HttpResponse('index') obj.render = render return obj
2、针对CBV
from django.views import View from django.utils.decorators import method_decorator # 要想给CBV加装饰器的使用method_decorator它之后才能加装饰器 # @method_decorator(csrf_protect, name='post') # 第二种 指名道姓的给类中的某个方法装 # @method_decorator(csrf_exempt, name='post') # 第二种 不行 @method_decorator(csrf_exempt, name='dispatch') # 可以 类似于第三种 class Myhome(View): # @method_decorator(csrf_protect) # 第三种 类中所有的方法都装上 @method_decorator(csrf_exempt) # 第三种csrf_exempt 可以 def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs) def get(self, request): return HttpResponse('get') # @method_decorator(csrf_protect) # 第一种装饰 @method_decorator(csrf_exempt) # 第一种 不行 def post(self, request): return HttpResponse('post')
3、总结:
给CBV加装饰器,推荐你使用模块method_decorator,我们自己写的装饰器和csrf_protect用法一样,只有csrf_exempt是例外的,它只有在给dispatch装饰是才起作用
五、auth模块
auth是django用户相关自带的功能模块, 它需要依赖一张auth_user表
先要执行数据库迁移命令,迁移完成之后我们可以看见有如下的表,下面我们研究auth_user表
1、命令行创建超级用户(不需要写邮箱)
好了以后刷新auth_user表发现多了一个超级用户
加下来就可以去登录管理员界面啦
2、基于auth_user做注册功能
三种创建方式:
1、create 创建出来的密码是明文(不推荐使用)
2、create_user 创建普通用户(推荐使用)
3、create_superuser 创建超级用户(也不推荐使用)
如何找到auth_user表:
from django.contrib import auth
from django.contrib.auth.models import User # 找到auth_user表
"""day57 URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/1.11/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.conf.urls import url, include 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) """ from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/', views.index), url(r'^transfer/', views.transfer), url(r'^home/', views.Myhome.as_view()), url(r'^reg/', views.register), ]
from django.contrib import auth from django.contrib.auth.models import User # 找到auth_user表 def register(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') # 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') # 注意:这里如果要通过逻辑代码来创建而不是命令行创建超级用户,那么你必须要传邮箱不然会报错,而且创建超级用户也不用该是你来创建,所以我们只用第二种 return render(request, 'register.html')
1、create
2、create_user
3、create_superuser(代码创建必须给它传邮箱字段)
3、基于auth_user校验用户名与密码以及保存功能
def login(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') # 数据库校验用户名和密码是否正确 res = auth.authenticate(username=username, password=password)
# 用户名和密码两个参数一个都不能少 # print(res) # 是一个用户对象 # print(res.username) # print(res.password) ''' 用户名和密码正确返回的是用户对象 错误返回none ''' if res: # 保存用户登录状态 # 之前我们是用request.session, 现在使用auth模块不需要我们自己操作 auth.login(request, res) return HttpResponse('登录成功') ''' 只要执行了这一句话 之后在任何可以获取到request对象的地方都可以通request.user获取当前登录 的用户对象 ''' return render(request, 'login.html')
4、基于auth_user判断用户是否登录
def get_user(request): print(request.user) ''' 用户登录成功之后 request.user拿到的就是用户对象 没有登录 获取到的就是匿名用户 ''' print(request.user.is_authenticated()) # 判断用户是否登录,true表示登录,false表示未登录 return HttpResponse('get_user')
5、基于auth_user校验用户是否登录
局部配置
@login_required(login_url='/login/') # 局部自己指定跳转到那个页面 def xxx(request): return HttpResponse('xxx页面')
全局配置(去settings中写以下代码)
LOGIN_URL = '/login/' # 全局配置登录
@login_required # 全局配置好之后只需加上@login_required 并不需要去指定 def xxx(request): return HttpResponse('xxx页面')
如果两个都设置了,优先执行局部配置
6、基于auth_user修改密码
@login_required def set_password(request): if request.method == 'POST': old_password = request.POST.get('old_password') new_password = request.POST.get('new_password') # 1、先校验原密码是否正确 is_right = request.user.check_password(old_password) # 有个返回值是否正确 # print(is_right) # 2、再去修改密码 if is_right: request.user.set_password(new_password) request.user.save() # 注意修改之后一定要保存,不然没效果数据库没变化 return render(request, 'set_password.html')
7、基于auth_user注销功能
@login_required def logout(request): auth.logout(request) return HttpResponse('注销啦')
六、基于auth模块扩展表
思路:1、利用一对一表关系(了解)
2、利用类的继承(推荐)
1、利用类的继承去扩展表
前期准备,换一个数据库,之前的数据库已经被迁移过啦,这里换成mysql,去自定义字段
然后去settings中配置告诉django用我的自己的表
- models.py
from django.db import models from django.contrib.auth.models import User, AbstractUser # Create your models here. class Userinfo(AbstractUser): phone = models.BigIntegerField() avatar = models.FileField() # 扩展的字段 尽量不要与原先表中的字段冲突
- 配置文件
AUTH_USER_MODEL = '应用名.表名'
- 迁移数据