django:session会话控制

一,session

当用户第一次访问某网站时,网站服务器就在内存中开辟一块空间来创建session对象,用于在“无状态”的HTTP的长会话中存储用户信息。能使用户在同一浏览器的不同页面间跳转时保持信息一致。session会在失效或过期时被清理。主要的原理性内容可以参考网络爬虫基础知识:HTTP和HTTPS、cookie和session这篇博客。

二,django中的session

1,django默认的session设置

django默认开启session,由settings.py中MIDDLEWARE的'django.contrib.sessions.middleware.SessionMiddleware'进行状态判断,并根据判断结果调用settings.py中INSTALLED_APPS的'django.contrib.sessions'进行相关处理。
默认情况下,Django将会话存储在数据库中(使用模型Django.controller.session.models.session),如下:
在这里插入图片描述
尽管这样做很方便,但在某些设置中,将会话数据存储在其他地方会更快,比如可以配置Django将会话数据存储在文件系统或缓存中。通过在settings.py中设置SESSION_ENGINE来使用django提供多种保存session的方式:
1,默认使用数据库保存:

SESSION_ENGINE = 'django.contrib.sessions.backends.db'

2,使用文件保存:

SESSION_ENGINE = 'django.contrib.sessions.backends.file'
SESSION_FILE_PATH = '/MyProject'

3,使用缓存保存:
要使用Django的缓存系统存储会话数据,首先需要确保已经配置了缓存。

SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'default' #设置缓存别名,默认内存缓存
  • 该方式以获取简单的缓存会话存储。 会话数据将直接存储在您的缓存中。 但是,会话数据可能不是持久性的:如果缓存已满或重新启动了缓存服务器,则可以将缓存的数据清除。

4,使用缓存+数据库保存:

SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'
  • 对于持久性缓存数据,请将SESSION_ENGINE设置为“ django.contrib.sessions.backends.cached_db”。 这使用了直写式高速缓存-对高速缓存的每次写入也将被写入数据库。 如果数据不在缓存中,则会话仅使用数据库进行读取。

总之,只有在使用Memcached缓存后端时,才应该使用基于缓存的会话。本地内存缓存后端保存数据的时间不够长,因此不是一个好的选择,直接使用文件或数据库会话比通过文件或数据库缓存后端发送所有内容更快。此外,本地内存缓存后端不是多进程安全的,因此可能不是生产环境的好选择。

5,使用cookie形式保存:

SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'
  • 会话数据将使用Django的用于加密签名和密钥设置的工具进行存储。

6,与session相关的其他配置:

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,默认修改之后才保存(默认)

2,django中session的运行机制

1,以游客身份访问站点而未登录时,在chrome的“检查”中可以发现,当前cookie并没有生成对应的sessionid,说明一般情况下django不会为游客创建session。
2,当登陆之后,sessionid被创建并保存在默认的数据表中:
在这里插入图片描述
3,复制网站地址,用新窗口打开,即在登录状态下再次访问该网址,还是登陆状态,期间,浏览器先将本地cookie发送到django,django获取其中的sessionid后与默认数据表中的session_key进行比较,确定当前用户信息:
在这里插入图片描述

3,session的读写操作

session可理解为从request中获取的一段dict格式的数据。
1,获取session:

获取session中VCode的数据,若VCode不存在则报错:
request.session.['VCode']

获取session中VCode的数据,若VCode不存在则为空:
request.session.get('VCode', '')
request.session.setdefault('VCode', '')

2,向session写入数据:

request.session['VCode'] = VCode

3,删除session中的数据:

del request.session['VCode']

4,获取session键或值

request.session.keys()
request.session.values()

5,获取session_key:

request.session.session_key

