Django(5) session登录注销、csrf及中间件自定义、django Form表单验证(非常好用)

时间:2024-03-29 11:36:20

一、Django中默认支持Session,其内部提供了5种类型的Session供开发者使用:

  • 数据库(默认)
  • 缓存
  • 文件
  • 缓存+数据库
  • 加密cookie

1、数据库Session

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。
a. 配置 settings.py
    SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)
     
    SESSION_COOKIE_NAME = "sessionid"                       # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
    SESSION_COOKIE_PATH = "/"                               # Session的cookie保存的路径(默认)
    SESSION_COOKIE_DOMAIN = None                             # Session的cookie保存的域名(默认)
    SESSION_COOKIE_SECURE = False                            # 是否Https传输cookie(默认)
    SESSION_COOKIE_HTTPONLY = True                           # 是否Session的cookie只支持http传输(默认)
    SESSION_COOKIE_AGE = 1209600                             # Session的cookie失效日期(2周)(默认)
    SESSION_EXPIRE_AT_BROWSER_CLOSE = False                  # 是否关闭浏览器使得Session过期(默认)
    SESSION_SAVE_EVERY_REQUEST = False                       #为True时,不再点击页面后,相应时间后session失效,为False,第一次点击后,
    相应时间后失效 是否每次请求都保存Session,默认修改之后才保存(默认
b. 使用
    def index(request):
        # 获取、设置、删除Session中数据
        request.session['k1']
        request.session.get('k1',None)
        request.session['k1'= 123
        request.session.setdefault('k1',123# 存在则不设置
        del request.session['k1']
        # 所有 键、值、键值对
        request.session.keys()  所有键
        request.session.values() 所有值
        request.session.items() 所有键值对
        request.session.iterkeys()
        request.session.itervalues()
        request.session.iteritems()
        # 生成用户session的随机字符串
        request.session.session_key
        # 将所有Session失效日期小于当前日期的数据删除
        request.session.clear_expired()
        # 检查 用户session的随机字符串 在数据库中是否
        request.session.exists("session_key")
        # 删除当前用户的所有Session数据
        request.session.delete("session_key")
        request.session.set_expiry(value)#设置超时时间,默认是两周
            * 如果value是个整数,session会在些秒数后失效。
            * 如果value是个datatime或timedelta,session就会在这个时间后失效。
            * 如果value是0,用户关闭浏览器session就会失效。
            * 如果value是None,session会依赖全局session失效策略。

 

2、缓存Session

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
a. 配置 settings.py
            修改SESSION_ENGINE
    SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
    SESSION_CACHE_ALIAS = 'default'                            # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
    SESSION_COOKIE_NAME = "sessionid"                        # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
    SESSION_COOKIE_PATH = "/"                                # Session的cookie保存的路径
    SESSION_COOKIE_DOMAIN = None                              # Session的cookie保存的域名
    SESSION_COOKIE_SECURE = False                             # 是否Https传输cookie
    SESSION_COOKIE_HTTPONLY = True                            # 是否Session的cookie只支持http传输
    SESSION_COOKIE_AGE = 1209600                              # Session的cookie失效日期(2周)
    SESSION_EXPIRE_AT_BROWSER_CLOSE = False                   # 是否关闭浏览器使得Session过期
    SESSION_SAVE_EVERY_REQUEST = True                       # 是否每次请求都保存Session,默认修改之后才保存
b. 使用
    同上

3、文件Session

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
a. 配置 settings.py
    SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
    SESSION_FILE_PATH = None                                    # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir()                                                            # 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T
    SESSION_COOKIE_NAME = "sessionid"                          # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
    SESSION_COOKIE_PATH = "/"                                  # Session的cookie保存的路径
    SESSION_COOKIE_DOMAIN = None                                # Session的cookie保存的域名
    SESSION_COOKIE_SECURE = False                               # 是否Https传输cookie
    SESSION_COOKIE_HTTPONLY = True                              # 是否Session的cookie只支持http传输
    SESSION_COOKIE_AGE = 1209600                                # Session的cookie失效日期(2周)
    SESSION_EXPIRE_AT_BROWSER_CLOSE = False                     # 是否关闭浏览器使得Session过期
    SESSION_SAVE_EVERY_REQUEST = False                          # 是否每次请求都保存Session,默认修改之后才保存
b. 使用
    同上

4、缓存+数据库Session

1
2
3
4
5
6
7
8
9
数据库用于做持久化,缓存用于提高效率
a. 配置 settings.py
    SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'        # 引擎
b. 使用
    同上

5、加密cookie Session

1
2
3
4
5
6
7
a. 配置 settings.py
     
    SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'   # 引擎
b. 使用
    同上

二、session应用示例:用户登录验证与用户注销

urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/$', views.login),
    url(r'^index/$', views.index),
    url(r'^logout/$', views.logout),

]

views.py

from django.shortcuts import render,redirect,HttpResponse

def login(request):
    if request.method == "GET":
        return render(request,'login.html')
    elif request.method == "POST":
        user = request.POST.get('user')
        pwd = request.POST.get('pwd')
        if user == 'root' and pwd == "123":如果验证成功,则
            # session中设置值,其中session是一个字典
            request.session['username'] = user
            request.session['is_login'] = True#设置是否登录标记
            if request.POST.get('rmb',None) == '1':#判断界面是否选择10秒免登陆,如果是10秒免登陆,则设置超时时间为10秒
                #即10秒后需重新登录
                request.session.set_expiry(10)# 超时时间为10秒
            return redirect('/index/')
        else:
            return render(request,'login.html')

def index(request):
    #获取session中的值
    if request.session.get('is_login',None):#如果能获取到已经登录标记,则跳转到数据显示界面
        return render(request,'index.html',{'username': request.session['username']})
    else:#否则跳转到错误界面
        return HttpResponse('gun')

def logout(request):#注销,清除session即可
    # del request.session['username'] 清除某一个用户的session
    request.session.clear() #清空所有的session
    return redirect('/login/')

login.html

<body>
    <form action="/login/" method="POST">
        {% csrf_token %}
        <input type="text" name="user" />
        <input type="text" name="pwd" />
        <input type="checkbox" name="rmb" value="1" /> 10秒免登录
        <input type="submit" value="提交" />
    </form>
</body>

三、在不注释settings.py中的csrf时,要用ajax提交的话,必须在提交前用 $.ajaxSetup

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <form action="/login/" method="POST">
        {% csrf_token %}
        <input type="text" name="user" />
        <input type="text" name="pwd" />
        <input type="checkbox" name="rmb" value="1" /> 10秒免登录
        <input type="submit" value="提交" />
        <input id="btn1" type="button" value="按钮1" />
        <input id="btn2" type="button" value="按钮2" />
    </form>

    <script src="/static/jquery-1.12.4.js"></script>
    <script src="/static/jquery.cookie.js"></script>
    <script>
        $(function(){
            //在不注释settings.py中的csrf时,要用ajax提交,必须在提交前用下边的$.ajaxSetup,这样无论下边有多少ajax操作,
            //只写这一个$.ajaxSetup即可
            $.ajaxSetup({
                beforeSend: function(xhr,settings){  //括号里的都是固定格式
                    xhr.setRequestHeader('X-CSRFtoken', $.cookie('csrftoken')); //固定格式,设置csrftoken
                }
            });

            $('#btn1').click(function () {
                $.ajax({
                    url: '/login/',
                    type:"GET",
                    data: {'user': 'root', 'pwd': '123'},
                    // headers: {'X-CSRFtoken': $.cookie('csrftoken')},
                    success:function(arg){

                    }
                })
            });
        })
    </script>
</body>
</html>

views.py 同二

urls.py  同二

 四、Django中间件

settings.py

MIDDLEWARE = [
    # 'django.middleware.cache.UpdateCacheMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
     'Middle.m1.Row1',#写上自定义中间件函数的路径,.为分隔符
     'Middle.m1.Row2',
     'Middle.m1.Row3',

]

自定义中间件函数(自己创建一个目录:项目/Middle/m1.py)

m1.py

from django.utils.deprecation import MiddlewareMixin

class Row1(MiddlewareMixin):#自定义中间件
    def process_request(self,request):  #注意,中间件的函数名必须为process_request、process_view、process_response,而且process_request
                                         #不能有返回值,如果有返回值,请求就不继续往下一个中间件走了,就会沿着已经走过的中间件的
                                         # process_response返回                                        #正常情况下,数据流程:数据先到Row1的process_request,再到Row2的process_request,再到Row1的process_view,                                        #再到Row2的process_view,再到Row2的process_response、再到Row1的process_response,最后再执行view.py中                                        #的函数。

        print('王森')

    def process_view(self, request, view_func, view_func_args, view_func_kwargs):
        print('张欣彤')

    def process_response(self, request, response):
        print('扛把子')
        return response

from django.shortcuts import HttpResponse
class Row2(MiddlewareMixin):
    def process_request(self,request):
        print('程毅强')
        # return HttpResponse('走')

    def process_view(self, request, view_func, view_func_args, view_func_kwargs):
        print('张需要')

    def process_response(self, request, response):
        print('侯雅凡')
        return response

class Row3(MiddlewareMixin):
    def process_request(self,request):
        print('刘东')

    def process_view(self, request, view_func, view_func_args, view_func_kwargs):
        print('邵林')

    def process_response(self, request, response):
        print('连之泪')
        return response

    def process_exception(self, request, exception):
        if isinstance(exception,ValueError):
            return HttpResponse('出现异常》。。')

    def process_template_response(self,request,response):
        # 如果Views中的函数返回的对象中,具有render方法
        print('-----------------------')
        return response

五、django Form表单验证

1.最基本的Form表单验证及错误信息返回

views.py

from django import forms
from django.forms import widgets
from django.forms import fields
from django.shortcuts import render
from app01 import models

class FM(forms.Form):
    # 字段本身只做验证
    user = fields.CharField(
        error_messages={'required': '用户名不能为空.'},#自定义错误信息
        label="用户名",
        )
    pwd = fields.CharField(
        max_length=12,  #指定密码长度最大为12
        min_length=6,   #指定密码长度最小为6
        error_messages={'required': '密码不能为空.', 'min_length': '密码长度不能小于6', "max_length": '密码长度不能大于12'},
    )
    email = fields.EmailField(error_messages={'required': '邮箱不能为空.','invalid':"邮箱格式错误"})

def fm(request):
    if request.method == "GET":
        obj = FM()#实例化自定义表单
        return render(request,'fm.html',{'obj': obj})#这里将obj传给前端,实际上是将html标签传到前端,前端不用再写html标签
    elif request.method == "POST":
        # 获取用户所有数据
        # 每条数据请求的验证
        # 成功:获取所有的正确的信息
        # 失败:显示错误信息
        obj = FM(request.POST)#获取以post方式提交的数据,包含正确和错误信息
        r1 = obj.is_valid()#如果正确获得全部数据,则返回true,否则返回false
        if r1:
            # obj.cleaned_data  获取到前端输入数据,形式是字典形式
            models.UserInf.objects.create(**obj.cleaned_data) #直接将获取到的字典数据写到数据库里即可
        else:
            # obj.errors继承自字典ErrorDict
            # print(obj.errors.as_json())  以json形式获取数据,得到字典数据
            # print(obj.errors['user'][0]) 获取到用户名 的第一个错误信息
            return render(request,'fm.html', {'obj': obj})#这里将错误信息传到前端
        return render(request,'fm.html')

fm.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <form action="/fm/" method="POST">
        {% csrf_token %}
        <p> {{ obj.user }} {{ obj.errors.user.0 }}</p>  //{{ obj.user }}是输入用户名的标签;{{ obj.errors.user.0 }}是要显示的错误信息
        <p>{{ obj.pwd }} {{ obj.errors.pwd.0 }}</p>
        <p>{{ obj.email }}{{ obj.errors.email.0 }}</p>
        <input type="submit" value="提交" />
    </form>
</body>
</html>

2.完整Form表单验证实例

views.py

from django import forms
from django.forms import widgets
from django.forms import fields
class FM(forms.Form):
    # 字段本身只做验证
    user = fields.CharField(  #字段:自定义输入框要用fields
        error_messages={'required': '用户名不能为空.'},
        label="用户名",#自定义前端标签名
        widget=widgets.Textarea(attrs={'class': 'c1'}),#插件:自定义输入框的样式要用widget
        )
    pwd = fields.CharField(
        max_length=12,
        min_length=6,
        error_messages={'required': '密码不能为空.', 'min_length': '密码长度不能小于6', "max_length": '密码长度不能大于12'},
        widget=widgets.PasswordInput(attrs={'class': 'c2'})#插件:自定义输入框的样式
    )
    email = fields.EmailField(error_messages={'required': '邮箱不能为空.','invalid':"邮箱格式错误"})

    f = fields.FileField()#上传文件,在下边的fm函数获取文件时,f就是字典的key

    # p = fields.FilePathField(path='app01') #显示出app01目录下的所有文件路径,注意是路径

    city1 = fields.ChoiceField( #显示select下拉框
        choices=[(0,'上海'),(1,'广州'),(2,'东莞')]
    )
    city2 = fields.MultipleChoiceField( #显示select多选框
        choices=[(0,'上海'),(1,'广州'),(2,'东莞')]
    )

from app01 import models
def fm(request):
    if request.method == "GET":
        # 从数据库中把数据获取到所需要的数据
        dic = {#给各个字段设置默认值(也可以用于在页面做编辑或添加操作时,显示默认值)
            "user": 'r1',
            ',
            'email': 'sdfsd',
            'city1': 1,
            'city2': [1,2]
        }
        obj = FM(initial=dic)#给各个字段设置默认值
        return render(request,'fm.html',{'obj': obj})
    elif request.method == "POST":
        # 获取用户所有数据
        # 每条数据请求的验证
        # 成功:获取所有的正确的信息
        # 失败:显示错误信息
        obj = FM(request.POST)
        r1 = obj.is_valid()
        if r1:
            # obj.cleaned_data
            models.UserInf.objects.create(**obj.cleaned_data)
        else:
            # ErrorDict
            # print(obj.errors.as_json())
            # print(obj.errors['user'][0])
            return render(request,'fm.html', {'obj': obj})
        return render(request,'fm.html')

fm.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <form action="/fm/" method="POST">
        {% csrf_token %}#中间件插件
        <p>{{ obj.user.label }} {{ obj.user }} {{ obj.errors.user.0 }}</p>
         #{{ obj.user.label }}显示标签名 {{ obj.user }}显示输入框 {{ obj.errors.user.0 }}显示错误信息
        <p>{{ obj.pwd }} {{ obj.errors.pwd.0 }}</p>
        <p>{{ obj.email }}{{ obj.errors.email.0 }}</p>
        <p>{{ obj.f }}{{ obj.errors.f.0 }}</p> #显示上传文件选择框
        <p>{{ obj.p }}{{ obj.errors.p.0 }}</p> #以下拉框的形式,显示app01目录下的所有文件的路径
        {{ obj.city1 }}  #显示select下拉框
        {{ obj.city2 }}  #显示select多选框
        <input type="submit" value="提交" />
    </form>
</body>
</html>

 附加:从数据库取choice选项

在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段 ***当数据库数据发生变化时,无法实时获取最新数据***,那么需要自定义构造方法从而达到此目的。

方式一:

from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.validators import RegexValidator

class MyForm(Form):

    user = fields.ChoiceField(#创建一个输入用户名的user框
        # choices=((1, '上海'), (2, '北京'),),
        initial=2,
        widget=widgets.Select(choices=[])
    )

    def __init__(self, *args, **kwargs):
        super(MyForm,self).__init__(*args, **kwargs)
        self.fields['user'].widget.choices = models.Classes.objects.all().value_list('id','caption')

3.常用的Form表单字段及其用法(视频22天--19、20)

自定义widget样式要用attr参数: widget=widgets.Textarea(attrs={'class': 'c1'}),

用的Form表单字段及其用法