2020年9月26日星期六

018 Python编程 從入門到實踐, 第二部分  專案3 Web應用程式 第 18 章 Django入門

 申明本站飛宇網 https://feiyetopro.blogspot.com/自網路收集整理之書籍文章影音僅供預覽交流學習研究,其[書籍、文章、影音]情節內容, 評論屬其個人行為, 與本網站無關。版權歸原作者和出版社所有,請在下載 24 小時內刪除,不得用作商業用途;如果您喜歡其作品,請支持訂閱購買[正版]謝謝!



專案
3 Web應用程式

 

18 章 Django入門

當今的網站實際上都是富應用程式(rich application),就像成熟的桌面應用程式一樣。Python提供了一組開發Web應用程式的卓越工具。在本章中,你將學習如何使用Djangohttp://djangoproject.com/ )來開發一個名為學習筆記Learning Log)的專案,這是一個線上日誌系統,讓你能夠記錄所學習的有關特定主題的知識。

我們將為這個專案制定規範,然後為應用程式使用的資料定義模型。我們將使用Django的管理系統來輸入一些初始資料,再學習編寫視圖和範本,讓Django能夠為我們的網站創建網頁。

Django是一個Web框架 ——一套用於幫助開發互動式網站的工具。Django能夠回應網頁請求,還能讓你更輕鬆地讀寫資料庫、管理使用者等。在第19章和第20章,我們將改進學習筆記專案,再將其部署到活動的伺服器,讓你和你的朋友能夠使用它。

18.1 建立專案

建立專案時,首先需要以規範的方式對專案進行描述,再建立虛擬環境,以便在其中創建專案。

18.1.1 制定規範

完整的規範詳細說明了專案的目標,闡述了專案的功能,並討論了專案的外觀和使用者介面。與任何良好的項目規劃和商業計畫書一樣,規範應突出重點,説明避免專案偏離軌道。這裡不會制定完整的項目規劃,而只列出一些明確的目標,以突出開發的重點。我們制定的規範如下:

我們要編寫一個名為學習筆記Web應用程式,讓使用者能夠記錄感興趣的主題,並在學習每個主題的過程中添加日誌條目。學習筆記的主頁對這個網站進行描述,並邀請用戶註冊或登錄。使用者登錄後,就可創建新主題、添加新條目以及閱讀既有的條目。

學習新的主題時,記錄學到的知識可幫助跟蹤和複習這些知識。優秀的應用程式讓這個記錄過程簡單易行。

18.1.2 建立虛擬環境

要使用Django,首先需要建立一個虛擬工作環境。虛擬環境 是系統的一個位置,你可以在其中安裝包,並將其與其他Python包隔離。將專案的庫與其他專案分離是有益的,且為了在第20章將學習筆記部署到伺服器,這也是必須的。

為專案新建一個目錄,將其命名為learning_log,再在終端中切換到這個目錄,並創建一個虛擬環境。如果你使用的是Python 3,可使用如下命令來創建虛擬環境:

 

learning_log$ python -m venv ll_env

learning_log$

 

 

 

 

 

 

 

 

這裡運行了模組venv ,並使用它來創建一個名為ll_env的虛擬環境。如果這樣做管用,請跳到後面的18.1.4節;如果不管用,請閱讀18.1.3節。

18.1.3 安裝virtualenv

如果你使用的是較早的Python版本,或者系統沒有正確地設置,不能使用模組venv ,可安裝virtualenv包。為此,可執行如下命令:

 

$ pip install --user virtualenv

 

 

 

 

 

 

 

 

別忘了,對於這個命令,你可能需要使用稍微不同的版本(如果你沒有使用過pip,請參閱12.2.1節)。

注意  如果你使用的是Linux系統,且上面的做法不管用,可使用系統的包管理器來安裝virtualenv。例如,要在Ubuntu系統中安裝virtualenv,可使用命令sudo apt-get install python-virtualenv 

在終端中切換到目錄learning_log,並像下面這樣創建一個虛擬環境:

 

learning_log$ virtualenv ll_env

New python executable in ll_env/bin/python

Installing setuptools, pip...done.

learning_log$

 

 

 

 

 

 

 

 

注意  如果你的系統安裝了多個Python版本,需要指定virtualenv使用的版本。例如,命令virtualenv ll_env --python=python3 創建一個使用Python 3的虛擬環境。

18.1.4 啟動虛擬環境

建立虛擬環境後,需要使用下面的命令啟動它:

 

  learning_log$ source ll_env/bin/activate

(ll_env)learning_log$

 

 

 

 

 

 

 

 

這個命令運行ll_env/bin中的腳本activate。環境處於活動狀態時,環境名將包含在括弧內,如處所示。在這種情況下,你可以在環境中安裝包,並使用已安裝的包。你在ll_env中安裝的包僅在該環境處於活動狀態時才可用。

注意  如果你使用的是Windows系統,請使用命令ll_env\Scripts\activate (不包含source )來啟動這個虛擬環境。

要停止使用虛擬環境,可執行命令deactivate 

 

(ll_env)learning_log$ deactivate

learning_log$

 

 

 

 

 

 

 

 

如果關閉運行虛擬環境的終端,虛擬環境也將不再處於活動狀態。

18.1.5 安裝Django

創建並啟動虛擬環境後,就可安裝Django了:

 

(ll_env)learning_log$ pip install Django

Installing collected packages: Django

Successfully installed Django

Cleaning up...

(ll_env)learning_log$

 

 

 

 

 

 

 

 

由於我們是在虛擬環境中工作,因此在所有的系統中,安裝Django的命令都相同:不需要指定標誌--user ,也無需使用python -m pip install package_name 這樣較長的命令。

別忘了,Django僅在虛擬環境處於活動狀態時才可用。

18.1.6 在Django中創建項目

在依然處於活動的虛擬環境的情況下(ll_env包含在括弧內),執行如下命令來新建一個專案:

 

(ll_env)learning_log$ django-admin.py startproject learning_log .

(ll_env)learning_log$ ls

  learning_log ll_env manage.py

(ll_env)learning_log$ ls learning_log

  __init__.py  settings.py  urls.py  wsgi.py

 

 

 

 

 

 

 

 

處的命令讓Django新建一個名為learning_log的項目。這個命令末尾的句點讓新專案使用合適的目錄結構,這樣開發完成後可輕鬆地將應用程式部署到伺服器。

注意  千萬別忘了這個句點,否則部署應用程式時將遭遇一些配置問題。如果忘記了這個句點,就將創建的檔和資料夾刪除(ll_env除外),再重新運行這個命令。

處,運行了命令ls (在Windows系統上應為dir ),結果表明Django新建了一個名為learning_log的目錄。它還創建了一個名為manage.py的檔,這是一個簡單的程式,它接受命令並將其交給Django的相關部分去運行。我們將使用這些命令來管理諸如使用資料庫和運行伺服器等任務。