简单的session应用,已在django:使用邮箱获取验证码中有所体现:

			# 判断验证码是否已发送
            if not request.session.get('VCode', ''):
                # 发送验证码并将验证码写入session
                button = '重置密码'
                tips = '验证码已发送'
                password = True
                VCodeInfo = True
                VCode = str(random.randint(000000, 999999))
                request.session['VCode'] = VCode
                user[0].email_user('找回密码', VCode)
            # 匹配输入的验证码是否正确
            elif VCode == request.session.get('VCode'):
                # 密码加密处理并保存到数据库
                dj_ps = make_password(p, None, 'pbkdf2_sha256')
                user[0].password = dj_ps
                user[0].save()
                del request.session['VCode']
                tips = '密码已重置'

django中对cookie的简单操作可以看看cookie反爬虫应用与绕过原理

三,session实战:使用session实现商品抢购

1,创建开发环境

使用python3.7与django2.2.14,项目结构如下:
在这里插入图片描述

2,创建商品模型与数据

index/models.py:
from django.db import models

class Product(models.Model):
    id = models.AutoField('序号', primary_key=True)
    name = models.CharField('名称', max_length=50)
    slogan = models.CharField('简介', max_length=50)
    sell = models.CharField('宣传', max_length=50)
    price = models.IntegerField('价格')
    photo = models.CharField('相片', max_length=50)

    # 设置返回值
    def __str__(self):
        return self.name

    class Meta:
        verbose_name = '商品信息'
        verbose_name_plural = verbose_name

迁移数据后,添加商品信息:
在这里插入图片描述

3,模板文件

templates/index.html:
<!DOCTYPE html>
<html>
<head>
    <title>商城首页</title>
    {% load static %}
    <link rel="stylesheet" href="{% static "css/hw_index.css" %}">
</head>
<body>
<div id="top_main">
    <div class="lf" id="my_hw">
        当前用户:{
   
   { user.username }}
    </div>
    <div class="lf" id="settle_up">
        <a href="{% url 'index:order' %}">订单信息</a>
    </div>
</div>
<section id="main">
    <div class="layout">
        <div class="fl u-4-3 lf">
            <ul>
                {% for p in products %}
                <li class="channel-pro-item">
                    <div class="p-img">
                        <img src="{% static p.photo %}">
                    </div>
                    <div class="p-name lf"><a href="#">{
   
   { p.name }}</a></div>
                    <div class="p-shining">
                        <div class="p-slogan">{
   
   { p.slogan }}</div>
                        <div class="p-promotions">{
   
   { p.sell }}</div>
                    </div>
                    <div class="p-price">
                        <em>¥</em><span>{
   
   { p.price }}</span>
                    </div>
                    <div class="p-button lf">
                        <a href="{% url 'index:index' %}?id={
     
     { p.id }}">立即抢购</a>
                    </div>
                </li>
                {% if forloop.counter == 2 %}
                    <div class="hr-2"></div>
                {% endif %}
                {% endfor %}
            </ul>
        </div>
    </div>
</section>
</body>
</html>


