Session会话管理

Session主要用于身份验证、偏好设置、购物车等场景,Django的session模块提供了一种在服务端存储用户会话状态的机制,它提供了多种存储后端供我们选择,包括基于数据库和基于Redis的Session机制,同时该模块还被设计为了可插拔的模式,如果我们完全不使用它(例如我们仅使用JWT机制)也可以将其关闭。这篇笔记我们对Django的session模块进行简单介绍。

Session机制原理

Django的session模块的实现基于以下几个机制。

会话ID(SessionID):当用户首次访问网站时,Django会为其生成唯一的会话ID,并将其存储在客户端的Cookie中。

存储会话数据:实际的会话数据会被存储在服务端,session模块支持配置数据存储到多种后端,包括数据库、缓存、文件等。

数据访问:在用户的后续请求中,Django会根据附带在请求中Cookie内的会话ID检索存储在服务端的会话数据。

SessionID在浏览器侧的Cookie信息如下图。

具体来说,session模块有关的功能其实都是通过中间件django.contrib.sessions.middleware.SessionMiddleware实现的,默认创建工程时,已经自动帮我们添加这个组件了,如果需要关闭session模块,移除这个中间件即可。

写入和访问Session

下面例子中,我们向Session写入一个布尔类型值。

from django.shortcuts import render


def index(request):
    # 写入Session
    request.session["is_login"] = True
    return render(request, 'index.html')

读取Session也是类似的,直接访问request.session即可。

注意:对于Session的设置有一个暗坑,默认情况下,Session不会每一次请求都更新,仅仅会在Session的内容被修改后,Session中会有一个标识request.session.modified被自动置为True,此时Session才会更新。然而,如果我们对Session中的对象进行“深层”的修改(比如对session['user']对象内的属性进行修改,而不是user这个Session中相对外层的引用),这个标识就不会自动更新!该问题的解决办法有2种:

  1. 手动设置request.session.modified = True,但这会使代码变得冗余。
  2. settings.py中加入配置SESSION_SAVE_EVERY_REQUEST = True,使得每次请求都会更新Session,但如果使用基于数据库的Session会使得数据库压力显著增大,最好配合Redis等中间件使用。

在模板中访问Session

一种常见需求是顶部导航的登陆判断,如果已经登陆显示用户名和用户菜单,如果未登录显示“请登录”,那么此时显然我们需要在模板中访问Session并判断用户是否已经登录。下面是一个例子模板代码,假设用户登录后,我们会将user对象存入Session中,模板中可以如下进行判断。

{% if 'user' in request.session %}
    ...
{% endif %}

Django模板中也实现了类似JSP的内置对象,通过request.session就可以访问Session内容了。

Session的超时和清除

关闭浏览器时清除Session

对于Session,大多数Web框架的处理方式都是关闭浏览器时过期,但注意Django默认不是这样的,我们可以在settings.py中设置如下选项来实现这一配置。

# 关闭浏览器时让Session过期
SESSION_EXPIRE_AT_BROWSER_CLOSE = True

删除Session字段

我们也可以手动删除一个Session中的字段,下面例子中,我们删除了Session中的user字段。

request.session.pop('user')

手动设置Session的超时时间

下面例子中,Session会在5秒后超时。

request.session.set_expiry(5)

注:经过实际测试,这个超时时间似乎不是太准确,即使指定5秒超时,可能还是10秒后才真的起作用。

Session的存储后端

默认Django会在数据库中持久化Session,其表名为django_session,如果需要手动清空服务端的Session,直接清空该表就可以了。但如果对性能有一些要求,我们可能需要将Session存储到Redis、Memcached等分布式缓存系统。

使用Redis存储Session

大量的请求会给数据库造成不小的压力,在有性能要求的场景下,像Redis这种分布式缓存系统存储Session具有高性能、高可用的特点。不过Django默认没有集成Redis的客户端,我们需要先用pip安装Django的Redis插件。

pip install django-redis

此外还需要在settings.py中配置Redis缓存,并指定Session使用Redis缓存作为存储后端。

CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/0",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    }
}

SESSION_ENGINE = "django.contrib.sessions.backends.cache"

LOCATION中配置的是Redis服务器的地址,其中最后的/0指定使用0号数据库。配置好后,我们可以尝试在代码中设置Session。在redis-cli工具中,我们可以看到类似如下的键值对。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。