Django 教程第 5 部分:建立我們的主頁
現在,我們準備新增程式碼來顯示第一個完整的頁面 - LocalLibrary 網站的首頁。首頁將顯示我們擁有的每種模型型別的記錄數量,並提供指向其他頁面的側邊欄導航連結。在此過程中,我們將獲得編寫基本 URL 對映和檢視、從資料庫獲取記錄以及使用模板的實踐經驗。
| 先決條件 | 閱讀 Django 簡介。完成之前的教程主題(包括 Django 教程第 4 部分:Django 管理站點)。 |
|---|---|
| 目標 | 學習建立簡單的 URL 對映和檢視(其中沒有資料編碼在 URL 中)、從模型獲取資料以及建立模板。 |
概述
在定義完模型並建立了一些初始的圖書館記錄來使用後,現在該編寫程式碼將這些資訊呈現給使用者了。我們首先需要確定要顯示的資訊,並定義用於返回這些資源的 URL。然後,我們將建立 URL 對映器、檢視和模板來顯示頁面。
以下圖表描述了主要資料流,以及處理 HTTP 請求和響應時所需的元件。由於我們已經實現了模型,因此我們要建立的主要元件是
- URL 對映器,用於將支援的 URL(以及 URL 中編碼的任何資訊)轉發到相應的檢視函式。
- 檢視函式,用於從模型獲取請求的資料,建立顯示資料的 HTML 頁面,並將頁面返回給使用者,以便在瀏覽器中檢視。
- 用於在檢視中呈現資料的模板。
正如您將在下一節中看到的,我們有 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 模組
urlpatterns += [
path('catalog/', include('catalog.urls')),
]
注意:每當 Django 遇到匯入函式 django.urls.include() 時,它將在指定結束字元處拆分 URL 字串,並將剩餘的子字串傳送到包含的 URLconf 模組以進行進一步處理。
我們還建立了一個名為 /catalog/urls.py 的 URLConf 模組的佔位符檔案。將以下行新增到該檔案中
urlpatterns = [
path('', views.index, name='index'),
]
path() 函式定義了以下內容
- URL 模式,它是一個空字串:
''。在處理其他檢視時,我們將詳細討論 URL 模式。 - 如果檢測到 URL 模式,將呼叫的檢視函式:
views.index,這是 views.py 檔案中名為index()的函式。
path() 函式還指定了一個 name 引數,它是此特定 URL 對映的唯一識別符號。您可以使用該名稱“反轉”對映器,即動態建立指向對映器旨在處理的資源的 URL。例如,我們可以使用名稱引數透過在模板中新增以下連結來從任何其他頁面連結到我們的主頁
<a href="{% url 'index' %}">Home</a>.
注意:我們可以像在 <a href="/catalog/">Home</a> 中一樣硬編碼連結),但如果我們更改主頁的模式,例如,更改為 /catalog/index)模板將不再正確連結。使用反向 URL 對映更健壯。
檢視(基於函式的)
檢視是一個處理 HTTP 請求、從資料庫獲取所需資料、使用 HTML 模板將資料渲染到 HTML 頁面,然後將生成的 HTML 返回到 HTTP 響應以向用戶顯示頁面的函式。索引檢視遵循這種模型 - 它獲取有關資料庫中 Book、BookInstance、可用的 BookInstance 和 Author 記錄數量的資訊,並將這些資訊傳遞給模板以顯示。
開啟 catalog/views.py,並注意該檔案已匯入 render() 快捷函式,以使用模板和資料生成 HTML 檔案
from django.shortcuts import render
# Create your views here.
將以下行貼上到檔案底部
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() 屬性來獲取記錄數量。它還獲取了一個 BookInstance 物件的列表,這些物件在 status 欄位中具有 'a'(可用)的值。您可以在我們之前的教程 Django 教程第 3 部分:使用模型 > 搜尋記錄 中找到有關如何訪問模型資料的更多資訊。
在檢視函式的末尾,我們呼叫 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,其中包含標題、側邊欄和主要內容的部分,這些部分用命名 block 和 endblock 模板標記標記。您可以將這些塊留空,或包括預設內容以在渲染從模板派生的頁面時使用。
注意:模板標記是在模板中使用的函式,用於遍歷列表、根據變數的值執行條件操作等。除了模板標記之外,模板語法還允許您引用從檢視傳遞到模板的變數,並使用模板過濾器來格式化變數(例如,將字串轉換為小寫)。
<!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 塊代替預設塊。
{% 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 程式碼,並定義了title、sidebar和content的塊。我們有一個預設標題和一個預設側邊欄,其中包含指向所有書籍和作者列表的連結,兩者都包含在塊中,以便將來輕鬆更改。
注意:我們還引入了兩個額外的模板標籤:url和load static。這些標籤將在後續章節中解釋。
在/django-locallibrary-tutorial/catalog/templates/中建立一個新檔案base_generic.html,並將以下程式碼貼上到該檔案中
<!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"
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
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檔案,並將以下程式碼貼上到該檔案中
.sidebar-nav {
margin-top: 20px;
padding: 0;
list-style: none;
}
索引模板
在/django-locallibrary-tutorial/catalog/templates/中建立一個新的 HTML 檔案index.html,並將以下程式碼貼上到該檔案中。此程式碼在第一行擴充套件了我們的基本模板,然後替換了模板的預設content塊。
{% 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字典中的鍵命名(請參見下面的示例)。變數將在渲染模板時替換為其關聯的值。
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。
<!-- Add additional CSS in static file -->
{% load static %}
<link rel="stylesheet" href="{% static 'css/styles.css' %}" />
您可以透過類似的方式將影像新增到頁面中,例如
{% load static %}
<img
src="{% static 'catalog/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模板標籤。
<li><a href="{% url 'index' %}">Home</a></li>
此標籤接受在您的urls.py中呼叫的path()函式的名稱以及關聯檢視將從該函式接收的任何引數的值,並返回一個 URL,您可以使用它來連結到該資源。
配置查詢模板的位置
Django 搜尋模板的位置在settings.py檔案中的TEMPLATES物件中指定。預設的settings.py(如本教程中建立的)看起來像這樣
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/。如果一切配置正確,您的網站應該看起來像下面的螢幕截圖。
注意:“所有書籍”和“所有作者”連結目前還無法使用,因為這些頁面的路徑、檢視和模板尚未定義。我們只是在base_generic.html模板中為這些連結插入了佔位符。
挑戰自己
總結
我們剛剛建立了我們網站的主頁 - 一個 HTML 頁面,它顯示來自資料庫的一些記錄,並連結到其他尚未建立的頁面。在此過程中,我們學習了有關 URL 對映器、檢視、使用模型查詢資料庫、從檢視將資訊傳遞到模板以及建立和擴充套件模板的基本資訊。
在下一篇文章中,我們將在此基礎上繼續構建,以建立我們網站其餘的四個頁面。
另請參閱
- 編寫第一個 Django 應用程式,第 3 部分:檢視和模板(Django 文件)
- URL 分發器(Django 文件)
- 檢視函式(DJango 文件)
- 模板(Django 文件)
- 管理靜態檔案(Django 文件)
- Django 快捷函式(Django 文件)