templates/order.html:
<!DOCTYPE html>
<html lang="en">
<head>
<title>订单信息</title>
{% load static %}
<link rel="stylesheet" href="{% static 'css/reset.css' %}">
<link rel="stylesheet" href="{% static 'css/carts.css' %}">
<script src="{% static 'js/jquery.min.js' %}"></script>
<script src="{% static 'js/carts.js' %}"></script>
</head>
<body>
<section class="cartMain">
	<div class="cartMain_hd">
		<ul class="order_lists cartTop">
			<li class="list_chk">
				<input type="checkbox" id="all" class="whole_check">
				<label for="all"></label>
				全选
			</li>
			<li class="list_con">商品信息</li>
			<li class="list_price">单价</li>
			<li class="list_amount">数量</li>
			<li class="list_sum">金额</li>
			<li class="list_op">操作</li>
            <li class="list_op"><a href="{% url 'index:index' %}">返回首页</a></li>
		</ul>
	</div>
	<div class="cartBox">
		<div class="shop_info">
			<div class="all_check">
				<!--店铺全选-->
				<input type="checkbox" id="shop_b" class="shopChoice">
				<label for="shop_b" class="shop"></label>
			</div>
			<div class="shop_name">
				店铺:<a href="javascript:;">MyDjango</a>
			</div>
		</div>
		<div class="order_content">
            {% for p in products %}
			<ul class="order_lists">
				<li class="list_chk">
					<input type="checkbox" id="checkbox_4" class="son_check">
					<label for="checkbox_4"></label>
				</li>
				<li class="list_con">
					<div class="list_text"><a href="javascript:;">{
   
   { p.name }}</a></div>
				</li>
				<li class="list_price">
					<p class="price">¥{
   
   { p.price }}</p>
				</li>
				<li class="list_amount">
					<div class="amount_box">
						<a href="javascript:;" class="reduce reSty">-</a>
						<input type="text" value="1" class="sum">
						<a href="javascript:;" class="plus">+</a>
					</div>
				</li>
				<li class="list_sum">
					<p class="sum_price">¥{
   
   { p.price }}</p>
				</li>
				<li class="list_op">
					<p class="del">
                    <a href="{% url 'index:order' %}?id={
     
     { p.id }}" class="delBtn">移除商品</a>
                    </p>
				</li>
			</ul>
			{% endfor %}
		</div>
	</div>
	<!--底部-->
	<div class="bar-wrapper">
		<div class="bar-right">
			<div class="piece">已选商品<strong class="piece_num">0</strong></div>
			<div class="totalMoney">共计: <strong class="total_text">0.00</strong></div>
			<div class="calBtn"><a href="javascript:;">结算</a></div>
		</div>
	</div>
</section>
</body>
</html>

4,功能实现

from django.shortcuts import render, redirect
from .models import Product
from django.contrib.auth.decorators import login_required

# 商品信息展示
@login_required(login_url='/admin/login')
def index(request):
	
	# 用户抢购时,获取商品id
    id = request.GET.get('id', '')
    if id:
        # 获取存储在Session的idList
        idList = request.session.get('idList', [])
        # 判断当前请求参数是否已存储在Session
        if not id in idList:
            # 将商品的主键id存储在idList
            idList.append(id)
        # 更新Session的idList
        request.session['idList'] = idList
        return redirect('/')
    # 查询所有商品信息
    products = Product.objects.all()
    return render(request, 'index.html', locals())

# 订单确认
def orderView(request):
    idList = request.session.get('idList', [])
    del_id = request.GET.get('id', '')
    # 判断是否为空,若非空,删除Session里的商品信息
    if del_id in idList:
        # 删除Session里某个商品的主键
        idList.remove(del_id)
        # 将删除后的数据覆盖原来的Session
        request.session['idList'] = idList
    # 根据idList查询商品的所有信息
    products = Product.objects.filter(id__in=idList)
    return render(request, 'order.html', locals())
  • 在点击立即抢购后,首先在模板里向请求中添加了商品id后,由index路由将请求转发给index进行处理,index将这个id加入idList实现购物车商品添加功能,然后发生了一次刷新当前页面的重定向。
  • 进入订单信息点击移除商品后,在模板里向请求中添加了商品id,由order路由将请求转发给orderView进行处理,orderView将这个id从idList中删除,实现购物车商品删除功能。
  • 订单信息页面中的商品选择、数量统计、总价统计由JavaScript完成。

5,运行

登录后进入商品信息页,就会会产生session信息:
在这里插入图片描述
在这里插入图片描述

点击商品的立即抢购后,会将商品id加入到session的idList中,可以打印出session信息进行查看:
在这里插入图片描述

print(request.session.values())
# 在抢购了两件商品之后:
dict_values(['1', 'django.contrib.auth.backends.ModelBackend', '7f4dfdaf193f6c0f7e62792ab67eb835a0c86168', ['1', '3']])

进入订单界面:
在这里插入图片描述
当选择新窗口登录并打开订单信息界面时,直接跳过了登陆页面,还会发现之前加入的商品都还在,也就是说,我们通过session成功保存了用户相关信息,实现了用户在不同窗口间的信息一致功能。但如果用另一个浏览器进行访问,则还是需要重新登陆,这也是session/cookie原理的体现。

猜你喜欢

转载自blog.csdn.net/dangfulin/article/details/107615827
今日推荐