Django 教程 第 7 部分:会话框架

本教程扩展了我们的 LocalLibrary 网站,在首页添加了一个基于会话的访问计数器。这是一个相对简单的示例,但它确实展示了如何在自己的网站中使用会话框架为匿名用户提供持久行为。

先决条件 完成所有之前的教程主题,包括 Django 教程 第 6 部分:通用列表和详细信息视图
目标 了解如何使用会话。

概述

我们在之前的教程中创建的 LocalLibrary 网站允许用户浏览目录中的书籍和作者。虽然内容是从数据库动态生成的,但每个用户在使用网站时基本上都可以访问相同的页面和信息类型。

在“真正的”图书馆中,您可能希望根据用户之前对网站的使用情况、偏好等为每个用户提供个性化的体验。例如,您可以隐藏用户之前已确认的警告消息,或者存储并尊重他们的偏好(例如,他们希望在每个页面上显示的搜索结果数量)。

会话框架允许您实现这种行为,允许您基于每个网站访问者存储和检索任意数据。

什么是会话?

Web 浏览器和服务器之间所有通信都通过 HTTP 进行,它是无状态的。协议无状态的事实意味着客户端和服务器之间的消息彼此完全独立——没有“序列”的概念,也没有基于先前消息的行为。因此,如果您希望拥有一个跟踪与客户端持续关系的网站,则需要自己实现。

会话是 Django(以及大多数互联网)用于跟踪网站和特定浏览器之间“状态”的机制。会话允许您为每个浏览器存储任意数据,并在浏览器连接到网站时使这些数据可用。然后,与会话关联的单个数据项通过“键”进行引用,该键用于存储和检索数据。

Django 使用包含特殊会话 ID 的 cookie 来识别每个浏览器及其与网站关联的会话。实际的会话数据默认存储在网站数据库中(这比将数据存储在 cookie 中更安全,因为 cookie 更容易受到恶意用户的攻击)。您可以配置 Django 将会话数据存储在其他位置(缓存、文件、“安全” cookie),但默认位置是一个良好且相对安全的选项。

启用会话

当我们 创建网站框架(在教程 2 中)时,会话已自动启用。

配置设置在项目文件(django-locallibrary-tutorial/locallibrary/settings.py)的INSTALLED_APPSMIDDLEWARE 部分中,如下所示

python
INSTALLED_APPS = [
    # …
    'django.contrib.sessions',
    # …

MIDDLEWARE = [
    # …
    'django.contrib.sessions.middleware.SessionMiddleware',
    # …

使用会话

您可以从request 参数(作为视图的第一个参数传入的HttpRequest)中的视图访问session 属性。此会话属性表示与当前用户(或更准确地说,与当前浏览器的连接,由该网站的浏览器 cookie 中的会话 ID 标识)。

session 属性是一个类似字典的对象,您可以在视图中对其进行任意次数的读取和写入,并根据需要对其进行修改。您可以执行所有正常的字典操作,包括清除所有数据、测试键是否存在、循环遍历数据等。但是,大多数情况下,您只会使用标准的“字典”API 来获取和设置值。

下面的代码片段显示了如何使用与当前会话(浏览器)关联的键“my_car”获取、设置和删除一些数据。

注意:Django 的一大优点是您无需在视图中考虑将会话绑定到当前请求的机制。如果我们在视图中使用下面的片段,我们会知道有关my_car的信息仅与发送当前请求的浏览器相关联。

python
# Get a session value by its key (e.g. 'my_car'), raising a KeyError if the key is not present
my_car = request.session['my_car']

# Get a session value, setting a default if it is not present ('mini')
my_car = request.session.get('my_car', 'mini')

# Set a session value
request.session['my_car'] = 'mini'

# Delete a session value
del request.session['my_car']

API 还提供了一些其他方法,这些方法主要用于管理关联的会话 cookie。例如,有一些方法可以测试客户端浏览器是否支持 cookie,设置和检查 cookie 过期日期,以及清除数据存储中已过期的会话。您可以在 如何使用会话(Django 文档)中了解有关完整 API 的信息。

保存会话数据

默认情况下,只有在会话被修改(赋值)或删除时,Django 才会保存到会话数据库并将会话 cookie 发送到客户端。如果您使用其会话键更新某些数据(如上一节所示),则无需担心!例如

python
# This is detected as an update to the session, so session data is saved.
request.session['my_car'] = 'mini'

如果您正在更新会话数据内部的一些信息,那么 Django 将无法识别您已对会话进行了更改并保存数据(例如,如果您要更改“my_car”数据内部的“wheels”数据,如下所示)。在这种情况下,您需要显式标记会话已被修改。

python
# Session object not directly modified, only data within the session. Session changes not saved!
request.session['my_car']['wheels'] = 'alloy'

# Set session as modified to force data updates/cookie to be saved.
request.session.modified = True

注意:您可以更改行为,以便网站在每次请求时更新数据库/发送 cookie,方法是在项目设置(django-locallibrary-tutorial/locallibrary/settings.py)中添加SESSION_SAVE_EVERY_REQUEST = True

简单示例 - 获取访问次数

作为一个简单的现实世界示例,我们将更新我们的库以告知当前用户他们访问了多少次LocalLibrary 首页。

打开/django-locallibrary-tutorial/catalog/views.py,并将包含num_visits 的行添加到index() 中(如下所示)。

python
def index(request):
    # …

    num_authors = Author.objects.count()  # The 'all()' is implied by default.

    # Number of visits to this view, as counted in the session variable.
    num_visits = request.session.get('num_visits', 0)
    num_visits += 1
    request.session['num_visits'] = num_visits

    context = {
        'num_books': num_books,
        'num_instances': num_instances,
        'num_instances_available': num_instances_available,
        'num_authors': num_authors,
        'num_visits': num_visits,
    }

    # Render the HTML template index.html with the data in the context variable.
    return render(request, 'index.html', context=context)

在这里,我们首先获取'num_visits' 会话键的值,如果之前未设置该值,则将其设置为 0。每次收到请求时,我们都会递增该值并将其存储回会话中(以便用户下次访问页面时使用)。然后,num_visits 变量在我们的上下文变量中传递给模板。

注意:我们还可以在这里测试浏览器是否支持 cookie(有关示例,请参阅 如何使用会话)或设计我们的 UI,以便无论是否支持 cookie 都无关紧要。

将以下代码块底部所示的行添加到您的主 HTML 模板(/django-locallibrary-tutorial/catalog/templates/index.html)中“动态内容”部分的底部,以显示num_visits 上下文变量。

django
<h2>Dynamic content</h2>

<p>The library has the following record counts:</p>
<ul>
  <li><strong>Books:</strong> {{ num_books }}</li>
  <li><strong>Copies:</strong> {{ num_instances }}</li>
  <li><strong>Copies available:</strong> {{ num_instances_available }}</li>
  <li><strong>Authors:</strong> {{ num_authors }}</li>
</ul>

<p>
  You have visited this page {{ num_visits }} time{{ num_visits|pluralize }}.
</p>

请注意,我们使用 Django 内置的模板标签 pluralize 在页面被访问多次时添加“s”。

保存更改并重新启动测试服务器。每次刷新页面时,数字都应该更新。

总结

现在您知道使用会话改善与匿名用户交互是多么容易了。

在我们的下一篇文章中,我们将解释身份验证和授权(权限)框架,并向您展示如何支持用户帐户。

另请参阅