目錄learning_log包含4個檔(見),其中最重要的是settings.pyurls.pywsgi.py。文件settings.py指定Django如何與你的系統交互以及如何管理專案。在開發項目的過程中,我們將修改其中一些設置,並添加一些設置。文件urls.py告訴Django應創建哪些網頁來回應流覽器請求。文件wsgi.py幫助Django提供它創建的檔,這個檔案名是web server gateway interfaceWeb伺服器閘道介面 )的首字母縮寫。

18.1.7 創建資料庫

Django將大部分與專案相關的資訊都存儲在資料庫中,因此我們需要創建一個供Django使用的資料庫。為給專案學習筆記創建資料庫,請在處於活動虛擬環境中的情況下執行下面的命令:

 

  (ll_env)learning_log$ python manage.py migrate

Operations to perform:

    Synchronize unmigrated apps: messages, staticfiles

    Apply all migrations: contenttypes, sessions, auth, admin

    --snip--

    Applying sessions.0001_initial... OK

(ll_env)learning_log$ ls

  db.sqlite3  learning_log  ll_env  manage.py

 

 

 

 

 

 

 

 

我們將修改資料庫稱為遷移 資料庫。首次執行命令migrate 時,將讓Django確保資料庫與專案的當前狀態匹配。在使用SQLite(後面將更詳細地介紹)的新項目中首次執行這個命令時,Django將新建一個資料庫。在處,Django指出它將創建必要的資料庫表,用於存儲我們將在這個專案(Synchronize unmigrated apps同步未遷移的應用程式 )中使用的資訊,再確保資料庫結構與當前代碼(Apply all migrations應用所有的遷移 )匹配。

處,我們運行了命令ls ,其輸出表明Django又創建了一個檔——db.sqlite3SQLite是一種使用單個檔的資料庫,是編寫簡單應用程式的理想選擇,因為它讓你不用太關注資料庫管理的問題。

18.1.8 查看項目

下麵來核實Django是否正確地創建了專案。為此,可執行命令runserver ,如下所示:

 

  (ll_env)learning_log$ python manage.py runserver

  Performing system checks...

 

System check identified no issues (0 silenced).

  July 15, 2015 - 06:23:51

Django version 1.8.4, using settings 'learning_log.settings'

Starting development server at http://127.0.0.1:8000/

  Quit the server with CONTROL-C.

 

 

 

 

 

 

 

 

Django啟動一個伺服器,讓你能夠查看系統中的專案,瞭解它們的工作情況。當你在流覽器中輸入URL以請求網頁時,該Django伺服器將進行回應:生成合適的網頁,並將其發送給流覽器。

處,Django通過檢查確認正確地創建了專案;在處,它指出了使用的Django版本以及當前使用的設置檔的名稱;在處,它指出了專案的URLURL http://127.0.0.1:8000/表明專案將在你的電腦(即localhost)的埠8000上偵聽請求。localhost是一種只處理當前系統發出的請求,而不允許其他任何人查看你正在開發的網頁的伺服器。

現在打開一款Web流覽器,並輸入URLhttp://localhost:8000/;如果這不管用,請輸入http://127.0.0.1:8000/。你將看到類似於圖18-1所示的頁面,這個頁面是Django創建的,讓你知道到目前為止一切正常。現在暫時不要關閉這個伺服器。若要關閉這個伺服器,按Ctrl + C即可。


18-1 到目前為止一切正常

注意  如果出現錯誤消息“That port is already in use”(指定埠已被佔用),請執行命令python manage.py runserver 8001 ,讓Diango使用另一個埠;如果這個埠也不可用,請不斷執行上述命令,並逐漸增大其中的埠號,直到找到可用的埠。

 

動手試一試

18-1 新項目 :為更深入地瞭解Django做了些什麼,可創建兩個空項目,看看Django創建了什麼。新建一個資料夾,並給它指定簡單的名稱,如InstaBookFaceGram(不要在目錄learning_log中新建該資料夾),在終端中切換到該資料夾,並創建一個虛擬環境。在這個虛擬環境中安裝Django,並執行命令django-admin.py startproject instabook. (千萬不要忘了這個命令末尾的句點)。

看看這個命令創建了哪些檔和資料夾,並與專案學習筆記包含的檔和資料夾進行比較。這樣多做幾次,直到對Django新建項目時創建的東西瞭若指掌。然後,將專案目錄刪除——如果你想這樣做的話。

18.2 創建應用程式

Django專案 由一系列應用程式組成,它們協同工作,讓專案成為一個整體。我們暫時只創建一個應用程式,它將完成專案的大部分工作。在第19章,我們將再添加一個管理使用者帳戶的應用程式。

當前,在前面打開的終端視窗中應該還運行著runserver 。請再打開一個終端視窗(或標籤頁),並切換到manage.py所在的目錄。啟動該虛擬環境,再執行命令startapp 

 

  learning_log$ source ll_env/bin/activate

  (ll_env)learning_log$ python manage.py startapp learning_logs

(ll_env)learning_log$ ls

  db.sqlite3  learning_log  learning_logs  ll_env  manage.py

(ll_env)learning_log$ ls learning_logs/

  admin.py  __init__.py  migrations  models.py  tests.py  views.py

 

 

 

 

 

 

 

 

命令startapp appname Django建立創建應用程式所需的基礎設施。如果現在查看專案目錄,將看到其中新增了一個資料夾learning_logs(見)。打開這個資料夾,看看Django都創建了什麼(見)。其中最重要的文件是models.pyadmin.pyviews.py。我們將使用models.py來定義我們要在應用程式中管理的資料。admin.pyviews.py將在稍後介紹。

18.2.1 定義模型

我們來想想涉及的資料。每位元使用者都需要在學習筆記中創建很多主題。使用者輸入的每個條目都與特定主題相關聯,這些條目將以文本的方式顯示。我們還需要存儲每個條目的時間戳記,以便能夠告訴用戶各個條目都是什麼時候創建的。

打開文件models.py,看看它當前包含哪些內容:

models.py

 

from django.db import models

 

# 在這裡創建模型

 

 

 

 

 

 

 

 

這為我們導入了模組models,還讓我們創建自己的模型。模型告訴Django如何處理應用程式中存儲的資料。在代碼層面,模型就是一個類,就像前面討論的每個類一樣,包含屬性和方法。下面是表示使用者將要存儲的主題的模型:

 

  from django.db import models

 

  class Topic(models.Model):

      """使用者學習的主題"""

     text = models.CharField(max_length=200)

     date_added = models.DateTimeField(auto_now_add=True)

 

     def __str__(self):

          """返回模型的字串表示"""

          return self.text

 

 

 

 

 

 

 

 

我們創建了一個名為Topic 的類,它繼承了Model ——Django中一個定義了模型基本功能的類。Topic 類只有兩個屬性:text date_added 

屬性text是一個CharField——由字元或文本組成的資料(見)。需要存儲少量的文本,如名稱、標題或城市時,可使用CharField 。定義CharField 屬性時,必須告訴Django該在資料庫中預留多少空間。在這裡,我們將max_length 設置成了200(即200個字元),這對存儲大多數主題名來說足夠了。

屬性date_added 是一個DateTimeField ——記錄日期和時間的資料(見)。我們傳遞了實參auto_add_now=True ,每當使用者創建新主題時,這都讓Django將這個屬性自動設置成當前日期和時間。

