Django 教程第 7 部分:會話框架

本教程擴充套件了我們的 LocalLibrary 網站,為主頁添加了一個基於會話的訪問計數器。這是一個相對簡單的示例,但它確實展示瞭如何在自己的站點中使用會話框架為匿名使用者提供持久行為。

預備知識 完成所有之前的教程主題,包括 Django 教程第 6 部分:通用列表和詳情檢視
目標 瞭解會話的使用方式。

概述

我們在之前的教程中建立的 LocalLibrary 網站允許使用者瀏覽目錄中的書籍和作者。雖然內容是從資料庫動態生成的,但每個使用者在使用該網站時都將基本上訪問相同的頁面和資訊型別。

在一個“真實”的圖書館中,您可能希望根據使用者之前對網站的使用、偏好等為他們提供定製化的體驗。例如,您可以隱藏使用者之前確認過的警告訊息,以便下次他們訪問網站時不再顯示,或者儲存並尊重他們的偏好(例如,他們希望每頁顯示多少搜尋結果)。

會話框架允許您實現這種行為,從而可以按每個網站訪問者儲存和檢索任意資料。

什麼是會話?

Web 瀏覽器和伺服器之間的所有通訊都透過 HTTP 進行,而 HTTP 是**無狀態**的。協議無狀態意味著客戶端和伺服器之間的訊息是完全獨立的——沒有“序列”或基於先前訊息的行為的概念。因此,如果您希望擁有一個能夠跟蹤與客戶端持續關係的網站,則需要自己實現。

會話是 Django(以及大多數網際網路)用於跟蹤站點和特定瀏覽器之間“狀態”的機制。會話允許您為每個瀏覽器儲存任意資料,並使這些資料在瀏覽器連線時可供站點使用。與會話關聯的單個數據項隨後透過一個“鍵”進行引用,該鍵用於儲存和檢索資料。

Django 使用包含特殊**會話 ID** 的 Cookie 來識別每個瀏覽器及其與站點關聯的會話。實際的會話**資料**預設儲存在站點資料庫中(這比將資料儲存在 Cookie 中更安全,因為 Cookie 更容易受到惡意使用者的攻擊)。您可以將 Django 配置為將會話資料儲存在其他位置(快取、檔案、“安全”Cookie),但預設位置是一個良好且相對安全的選項。

啟用會話

當我們建立骨架網站時(在教程 2 中),會話已自動啟用。

配置設定在專案檔案(**django-locallibrary-tutorial/locallibrary/settings.py**)的 `INSTALLED_APPS` 和 `MIDDLEWARE` 部分,如下所示:

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

**注意:** 您可以透過在專案設定(**django-locallibrary-tutorial/locallibrary/settings.py**)中新增 `SESSION_SAVE_EVERY_REQUEST = True` 來更改行為,使站點在每個請求時更新資料庫/傳送 cookie。

簡單示例 — 獲取訪問次數

作為一個簡單的實際示例,我們將更新我們的圖書館,以告知當前使用者他們訪問 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”。

儲存更改並重新啟動測試伺服器。每次重新整理頁面時,數字都應該更新。

總結

您現在知道使用會話來改善與**匿名**使用者的互動是多麼容易。

在我們的下一篇文章中,我們將解釋身份驗證和授權(許可權)框架,並向您展示如何支援使用者帳戶。

另見