Django 教程第五部分:建立我們的主頁

我們現在準備新增程式碼,以顯示我們的第一個完整頁面——一個用於 LocalLibrary 網站的主頁。主頁將顯示每種模型型別的記錄數量,並提供側邊欄導航連結到我們的其他頁面。在此過程中,我們將獲得編寫基本 URL 對映和檢視、從資料庫獲取記錄以及使用模板的實踐經驗。

預備知識 閱讀 Django 簡介。完成之前的教程主題(包括 Django 教程第四部分:Django 管理站點)。
目標 學習建立簡單的 URL 對映和檢視(URL 中沒有編碼資料),從模型中獲取資料,並建立模板。

概述

在定義了模型並建立了一些初始庫記錄之後,是時候編寫程式碼來向用戶展示這些資訊了。我們需要做的第一件事是確定我們想要在頁面中顯示哪些資訊,並定義用於返回這些資源的 URL。然後,我們將建立 URL 對映器、檢視和模板來顯示頁面。

以下圖表描述了主資料流以及處理 HTTP 請求和響應時所需的元件。由於我們已經實現了模型,我們將建立的主要元件是:

  • URL 對映器,用於將支援的 URL(以及 URL 中編碼的任何資訊)轉發到適當的檢視函式。
  • 檢視函式,用於從模型中獲取請求的資料,建立顯示資料的 HTML 頁面,並將頁面返回給使用者以在瀏覽器中檢視。
  • 在檢視中渲染資料時使用的模板。

Main data flow diagram: URL, Model, View & Template component required when handling HTTP requests and responses in a Django application. A HTTP request hits a Django server gets forwarded to the 'urls.py' file of the URLS component. The request is forwarded to the appropriate view. The view can read and write data from the Models 'models.py' file containing the code related to models. The view also accesses the HTML file template component. The view returns the response back to the user.

正如您在下一節中看到的,我們有 5 個頁面要顯示,這對於一篇單獨的文章來說資訊量太大了。因此,本文將重點介紹如何實現主頁,我們將在後續文章中介紹其他頁面。這應該能讓您對 URL 對映器、檢視和模型在實踐中如何工作有一個良好的端到端理解。

定義資源 URL

由於此版本的 LocalLibrary 對終端使用者來說基本上是隻讀的,我們只需要為網站提供一個登入頁(主頁),以及用於顯示圖書和作者的列表和詳細檢視的頁面。

我們的頁面所需的 URL 是:

  • catalog/ — 主頁(索引)頁。
  • catalog/books/ — 所有圖書的列表。
  • catalog/authors/ — 所有作者的列表。
  • catalog/book/<id> — 特定圖書的詳細檢視,其主鍵欄位為 <id>(預設)。例如,新增到列表中的第三本書的 URL 將是 /catalog/book/3
  • catalog/author/<id> — 具有主鍵欄位 <id> 的特定作者的詳細檢視。例如,新增到列表中的第 11 位作者的 URL 將是 /catalog/author/11

前三個 URL 將返回索引頁、圖書列表和作者列表。這些 URL 不編碼任何額外資訊,從資料庫獲取資料的查詢將始終相同。但是,查詢返回的結果將取決於資料庫的內容。

相比之下,最後兩個 URL 將顯示特定圖書或作者的詳細資訊。這些 URL 編碼了要顯示的專案標識(如上所示,用 <id> 表示)。URL 對映器將提取編碼資訊並將其傳遞給檢視,檢視將動態地確定從資料庫獲取哪些資訊。透過在 URL 中編碼資訊,我們將使用一套 URL 對映、一個檢視和一個模板來處理所有圖書(或作者)。

注意:使用 Django,您可以根據需要構建您的 URL——您可以在 URL 正文中編碼資訊,如上所示,或者在 URL 中包含 GET 引數,例如 /book/?id=6。無論您使用哪種方法,URL 都應保持清晰、邏輯和可讀,正如 W3C 所建議的。Django 文件建議在 URL 正文中編碼資訊以實現更好的 URL 設計。

正如概述中提到的,本文的其餘部分將描述如何構建索引頁。

建立索引頁

我們將建立的第一個頁面是索引頁(catalog/)。索引頁將包含一些靜態 HTML,以及資料庫中不同記錄的生成“計數”。為了實現這一點,我們將建立一個 URL 對映、一個檢視和一個模板。

注意:本節值得多加關注。大部分資訊也適用於我們將建立的其他頁面。

URL 對映

當我們建立 骨架網站 時,我們更新了 locallibrary/urls.py 檔案,以確保每當收到以 catalog/ 開頭的 URL 時,URLConf 模組 catalog.urls 將處理剩餘的子字串。