注意  要獲悉可在模型中使用的各種欄位,請參閱Django Model Field ReferenceDjango模型欄位參考),其網址為https://docs.djangoproject.com/en/1.8/ref/models/fields/ 。就當前而言,你無需全面瞭解其中的所有內容,但自己開發應用程式時,這些內容會提供極大的説明。

我們需要告訴Django,預設應使用哪個屬性來顯示有關主題的資訊。Django調用方法__str__() 來顯示模型的簡單表示。在這裡,我們編寫了方法__str__() ,它返回存儲在屬性text 中的字串(見

注意  如果你使用的是Python 2.7,應調用方法__unicode__() ,而不是__str__() ,但其中的代碼相同。

18.2.2 啟動模型

要使用模型,必須讓Django將應用套裝程式含到項目中。為此,打開settings.py(它位於目錄learning_log/learning_log中),你將看到一個這樣的片段,即告訴Django哪些應用程式安裝在專案中:

settings.py

 

--snip--

INSTALLED_APPS = (

    'django.contrib.admin',

    'django.contrib.auth',

    'django.contrib.contenttypes',

    'django.contrib.sessions',

    'django.contrib.messages',

    'django.contrib.staticfiles',

)

--snip--

 

 

 

 

 

 

 

 

這是一個元組,告訴Django專案是由哪些應用程式組成的。請將INSTALLED_APPS 修改成下面這樣,將前面的應用程式添加到這個元組中:

 

--snip--

INSTALLED_APPS = (

    --snip--

    'django.contrib.staticfiles',

 

    # 我的應用程式

    'learning_logs',

)

--snip--

 

 

 

 

 

 

 

 

通過將應用程式編組,在專案不斷增大,包含更多的應用程式時,有助於對應用程式進行跟蹤。這裡新建了一個名為My apps的片段,當前它只包含應用程式learning_logs

接下來,需要讓Django修改資料庫,使其能夠存儲與模型Topic 相關的資訊。為此,在終端視窗中執行下麵的命令:

 

(ll_env)learning_log$ python manage.py makemigrations learning_logs

Migrations for 'learning_logs':

  0001_initial.py:

    - Create model Topic

(ll_env)learning_log$

 

 

 

 

 

 

 

 

命令makemigrations Django確定該如何修改資料庫,使其能夠存儲與我們定義的新模型相關聯的資料。輸出表明Django創建了一個名為0001_initial.py的遷移檔,這個檔將在資料庫中為模型Topic 創建一個表。

下面來應用這種遷移,讓Django替我們修改資料庫:

 

  (ll_env)learning_log$ python manage.py migrate

  --snip--

  Running migrations:

    Rendering model states... DONE

   Applying learning_logs.0001_initial... OK

 

 

 

 

 

 

 

 

這個命令的大部分輸出都與我們首次執行命令migrate的輸出相同。我們需要檢查的是處的輸出行,在這裡,Django確認為learning_logs 應用遷移時一切正常(OK )。

每當需要修改學習筆記管理的資料時,都採取如下三個步驟:修改models.py;對learning_logs 調用makemigrations ;讓Django遷移項目。

18.2.3 Django管理網站

為應用程式定義模型時,Django提供的管理網站(admin site)讓你能夠輕鬆地處理模型。網站的管理員可使用管理網站,但普通用戶不能使用。在本節中,我們將建立管理網站,並通過它使用模型Topic 來添加一些主題。

1. 創建超級用戶

Django允許你創建具備所有權限的用戶——超級用戶。許可權決定了用戶可執行的操作。最嚴格的許可權設置只允許用戶閱讀網站的公開信息;註冊了的使用者通常可閱讀自己的私有資料,還可查看一些只有會員才能查看的資訊。為有效地管理Web應用程式,網站所有者通常需要訪問網站存儲的所有資訊。優秀的管理員會小心對待使用者的敏感資訊,因為使用者對其訪問的應用程式有極大的信任。

為在Django中創建超級用戶,請執行下面的命令並按提示做:

 

  (ll_env)learning_log$ python manage.py createsuperuser

Username (leave blank to use 'ehmatthes'): ll_admin

Email address:

Password:

  Password (again):

  Superuser created successfully.

  (ll_env)learning_log$

 

 

 

 

 

 

 

 

你執行命令createsuperuser 時,Django提示你輸入超級用戶的用戶名(見)。這裡我們輸入的是ll_admin,但你可以輸入任何用戶名,比如電子郵寄地址,也可讓這個欄位為空(見)。你需要輸入密碼兩次(見

注意  可能會對網站管理員隱藏有些敏感資訊。例如,Django並不存儲你輸入的密碼,而存儲從該密碼派生出來的一個字串——散列值。每當你輸入密碼時,Django都計算其散列值,並將結果與存儲的散列值進行比較。如果這兩個散列值相同,就通過了身份驗證。通過存儲散列值,即便駭客獲得了網站資料庫的訪問權,也只能獲取其中存儲的散列值,而無法獲得密碼。在網站配置正確的情況下,幾乎無法根據散列值推導出原始密碼。

2. 向管理網站註冊模型

Django自動在管理網站中添加了一些模型,如User Group ,但對於我們創建的模型,必須手工進行註冊。

我們創建應用程式learning_logs 時,Djangomodels.py所在的目錄中創建了一個名為admin.py的文件:

admin.py

 

from django.contrib import admin

 

# 在這裡註冊你的模型

 

 

 

 

 

 

 

 

為向管理網站註冊Topic ,請輸入下面的代碼:

 

  from django.contrib import admin

 

from learning_logs.models import Topic

 

admin.site.register(Topic)

 

 

 

 

 

 

 

 

這些代碼導入我們要註冊的模型Topic (見),再使用admin.site.register() (見Django通過管理網站管理我們的模型。

現在,使用超級用戶帳戶訪問管理網站:訪問http://localhost:8000/admin/ ,並輸入你剛創建的超級使用者的用戶名和密碼,你將看到類似於圖18-2所示的螢幕。這個網頁讓你能夠添加和修改用戶和用戶組,還可以管理與剛才定義的模型Topic 相關的資料。


18-2 包含模型Topic 的管理網站

注意  如果你在流覽器中看到一條消息,指出訪問的網頁不可用,請確認你在終端視窗中運行著Django伺服器。如果沒有,請啟動虛擬環境,並執行命令python manage.py runserver 

3. 添加主題

向管理網站註冊Topic 後,我們來添加第一個主題。為此,按一下Topics進入主題網頁,它幾乎是空的,這是因為我們還沒有添加任何主題。按一下Add,你將看到一個用於添加新主題的表單。在第一個方框中輸入Chess ,再按一下Save,這將返回到主題管理頁面,其中包含剛創建的主題。

下面再創建一個主題,以便有更多的資料可供使用。再次按一下Add,並創建另一個主題Rock Climbing 。當你按一下Save時,將重新回到主題管理頁面,其中包含主題ChessRock Climbing

18.2.4 定義模型Entry

要記錄學到的國際象棋和攀岩知識,需要為使用者可在學習筆記中添加的條目定義模型。每個條目都與特定主題相關聯,這種關係被稱為多對一關係,即多個條目可關聯到同一個主題。

下面是模型Entry 的代碼:

models.py

 

  from django.db import models

 

  class Topic(models.Model):

      --snip--

 

class Entry(models.Model):

      """學到的有關某個主題的具體知識"""

     topic = models.ForeignKey(Topic)

     text = models.TextField()

      date_added = models.DateTimeField(auto_now_add=True)

 

     class Meta:

          verbose_name_plural = 'entries'

 

      def __str__(self):

          """返回模型的字串表示"""

 

         return self.text[:50] + "..."

 

 

 

 

 

 

 

 

 

Topic 一樣,Entry 也繼承了Django基類Model (見)。第一個屬性topic 是一個ForeignKey 實例(見)。外鍵是一個資料庫術語,它引用了資料庫中的另一條記錄;這些代碼將每個條目關聯到特定的主題。每個主題創建時,都給它分配了一個鍵(或ID)。需要在兩項資料之間建立聯繫時,Django使用與每項資訊相關聯的鍵。稍後我們將根據這些聯繫獲取與特定主題相關聯的所有條目。

接下來是屬性text ,它是一個TextField 實例(見)。這種欄位不需要長度限制,因為我們不想限制條目的長度。屬性date_added 讓我們能夠按創建順序呈現條目,並在每個條目旁邊放置時間戳記。

處,我們在Entry 類中嵌套了Meta 類。Meta 存儲用於管理模型的額外資訊,在這裡,它讓我們能夠設置一個特殊屬性,讓Django在需要時使用Entries 來表示多個條目。如果沒有這個類, Django將使用Entrys來表示多個條目。最後,方法__str__() 告訴Django,呈現條目時應顯示哪些資訊。由於條目包含的文本可能很長,我們讓Django只顯示text 的前50個字元(見)。我們還添加了一個省略號,指出顯示的並非整個條目。

18.2.5 遷移模型Entry

由於我們添加了一個新模型,因此需要再次遷移資料庫。你將慢慢地對這個過程瞭若指掌:修改models.py,執行命令python manage.py makemigrations app_name ,再執行命令python manage.py migrate 

下面來遷移資料庫並查看輸出:

 

  (ll_env)learning_log$ python manage.py makemigrations learning_logs

  Migrations for 'learning_logs':

   0002_entry.py:

      - Create model Entry

  (ll_env)learning_log$ python manage.py migrate

  Operations to perform:

    --snip--

   Applying learning_logs.0002_entry... OK

 

 

 

 

 

 

 

 

生成了一個新的遷移檔——0002_entry.py,它告訴Django如何修改資料庫,使其能夠存儲與模型Entry 相關的資訊(見)。執行命令migrate ,我們發現Django應用了這種遷移且一切順利(見

18.2.6 向管理網站註冊Entry

我們還需要註冊模型Entry 。為此,需要將admin.py修改成類似於下面這樣:

admin.py

 

from django.contrib import admin

 

from learning_logs.models import Topic, Entry

 

admin.site.register(Topic)

admin.site.register(Entry)

 

 

 

 

 

 

 

 

返回到http://localhost/admin/ ,你將看到learning_logs下列出了Entries。按一下EntriesAdd連結,或者按一下Entries再選擇Add entry。你將看到一個下拉清單,讓你能夠選擇要為哪個主題創建條目,還有一個用於輸入條目的文字方塊。從下拉清單中選擇Chess,並添加一個條目。下面是我添加的第一個條目。

The opening is the first part of the game, roughly the first ten moves or so. In the opening, it's a good idea to do three things— bring out your bishops and knights, try to control the center of the board, and castle your king.(國際象棋的第一個階段是開局,大致是前10步左右。在開局階段,最好做三件事情:將象和馬調出來;努力控制棋盤的中間區域;用車將王護住。)

Of course, these are just guidelines. It will be important to learn when to follow these guidelines and when to disregard these suggestions.(當然,這些只是指導原則。學習什麼情況下遵守這些原則、什麼情況下不用遵守很重要。)

當你按一下Save時,將返回到主條目管理頁面。在這裡,你將發現使用text[:50] 作為條目的字串表示的好處:管理介面中,只顯示了條目的開頭部分而不是其所有文本,這使得管理多個條目容易得多。

再來創建一個國際象棋條目,並創建一個攀岩條目,以提供一些初始資料。下面是第二個國際象棋條目。

In the opening phase of the game, it's important to bring out your bishops and knights. These pieces are powerful and maneuverable enough to play a significant role in the beginning moves of a game.(在國際象棋的開局階段,將象和馬調出來很重要。這些棋子威力大,機動性強,在開局階段扮演著重要角色。)

下面是第一個攀岩條目:

One of the most important concepts in climbing is to keep your weight on your feet as much as possible. There's a myth that climbers can hang all day on their arms. In reality, good climbers have practiced specific ways of keeping their weight over their feet whenever possible.(最重要的攀岩概念之一是盡可能讓雙腳承受體重。有謬誤認為攀岩者能依靠手臂的力量堅持一整天。實際上,優秀的攀岩者都經過專門訓練,能夠盡可能讓雙腳承受體重。)

繼續往下開發學習筆記時,這三個條目可為我們提供使用的資料。

18.2.7 Django shell

輸入一些資料後,就可通過互動式終端會話以程式設計方式查看這些資料了。這種互動式環境稱為Django shell,是測試專案和排除其故障的理想之地。下面是一個互動式shell會話示例:

 

  (ll_env)learning_log$ python manage.py shell

>>> from learning_logs.models import Topic

  >>> Topic.objects.all()

  [<Topic: Chess>, <Topic: Rock Climbing>]

 

 

 

 

 

 

 

 

在活動的虛擬環境中執行時,命令python manage.py shell 啟動一個Python解譯器,可使用它來探索存儲在專案資料庫中的資料。在這裡,我們導入了模組learning_logs.models 中的模型Topic (見),然後使用方法Topic.objects.all() 來獲取模型Topic 的所有實例;它返回的是一個列表,稱為查詢集(queryset)。

我們可以像遍歷列表一樣遍歷查詢集。下面演示了如何查看分配給每個主題物件的ID

 

>>> topics = Topic.objects.all()

>>> for topic in topics:

...   print(topic.id, topic)

...

1 Chess

2 Rock Climbing

 

 

 

 

 

 

 

 

我們將返回的查詢集存儲在topics 中,然後列印每個主題的id 屬性和字串表示。從輸出可知,主題ChessID1,而Rock ClimbingID2

知道物件的ID後,就可獲取該物件並查看其任何屬性。下面來看看主題Chess的屬性text date_added 的值:

 

>>> t = Topic.objects.get(id=1)

>>> t.text

'Chess'

>>> t.date_added

datetime.datetime(2015, 5, 28, 4, 39, 11, 989446, tzinfo=<UTC>)

 

 

 

 

 

 

 

 

我們還可以查看與主題相關聯的條目。前面我們給模型Entry 定義了屬性topic ,這是一個ForeignKey ,將條目與主題關聯起來。利用這種關聯,Django能夠獲取與特定主題相關聯的所有條目,如下所示:

 

>>> t.entry_set.all()

  [<Entry: The opening is the first part of the game, roughly...>, <Entry: In the opening phase of the game, it's important t...>]

 

 

 

 

 

 

 

 

為通過外鍵關係獲取資料,可使用相關模型的小寫名稱、底線和單詞set(見)。例如,假設你有模型Pizza Topping ,而Topping通過一個外鍵關聯到Pizza ;如果你有一個名為my_pizza 的物件,表示一張比薩,就可使用代碼my_pizza.topping_set.all() 來獲取這張比薩的所有配料。

編寫用戶可請求的網頁時,我們將使用這種語法。確認代碼能獲取所需的資料時,shell很有幫助。如果代碼在shell中的行為符合預期,那麼它們在專案檔案中也能正確地工作。如果代碼引發了錯誤或獲取的資料不符合預期,那麼在簡單的shell環境中排除故障要比在生成網頁的檔中排除故障容易得多。我們不會太多地使用shell,但應繼續使用它來熟悉對存儲在專案中的資料進行訪問的Django語法。

注意  每次修改模型後,你都需要重啟shell,這樣才能看到修改的效果。要退出shell會話,可按Ctr + D;如果你使用的是Windows系統,應按Ctr + Z,再按回車鍵。

 

動手試一試

18-2 簡短的條目 :當前,Django在管理網站或shell中顯示Entry 實例時,模型Entry 的方法__str__() 都在它的末尾加上省略號。請在方法__str__() 中添加一條if 語句,以便僅在條目長度超過50字元時才添加省略號。使用管理網站來添加一個長度少於50字元的條目,並核實顯示它時沒有省略號。

18-3 Django API :編寫訪問專案中的資料的代碼時,你編寫的是查詢。請流覽有關如何查詢資料的文檔,其網址為https://docs.djangoproject.com/en/7.8/topics/db/queries/ 。其中大部分內容都是你不熟悉的,但等你自己開發專案時,這些內容會很有用。

18-4 比薩店 :新建一個名為pizzeria 的專案,並在其中添加一個名為pizzas 的應用程式。定義一個名為Pizza 的模型,它包含欄位name ,用於存儲比薩名稱,如HawaiianMeat Lovers。定義一個名為Topping 的模型,它包含欄位pizza name ,其中欄位pizza 是一個關聯到Pizza 的外鍵,而欄位name 用於存儲配料,如pineapple Canadian bacon sausage 

向管理網站註冊這兩個模型,並使用管理網站輸入一些比薩名和配料。使用shell來查看你輸入的資料。

18.3 創建網頁:學習筆記主頁

使用Django創建網頁的過程通常分三個階段:定義URL、編寫視圖和編寫範本。首先,你必須定義URL模式。URL模式描述了URL是如何設計的,讓Django知道如何將流覽器請求與網站URL匹配,以確定返回哪個網頁。

每個URL都被映射到特定的視圖 ——視圖函數獲取並處理網頁所需的資料。視圖函數通常調用一個範本,後者生成流覽器能夠理解的網頁。為明白其中的工作原理,我們來創建學習筆記的主頁。我們將定義該主頁的URL、編寫其視圖函數並創建一個簡單的範本。

鑒於我們只是要確保學習筆記按要求的那樣工作,我們將暫時讓這個網頁盡可能簡單。Web應用程式能夠正常運行後,設置樣式可使其更有趣,但中看不中用的應用程式毫無意義。就目前而言,主頁只顯示標題和簡單的描述。

18.3.1 映射URL

使用者通過在流覽器中輸入URL以及按一下連結來請求網頁,因此我們需要確定專案需要哪些URL。主頁的URL最重要,它是用戶用來訪問項目的基礎URL。當前,基礎URLhttp://localhost:8000/)返回默認的Django網站,讓我們知道正確地建立了項目。我們將修改這一點,將這個基礎URL映射到學習筆記的主頁。

打開專案主資料夾learning_log中的檔urls.py,你將看到如下代碼:

urls.py

 

from django.conf.urls import include, url

  from django.contrib import admin

 

urlpatterns = [

     url(r'^admin/', include(admin.site.urls)),

  ]

 

 

 

 

 

 

 

 

前兩行導入了為專案和管理網站管理URL的函數和模組(見)。這個檔的主體定義了變數urlpatterns (見)。在這個針對整個項目的urls.py檔中,變數urlpatterns 包含專案中的應用程式的URL處的代碼包含模組admin.site.urls ,該模組定義了可在管理網站中請求的所有URL

我們需要包含learning_logsURL

 

  from django.conf.urls import include, url

  from django.contrib import admin

 

  urlpatterns = [

      url(r'^admin/', include(admin.site.urls)),

     url(r'', include('learning_logs.urls', namespace='learning_logs')),

  ]

 

 

 

 

 

 

 

 

處,我們添加了一行代碼來包含模組learning_logs.urls 。這行代碼包含實參namespace ,讓我們能夠將learning_logs URL同項目中的其他URL區分開來,這在項目開始擴展時很有幫助。

默認的urls.py包含在資料夾learning_log中,現在我們需要在資料夾learning_logs中創建另一個urls.py文件:

urls.py

 

"""定義learning_logsURL模式"""

 

from django.conf.urls import url

 

from . import views

 

urlpatterns = [

      # 主頁

     url(r'^$', views.index, name='index'),

  ]

 

 

 

 

 

 

 

 

為弄清楚當前位於哪個urls.py檔中,我們在這個檔開頭添加了一個文檔字串(見)。接下來,我們導入了函數url ,因為我們需要使用它來將URL映射到視圖(見)。我們還導入了模組views (見),其中的句點Python從當前的urls.py模組所在的資料夾中導入視圖。在這個模組中,變數urlpatterns 是一個清單,包含可在應用程式learning_logs 中請求的網頁(見

實際的URL模式是一個對函數url() 的調用,這個函數接受三個實參(見)。第一個是一個規則運算式。Djangourlpatterns 中查找與請求的URL字串匹配的規則運算式,因此規則運算式定義了Django可查找的模式。

我們來看看規則運算式r'^$' 。其中的r Python將接下來的字串視為原始字串,而引號告訴Python規則運算式始於和終於何處。脫字元(^ )讓Python查看字串的開頭,而美元符號讓Python查看字串的末尾。總體而言,這個規則運算式讓Python查找開頭和末尾之間沒有任何東西的URLPython忽略項目的基礎URLhttp://localhost:8000/),因此這個規則運算式與基礎URL匹配。其他URL都與這個規則運算式不匹配。如果請求的URL不與任何URL模式匹配,Django將返回一個錯誤頁面。

url() 的第二個實參(見)指定了要調用的視圖函數。請求的URL與前述規則運算式匹配時,Django將調用views.index (這個視圖函數將在下一節編寫)。第三個實參將這個URL模式的名稱指定為index,讓我們能夠在代碼的其他地方引用它。每當需要提供到這個主頁的連結時,我們都將使用這個名稱,而不編寫URL

注意  規則運算式通常被稱為regex,幾乎每種程式設計語言都使用它。它們的用途多得難以置信,但需要經過一定的練習才能熟悉。如果你不明白前面介紹的內容,也不用擔心,你在完成這個項目的過程中,將會看到很多規則運算式。

18.3.2 編寫視圖

視圖函數接受請求中的資訊,準備好生成網頁所需的資料,再將這些資料發送給流覽器——這通常是使用定義了網頁是什麼樣的範本實現的。

learning_logs中的文件views.py是你執行命令python manage.py startapp 時自動生成的,當前其內容如下:

views.py

 

from django.shortcuts import render

 

# 在這裡創建視圖

 

 

 

 

 

 

 

 

當前,這個檔只導入了函數render() ,它根據視圖提供的資料渲染回應。下面的代碼演示了該如何為主頁編寫視圖:

 

from django.shortcuts import render

 

def index(request):

    """學習筆記的主頁"""

    return render(request, 'learning_logs/index.html')

 

 

 

 

 

 

 

 

URL請求與我們剛才定義的模式匹配時,Django將在檔views.py中查找函數index() ,再將請求物件傳遞給這個視圖函數。在這裡,我們不需要處理任何資料,因此這個函數只包含調用render() 的代碼。這裡向函數render() 提供了兩個實參:原始請求物件以及一個可用於創建網頁的範本。下面來編寫這個範本。

18.3.3 編寫範本

範本定義了網頁的結構。範本指定了網頁是什麼樣的,而每當網頁被請求時,Django將填入相關的資料。範本讓你能夠訪問視圖提供的任何資料。我們的主頁視圖沒有提供任何資料,因此相應的範本非常簡單。

在資料夾learning_logs中新建一個資料夾,並將其命名為templates。在資料夾templates中,再新建一個資料夾,並將其命名為learning_logs。這好像有點多餘(我們在資料夾learning_logs中創建了資料夾templates,又在這個資料夾中創建了資料夾learning_logs),但建立了Django能夠明確解讀的結構,即便專案很大,包含很多應用程式亦如此。在最裡面的資料夾learning_logs中,新建一個檔,並將其命名為index.html,再在這個檔中編寫如下代碼:

index.html

 

<p>Learning Log</p>

 

<p>Learning Log helps you keep track of your learning, for any topic you're

learning about.</p>

 

 

 

 

 

 

 

 

這個檔非常簡單。對於不熟悉HTML的讀者,這裡解釋一下:標籤<p></p> 標識段落;標籤<p> 指出了段落的開頭位置,而標籤</p> 指出了段落的結束位置。這裡定義了兩個段落:第一個充當標題,第二個闡述了使用者可使用學習筆記來做什麼。

現在,如果你請求這個項目的基礎URL——http://localhost:8000/,將看到剛才創建的網頁,而不是默認的Django網頁。Django接受請求的URL,發現該URL與模式r'^$' 匹配,因此調用函數views.index() ,這將使用index.html包含的範本來渲染網頁,結果如圖18-3所示。


18-3 學習筆記的主頁

創建網頁的過程看起來可能很複雜,但將URL、視圖和範本分離的效果實際上很好。這讓我們能夠分別考慮項目的不同方面,且在項目很大時,讓各個參與者可專注於其最擅長的方面。例如,資料庫專家可專注于模型,程式師可專注于視圖代碼,而Web設計人員可專注于範本。

動手試一試

18-5 膳食規劃程式 :假設你要創建一個應用程式,説明使用者規劃一周的膳食。為此,新建一個資料夾,並將其命名為meal_planner,再在這個資料夾中新建一個Django項目。接下來,新建一個名為meal_plans 的應用程式,並為這個專案創建一個簡單的主頁。

18-6 比薩店主頁 :在你為完成練習18-4而創建的項目Pizzeria中,添加一個主頁。

18.4 創建其他網頁

制定創建網頁的流程後,可以開始擴充學習筆記專案了。我們將創建兩個顯示資料的網頁,其中一個列出所有的主題,另一個顯示特定主題的所有條目。對於每個網頁,我們都將指定URL模式,編寫一個視圖函數,並編寫一個範本。但這樣做之前,我們先創建一個父範本,專案中的其他範本都將繼承它。

18.4.1 範本繼承

創建網站時,幾乎都有一些所有網頁都將包含的元素。在這種情況下,可編寫一個包含通用元素的父範本,並讓每個網頁都繼承這個範本,而不必在每個網頁中重複定義這些通用元素。這種方法能讓你專注于開發每個網頁的獨特方面,還能讓修改項目的整體外觀容易得多。

1. 父範本

我們首先來創建一個名為base.html的範本,並將其存儲在index.html所在的目錄中。這個檔包含所有頁面都有的元素;其他的範本都繼承base.html。當前,所有頁面都包含的元素只有頂端的標題。我們將在每個頁面中包含這個範本,因此我們將這個標題設置為到主頁的連結:

base.html

 

  <p>

   <a href="{% url 'learning_logs:index' %}">Learning Log</a>

  </p>

 

{% block content %}{% endblock content %}

 

 

 

 

 

 

 

 

這個檔的第一部分創建一個包含項目名的段落,該段落也是一個到主頁的連結。為創建連結,我們使用了一個範本標籤 ,它是用大括弧和百分號({% %} )表示的。範本標籤是一小段代碼,生成要在網頁中顯示的資訊。在這個實例中,範本標籤{% url 'learning_logs:index' %} 生成一個URL,該URLlearning_logs/urls.py中定義的名為index URL模式匹配(見)。在這個示例中,learning_logs 是一個命名空間 ,而index 是該命名空間中一個名稱獨特的URL模式。

在簡單的HTML頁面中,連結是使用 標籤定義的:

 

<a href="link_url">link text</a>

 

 

 

 

 

 

 

 

讓範本標籤來生成URL,可讓連結保持最新容易得多。要修改專案中的URL,只需修改urls.py中的URL模式,這樣網頁被請求時,Django將自動插入修改後的URL。在我們的項目中,每個網頁都將繼承base.html,因此從現在開始,每個網頁都包含到主頁的連結。

處,我們插入了一對塊標籤。這個塊名為content ,是一個預留位置,其中包含的資訊將由子範本指定。

子範本並非必須定義父範本中的每個塊,因此在父範本中,可使用任意多個塊來預留空間,而子範本可根據需要定義相應數量的塊。

注意  在Python代碼中,我們幾乎總是縮進四個空格。相比於Python檔,範本檔的縮進層級更多,因此每個層級通常只縮進兩個空格。

2. 子範本

現在需要重新編寫index.html,使其繼承base.html,如下所示:

index.html

 

{% extends "learning_logs/base.html" %}

 

{% block content %}

    <p>Learning Log helps you keep track of your learning, for any topic you're

    learning about.</p>

{% endblock content %}

 

 

 

 

 

 

 

 

如果將這些代碼與原來的index.html進行比較,可發現我們將標題Learning Log替換成了從父範本那裡繼承的代碼(見)。子範本的第一行必須包含標籤{% extends %} ,讓Django知道它繼承了哪個父範本。文件base.html位於資料夾learning_logs中,因此父範本路徑中包含learning_logs。這行代碼導入範本base.html的所有內容,讓index.html能夠指定要在content 塊預留的空間中添加的內容。

處,我們插入了一個名為content {% block %} 標籤,以定義content 塊。不是從父範本繼承的內容都包含在content 塊中,在這裡是一個描述專案學習筆記的段落。在處,我們使用標籤{% endblock content %} 指出了內容定義的結束位置。

範本繼承的優點開始顯現出來了:在子範本中,只需包含當前網頁特有的內容。這不僅簡化了每個範本,還使得網站修改起來容易得多。要修改很多網頁都包含的元素,只需在父範本中修改該元素,你所做的修改將傳導到繼承該父範本的每個頁面。在包含數十乃至數百個網頁的專案中,這種結構使得網站改進起來容易而且快捷得多。

注意  在大型項目中,通常有一個用於整個網站的父範本——base.html,且網站的每個主要部分都有一個父範本。每個部分的父範本都繼承base.html,而網站的每個網頁都繼承相應部分的父範本。這讓你能夠輕鬆地修改整個網站的外觀、網站任何一部分的外觀以及任何一個網頁的外觀。這種配置提供了一種效率極高的工作方式,讓你樂意不斷地去改進網站。

18.4.2 顯示所有主題的頁面

有了高效的網頁創建方法,就能專注於另外兩個網頁了:顯示全部主題的網頁以及顯示特定主題中條目的網頁。所有主題頁面顯示使用者創建的所有主題,它是第一個需要使用資料的網頁。

1. URL模式

首先,我們來定義顯示所有主題的頁面的URL。通常,使用一個簡單的URL片段來指出網頁顯示的資訊;我們將使用單詞topics,因此URL http://localhost:8000/topics/將返回顯示所有主題的頁面。下面演示了該如何修改learning_logs/urls.py

urls.py

 

  """learning_logs定義URL模式"""

  --snip--

  urlpatterns = [

      # 主頁

      url(r'^$', views.index, name='index'),

 

      # 顯示所有的主題

     url(r'^topics/$', views.topics, name='topics'),

  ]

 

 

 

 

 

 

 

 

我們只是在用於主頁URL的規則運算式中添加了topics/ (見)。Django檢查請求的URL時,這個模式與這樣的URL匹配:基礎URL後面跟著topics 。可以在末尾包含斜杠,也可以省略它,但單詞topics 後面不能有任何東西,否則就與該模式不匹配。其URL與該模式匹配的請求都將交給views.py中的函數topics() 進行處理。

2. 視圖

函數topics() 需要從資料庫中獲取一些資料,並將其發送給範本。我們需要在views.py中添加的代碼如下:

views.py

 

  from django.shortcuts import render

 

from .models import Topic

 

  def index(request):

      --snip--

 

def topics(request):

      """顯示所有的主題"""

     topics = Topic.objects.order_by('date_added')

     context = {'topics': topics}

     return render(request, 'learning_logs/topics.html', context)

 

 

 

 

 

 

 

 

我們首先導入了與所需資料相關聯的模型(見)。函數topics() 包含一個形參:Django從伺服器那裡收到的request 物件(見)。在處,我們查詢資料庫——請求提供Topic 物件,並按屬性date_added 對它們進行排序。我們將返回的查詢集存儲在topics 中。

處,我們定義了一個將要發送給範本的上下文。上下文是一個字典,其中的鍵是我們將在範本中用來訪問資料的名稱,而值是我們要發送給範本的資料。在這裡,只有一個鍵值對,它包含我們將在網頁中顯示的一組主題。創建使用資料的網頁時,除物件request 和範本的路徑外,我們還將變數context 傳遞給render() (見

3. 範本

顯示所有主題的頁面的範本接受字典context ,以便能夠使用topics() 提供的資料。請創建一個檔,將其命名為topics.html,並存儲到index.html所在的目錄中。下面演示了如何在這個範本中顯示主題:

topics.html

 

  {% extends "learning_logs/base.html" %}

 

  {% block content %}

 

    <p>Topics</p>

 

   <ul>

     {% for topic in topics %}

       <li>{{ topic }}</li>

     {% empty %}

        <li>No topics have been added yet.</li>

     {% endfor %}

   </ul>

 

   {% endblock content %}

 

 

 

 

 

 

 

 

就像範本index.html一樣,我們首先使用標籤{% extends %} 來繼承base.html,再開始定義content 塊。這個網頁的主體是一個專案列表,其中列出了使用者輸入的主題。在標準HTML中,專案列表被稱為無序列表,用標籤<ul></ul> 表示。包含所有主題的專案清單始於處。

處,我們使用了一個相當於for 迴圈的範本標籤,它遍歷字典context 中的清單topics 。範本中使用的代碼與Python代碼存在一些重要差別:Python使用縮進來指出哪些代碼行是for 迴圈的組成部分,而在範本中,每個for 迴圈都必須使用{% endfor %} 標籤來顯式地指出其結束位置。因此在範本中,迴圈類似於下面這樣:

 

{% for item in list %}

    do something with each item

{% endfor %}

 

 

 

 

 

 

 

 

在迴圈中,我們要將每個主題轉換為一個專案清單項。要在範本中列印變數,需要將變數名用雙花括弧括起來。每次迴圈時,處的代碼{{ topic }} 都被替換為topic 的當前值。這些花括弧不會出現在網頁中,它們只是用於告訴Django我們使用了一個範本變數。HTML標籤<li></li> 表示一個專案列表項,在標籤對<ul></ul> 內部,位於標籤<li> </li> 之間的內容都是一個專案清單項。

處,我們使用了範本標籤{% empty %} ,它告訴Django在列表topics 為空時該怎麼辦:這裡是列印一條消息,告訴使用者還沒有添加任何主題。最後兩行分別結束for 迴圈(見)和項目列表(見

現在需要修改父範本,使其包含到顯示所有主題的頁面的連結:

base.html

 

  <p>

   <a href="{% url 'learning_logs:index' %}">Learning Log</a> -

   <a href="{% url 'learning_logs:topics' %}">Topics</a>

  </p>

 

  {% block content %}{% endblock content %}

 

 

 

 

 

 

 

 

我們在到主頁的連結後面添加了一個連字號(見),然後添加了一個到顯示所有主題的頁面的連結——使用的也是範本標籤url (見)。這一行讓Django生成一個連結,它與learning_logs/ urls.py中名為topics URL模式匹配。

現在如果你刷新流覽器中的主頁,將看到連結Topics。按一下這個連結,將看到類似於圖18-4所示的網頁。


18-4 顯示所有主題的網頁

18.4.3 顯示特定主題的頁面

接下來,我們需要創建一個專注於特定主題的頁面——顯示該主題的名稱及該主題的所有條目。同樣,我們將定義一個新的URL模式,編寫一個視圖並創建一個範本。我們還將修改顯示所有主題的網頁,讓每個專案清單項都是一個連結,按一下它將顯示相應主題的所有條目。

1. URL模式

顯示特定主題的頁面的URL模式與前面的所有URL模式都稍有不同,因為它將使用主題的id 屬性來指出請求的是哪個主題。例如,如果使用者要查看主題Chess(其id 1)的詳細頁面,URL將為http://localhost:8000/topics/1/。下面是與這個URL匹配的模式,它包含在learning_logs/urls.py中:

urls.py

 

--snip--

urlpatterns = [

    --snip--

    # 特定主題的詳細頁面

    url(r'^topics/(?P<topic_id>\d+)/$', views.topic, name='topic'),

]

 

 

 

 

 

 

 

 

我們來詳細研究這個URL模式中的規則運算式——r'^topics/(?P<topic_id>\d+)/$' r Django將這個字串視為原始字串,並指出規則運算式包含在引號內。這個運算式的第二部分(/(?P<topic_id>\d+)/ )與包含在兩個斜杠內的整數匹配,並將這個整數存儲在一個名為topic_id 的實參中。這部分運算式兩邊的括弧捕獲URL中的值;?P<topic_id> 將匹配的值存儲到topic_id 中;而運算式\d+ 與包含在兩個斜杆內的任何數字都匹配,不管這個數字為多少位元。

發現URL與這個模式匹配時,Django將調用視圖函數topic() ,並將存儲在topic_id 中的值作為實參傳遞給它。在這個函數中,我們將使用topic_id 的值來獲取相應的主題。

2. 視圖

函數topic() 需要從資料庫中獲取指定的主題以及與之相關聯的所有條目,如下所示:

views.py

 

  --snip--

def topic(request, topic_id):

      """顯示單個主題及其所有的條目"""

     topic = Topic.objects.get(id=topic_id)

     entries = topic.entry_set.order_by('-date_added')

     context = {'topic': topic, 'entries': entries}

     return render(request, 'learning_logs/topic.html', context)

 

 

 

 

 

 

 

 

這是第一個除request 物件外還包含另一個形參的視圖函數。這個函數接受規則運算式(?P<topic_id>\d+) 捕獲的值,並將其存儲到topic_id 中(見)。在處,我們使用get() 來獲取指定的主題,就像前面在Django shell中所做的那樣。在處,我們獲取與該主題相關聯的條目,並將它們按date_added 排序:date_added 前面的減號指定按降冪排列,即先顯示最近的條目。我們將主題和條目都存儲在字典context 中(見),再將這個字典發送給範本topic.html(見

注意  處和處的代碼被稱為查詢,因為它們向資料庫查詢特定的資訊。在自己的項目中編寫這樣的查詢時,先在Django shell中進行嘗試大有裨益。相比於編寫視圖和範本,再在流覽器中檢查結果,在shell中執行代碼可更快地獲得回饋。

3. 範本

這個範本需要顯示主題的名稱和條目的內容;如果當前主題不包含任何條目,我們還需向用戶指出這一點:

topic.html

 

  {% extends 'learning_logs/base.html' %}

 

  {% block content %}

 

   <p>Topic: {{ topic }}</p>

 

    <p>Entries:</p>

   <ul>

   {% for entry in entries %}

      <li>

       <p>{{ entry.date_added|date:'M d, Y H:i' }}</p>

       <p>{{ entry.text|linebreaks }}</p>

      </li>

   {% empty %}

      <li>

        There are no entries for this topic yet.

      </li>

    {% endfor %}

    </ul>

 

  {% endblock content %}

 

 

 

 

 

 

 

 

像這個專案的其他頁面一樣,這裡也繼承了base.html。接下來,我們顯示當前的主題(見),它存儲在範本變數{{ topic }} 中。為什麼可以使用變數topic 呢?因為它包含在字典context 中。接下來,我們開始定義一個顯示每個條目的專案清單(見),並像前面顯示所有主題一樣遍歷條目(見

每個專案清單項都將列出兩項資訊:條目的時間戳記和完整的文本。為列出時間戳記(見),我們顯示屬性date_added 的值。在Django範本中,分隔號(| )表示範本篩檢程式——對範本變數的值進行修改的函數。篩檢程式date: 'M d, Y H:i' 以這樣的格式顯示時間戳記:January 1, 2015 23:00。接下來的一行顯示text 的完整值,而不僅僅是entry 的前50個字元。篩檢程式linebreaks (見)將包含分行符號的長條目轉換為流覽器能夠理解的格式,以免顯示為一個不間斷的文字區塊。在處,我們使用範本標籤{% empty %} 列印一條消息,告訴使用者當前主題還沒有條目。

4. 將顯示所有主題的頁面中的每個主題都設置為連結

在流覽器中查看顯示特定主題的頁面前,我們需要修改範本topics.html,讓每個主題都連結到相應的網頁,如下所示:

topics.html

 

--snip--

    {% for topic in topics %}

      <li>

        <a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}</a>

      </li>

    {% empty %}

--snip--

 

 

 

 

 

 

 

 

我們使用範本標籤url 根據learning_logs中名為topic URL模式來生成合適的連結。這個URL模式要求提供實參topic_id ,因此我們在範本標籤url 中添加了屬性topic.id 。現在,主題清單中的每個主題都是一個連結,連結到顯示相應主題的頁面,如http://localhost:8000/topics/1/

如果你刷新顯示所有主題的頁面,再按一下其中的一個主題,將看到類似於圖18-5所示的頁面。


18-5 特定主題的詳細頁面,其中顯示了該主題的所有條目

動手試一試

18-7 範本文檔 :請流覽Django範本文檔,其網址為https://docs.djangoproject.com/en/1.8/ref/templates/ 。自己開發專案時,可再回過頭來參考該文檔。

18-8 比薩店頁面 :在練習18-6中開發的專案Pizzeria中添加一個頁面,它顯示供應的比薩的名稱。然後,將每個比薩名稱都設置成一個連結,按一下這種連結將顯示一個頁面,其中列出了相應比薩的配料。請務必使用範本繼承來高效地創建頁面。

18.5 小結

在本章中,你首先學習了如何使用Django框架來創建Web應用程式。你制定了簡要的專案規範,在虛擬環境中安裝了Django,創建了一個專案,並核實該項目已正確地創建。你學習了如何創建應用程式,以及如何定義表示應用程式資料的模型。你學習了資料庫,以及在你修改模型後,Django可為你遷移資料庫提供什麼樣的説明。你學習了如何創建可訪問管理網站的超級用戶,並使用管理網站輸入了一些初始資料。

你還探索了Django shell,它讓你能夠在終端會話中處理專案的資料。你學習了如何定義URL、創建視圖函數以及編寫為網站創建網頁的範本。最後,你使用了範本繼承,它可簡化各個範本的結構,並使得修改網站更容易。

在第19章,我們將創建對用戶友好而直觀的網頁,讓用戶無需通過管理網站就能添加新的主題和條目,以及編輯既有的條目。我們還將添加一個使用者註冊系統,讓使用者能夠創建帳戶和自己的學習筆記。讓任意數量的用戶都能與之交互,是Web應用程式的核心所在。

0 留言:

發佈留言