以下來自 locallibrary/urls.py 的程式碼片段包含了 catalog.urls 模組:

python
urlpatterns += [
    path('catalog/', include('catalog.urls')),
]

注意:每當 Django 遇到匯入函式 django.urls.include() 時,它會根據指定的結束字元分割 URL 字串,並將剩餘的子字串傳送到包含的 URLConf 模組進行進一步處理。

我們還為 URLConf 模組建立了一個名為 /catalog/urls.py 的佔位符檔案。將以下行新增到該檔案:

python
urlpatterns = [
    path('', views.index, name='index'),
]

path() 函式定義了以下內容:

  • 一個 URL 模式,一個空字串:''。我們將在處理其他檢視時詳細討論 URL 模式。
  • 如果檢測到 URL 模式,將呼叫的檢視函式:views.index,它是 views.py 檔案中名為 index() 的函式。

path() 函式還指定了一個 name 引數,它是特定 URL 對映的唯一識別符號。您可以使用該名稱“反轉”對映器,即動態建立指向對映器旨在處理的資源的 URL。例如,我們可以透過在模板中新增以下連結來使用 name 引數從任何其他頁面連結到我們的主頁:

django
<a href="{% url 'index' %}">Home</a>.

注意:我們可以像 <a href="/catalog/">Home</a> 那樣硬編碼連結,但如果我們將主頁的模式更改為,例如 /catalog/index,模板將不再正確連結。使用反轉 URL 對映更加健壯。

檢視(基於函式)

檢視是一個函式,它處理 HTTP 請求,從資料庫中獲取所需資料,使用 HTML 模板在 HTML 頁面中渲染資料,然後將生成的 HTML 以 HTTP 響應的形式返回,以向用戶顯示頁面。索引檢視遵循此模型——它獲取資料庫中 BookBookInstance、可用 BookInstanceAuthor 記錄數量的資訊,並將該資訊傳遞給模板以供顯示。

開啟 catalog/views.py 並注意該檔案已匯入 render() 快捷函式,用於使用模板和資料生成 HTML 檔案:

python
from django.shortcuts import render

# Create your views here.

將以下行貼上到檔案底部:

python
from .models import Book, Author, BookInstance, Genre

def index(request):
    """View function for home page of site."""

    # Generate counts of some of the main objects
    num_books = Book.objects.all().count()
    num_instances = BookInstance.objects.all().count()

    # Available books (status = 'a')
    num_instances_available = BookInstance.objects.filter(status__exact='a').count()

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

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

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

第一行匯入了我們將在所有檢視中用於訪問資料的模型類。

檢視函式的第一部分使用模型類上的 objects.all() 屬性獲取記錄數量。它還獲取狀態欄位中值為“a”(可用)的 BookInstance 物件列表。您可以在我們之前的教程 Django 教程第三部分:使用模型 > 搜尋記錄 中找到有關如何訪問模型資料的更多資訊。

在檢視函式末尾,我們呼叫 render() 函式來建立 HTML 頁面並以響應形式返回該頁面。此快捷函式封裝了許多其他函式,以簡化一個非常常見的用例。render() 函式接受以下引數:

  • 原始的 request 物件,這是一個 HttpRequest
  • 帶有資料佔位符的 HTML 模板。
  • 一個 context 變數,它是一個 Python 字典,包含要插入到佔位符中的資料。

我們將在下一節詳細討論模板和 context 變數。讓我們開始建立模板,以便我們能夠實際向用戶顯示一些內容!

模板

模板是一個文字檔案,它定義了檔案(例如 HTML 頁面)的結構或佈局,它使用佔位符來表示實際內容。

使用 startapp 建立的 Django 應用程式(例如本示例的骨架)將在應用程式的名為“templates”的子目錄中查詢模板。例如,在我們剛剛新增的索引檢視中,render() 函式將期望在 /django-locallibrary-tutorial/catalog/templates/ 中找到檔案 index.html,如果檔案不存在,將引發錯誤。

您可以透過儲存之前的更改並在瀏覽器中訪問 127.0.0.1:8000 來檢查這一點——它將顯示一個相當直觀的錯誤訊息:“TemplateDoesNotExist at /catalog/”,以及其他詳細資訊。

注意:根據您的專案設定檔案,Django 將在多個位置查詢模板,預設情況下在您已安裝的應用程式中搜索。您可以在 Django 文件的模板部分 中瞭解有關 Django 如何查詢模板以及它支援哪些模板格式的更多資訊。

擴充套件模板

索引模板將需要用於頭部和主體的標準 HTML 標記,以及連結到網站其他頁面(我們尚未建立)的導航部分,以及顯示介紹性文字和圖書資料的部分。

我們網站的每個頁面中的大部分 HTML 和導航結構都將相同。與其在每個頁面上重複樣板程式碼,您可以使用 Django 模板語言宣告一個基本模板,然後擴充套件它以只替換每個特定頁面不同的部分。

以下程式碼片段是一個來自 base_generic.html 檔案的示例基本模板。我們很快將為 LocalLibrary 建立模板。下面的示例包含通用 HTML,其中包含標題、側邊欄和主要內容的區塊,這些區塊用命名的 blockendblock 模板標籤標記。您可以將區塊留空,或包含在渲染從模板派生的頁面時使用的預設內容。

注意:模板標籤是您可以在模板中使用的函式,用於遍歷列表、根據變數值執行條件操作等等。除了模板標籤,模板語法還允許您引用從檢視傳遞到模板的變數,並使用模板過濾器來格式化變數(例如,將字串轉換為小寫)。

django
<!doctype html>
<html lang="en">
  <head>
    {% block title %}
      <title>Local Library</title>
    {% endblock %}
  </head>
  <body>
    {% block sidebar %}
      <!-- insert default navigation text for every page -->
    {% endblock %}
    {% block content %}
      <!-- default content text (typically empty) -->
    {% endblock %}
  </body>
</html>

當為特定檢視定義模板時,我們首先使用 extends 模板標籤指定基本模板——參見下面的程式碼示例。然後,我們宣告要替換模板中的哪些部分(如果有),使用 block/endblock 部分,如基本模板中所示。

例如,下面的程式碼片段展示瞭如何使用 extends 模板標籤並覆蓋 content 塊。生成的 HTML 將包含在基本模板中定義的程式碼和結構,包括您在 title 塊中定義的預設內容,但新的 content 塊將替換預設的塊。

django
{% extends "base_generic.html" %}

{% block content %}
  <h1>Local Library Home</h1>
  <p>
    Welcome to LocalLibrary, a website developed by
    <em>Mozilla Developer Network</em>!
  </p>
{% endblock %}

LocalLibrary 基本模板

我們將使用以下程式碼片段作為 LocalLibrary 網站的基本模板。如您所見,它包含一些 HTML 程式碼,並定義了 titlesidebarcontent 的塊。我們有一個預設標題和一個預設側邊欄,其中包含所有圖書和作者列表的連結,兩者都包含在塊中,以便將來輕鬆更改。

注意:我們還引入了另外兩個模板標籤:urlload static。這些標籤將在後續章節中解釋。

/django-locallibrary-tutorial/catalog/templates/ 中建立一個新檔案 base_generic.html,並將以下程式碼貼上到檔案中:

django
<!doctype html>
<html lang="en">
  <head>
    {% block title %}
      <title>Local Library</title>
    {% endblock %}
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
      rel="stylesheet"
      crossorigin="anonymous">
    <!-- Add additional CSS in static file -->
    {% load static %}
    <link rel="stylesheet" href="{% static 'css/styles.css' %}" />
  </head>
  <body>
    <div class="container-fluid">
      <div class="row">
        <div class="col-sm-2">
          {% block sidebar %}
            <ul class="sidebar-nav">
              <li><a href="{% url 'index' %}">Home</a></li>
              <li><a href="">All books</a></li>
              <li><a href="">All authors</a></li>
            </ul>
          {% endblock %}
        </div>
        <div class="col-sm-10 ">{% block content %}{% endblock %}</div>
      </div>
    </div>
  </body>
</html>

該模板包含來自 Bootstrap 的 CSS,以改善 HTML 頁面的佈局和呈現。使用 Bootstrap(或其他客戶端 Web 框架)是建立在不同螢幕尺寸上都能良好顯示的有吸引力頁面的快速方法。

基本模板還引用了一個提供額外樣式的本地 CSS 檔案 (styles.css)。在 /django-locallibrary-tutorial/catalog/static/css/ 中建立一個 styles.css 檔案,並將以下程式碼貼上到檔案中:

css
.sidebar-nav {
  margin-top: 20px;
  padding: 0;
  list-style: none;
}

索引模板

/django-locallibrary-tutorial/catalog/templates/ 中建立一個新的 HTML 檔案 index.html,並將以下程式碼貼上到檔案中。此程式碼在第一行擴充套件了我們的基本模板,然後替換了模板的預設 content 塊。

django
{% extends "base_generic.html" %}

{% block content %}
  <h1>Local Library Home</h1>
  <p>
    Welcome to LocalLibrary, a website developed by
    <em>Mozilla Developer Network</em>!
  </p>
  <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>
{% endblock %}

在“動態內容”部分,我們為要包含的檢視資訊聲明瞭佔位符(模板變數)。變數用雙大括號(雙花括號)括起來。

注意:您可以輕鬆識別模板變數和模板標籤(函式)——變數用雙大括號({{ num_books }})括起來,標籤用帶百分號的單大括號({% extends "base_generic.html" %})括起來。

這裡需要注意的重要一點是,變數的命名與我們在檢視的 render() 函式中傳遞給 context 字典的相同(參見下面的示例)。當模板被渲染時,變數將被替換為其關聯的

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

return render(request, 'index.html', context=context)

在模板中引用靜態檔案

您的專案可能會使用靜態資源,包括 JavaScript、CSS 和影像。由於這些檔案的位置可能未知(或可能更改),Django 允許您在模板中相對於 STATIC_URL 全域性設定指定位置。預設的骨架網站將 STATIC_URL 的值設定為 "/static/",但您可以選擇將其託管在內容分發網路或其他地方。

在模板中,您首先呼叫 load 模板標籤並指定“static”以新增模板庫,如以下程式碼示例所示。然後您可以使用 static 模板標籤並指定所需檔案的相對 URL。

django
<!-- Add additional CSS in static file -->
{% load static %}
<link rel="stylesheet" href="{% static 'css/styles.css' %}" />

您可以透過類似的方式將影像新增到頁面中,例如:

django
{% load static %}
<img
  src="{% static 'images/local_library_model_uml.png' %}"
  alt="UML diagram"
  style="width:555px;height:540px;" />

注意:上面的示例指定了檔案的位置,但 Django 預設不提供它們。我們在 建立網站骨架 時透過修改全域性 URL 對映器 (/django-locallibrary-tutorial/locallibrary/urls.py) 配置了開發 Web 伺服器來提供檔案,但仍需要在生產環境中啟用檔案提供。我們將在以後討論這一點。

有關使用靜態檔案的更多資訊,請參閱 Django 文件中的 管理靜態檔案

連結到 URL

上面的基本模板引入了 url 模板標籤。

django
<li><a href="{% url 'index' %}">Home</a></li>

此標籤接受在您的 urls.py 中呼叫的 path() 函式的名稱,以及該函式將從該函式接收到的任何引數的值,並返回一個可用於連結到資源的 URL。

配置模板查詢位置

Django 搜尋模板的位置在 settings.py 檔案中的 TEMPLATES 物件中指定。預設的 settings.py(為此教程建立的)看起來像這樣:

python
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

'APP_DIRS': True 的設定最重要,因為它告訴 Django 在專案中的每個應用程式的名為“templates”的子目錄中搜索模板(這使得模板更容易與相關的應用程式分組以便於重用)。

我們還可以使用 'DIRS': [] 為 Django 指定特定的目錄搜尋位置(但目前不需要)。

注意:您可以在 Django 文件的模板部分 中瞭解有關 Django 如何查詢模板以及它支援哪些模板格式的更多資訊。

它看起來怎麼樣?

至此,我們已經建立了顯示索引頁所需的所有資源。執行伺服器(python3 manage.py runserver)並在瀏覽器中開啟 http://127.0.0.1:8000/。如果一切配置正確,您的網站應該如下圖所示。

Index page for LocalLibrary website

注意:所有圖書”和“所有作者”連結暫時無法工作,因為這些頁面的路徑、檢視和模板尚未定義。我們只是在 base_generic.html 模板中為這些連結插入了佔位符。

挑戰自我

這裡有幾個任務可以測試您對模型查詢、檢視和模板的熟悉程度。

  1. LocalLibrary 的 基本模板 包含一個 title 塊。在 索引模板 中覆蓋此塊,併為頁面建立新標題。

    注意:擴充套件模板 部分解釋瞭如何建立塊並在另一個模板中擴充套件塊。

  2. 修改 檢視,生成包含特定單詞(不區分大小寫)的流派圖書的計數,並將結果傳遞給 context。您可以透過類似於建立和使用 num_booksnum_instances_available 的方式來實現此目的。然後更新 索引模板 以包含這些變數。

總結

我們剛剛建立了網站的主頁——一個顯示資料庫中多條記錄並連結到其他尚未建立的頁面的 HTML 頁面。在此過程中,我們瞭解了有關 URL 對映器、檢視、使用模型查詢資料庫、從檢視向模板傳遞資訊以及建立和擴充套件模板的基本資訊。

在下一篇文章中,我們將在此知識的基礎上建立我們網站的其餘四個頁面。

另見