编写你的第一个 Django 应用,第 2 部分
Hello,我是 Alex 007,一個熱愛計算機編程和硬件設計的小白,為啥是007呢?因為叫 Alex 的人太多了,再加上每天007的生活,Alex 007就誕生了。
投票應用程序,第 2 部分
我們將建立數據庫,創建您的第一個模型,并主要關注 Django 提供的自動生成的管理頁面。
上一節:編寫你的第一個 Django 應用,第 1 部分
數據庫配置
現在,打開 mysite/settings.py 。這是個包含了 Django 項目設置的 Python 模塊。
通常,這個配置文件使用 SQLite 作為默認數據庫。如果你不熟悉數據庫,或者只是想嘗試下 Django,這是最簡單的選擇。Python 內置 SQLite,所以你無需安裝額外東西來使用它。當你開始一個真正的項目時,你可能更傾向使用一個更具擴展性的數據庫,例如 MySQL,避免中途切換數據庫這個令人頭疼的問題。
如果你想使用其他數據庫,你需要安裝合適的數據庫綁定 ,然后改變設置文件中 DATABASES ‘default’ 項目中的一些鍵值:
- ENGINE – 可選值有
- ‘django.db.backends.sqlite3’,
- ‘django.db.backends.postgresql’,
- ‘django.db.backends.mysql’,
- ‘django.db.backends.oracle’
- 其它
- NAME - 數據庫的名稱。如果使用的是 SQLite,數據庫將是你電腦上的一個文件,在這種情況下, NAME 應該是此文件的絕對路徑,包括文件名。默認值 os.path.join(BASE_DIR, ‘db.sqlite3’) 將會把數據庫文件儲存在項目的根目錄。
如果你不使用 SQLite,則必須添加一些額外設置,比如 USER 、 PASSWORD 、 HOST 等等。
SQLite 以外的其它數據庫
如果你使用了 SQLite 以外的數據庫,請確認在使用前已經創建了數據庫。你可以通過在你的數據庫交互式命令行中使用 “CREATE DATABASE database_name;” 命令來完成這件事。
另外,還要確保該數據庫用戶中提供 mysite/settings.py 具有 “create database” 權限。這使得自動創建的 test database 能被以后的教程使用。
如果你使用 SQLite,那么你不需要在使用前做任何事——數據庫會在需要的時候自動創建。
編輯 mysite/settings.py 文件前,先設置 TIME_ZONE 為你自己時區Asia/Shanghai。
此外,關注一下文件頭部的 INSTALLED_APPS 設置項。這里包括了會在你項目中啟用的所有 Django 應用。應用能在多個項目中使用,你也可以打包并且發布應用,讓別人使用它們。
通常, INSTALLED_APPS 默認包括了以下 Django 的自帶應用:
- django.contrib.admin – 管理員站點, 你很快就會使用它。
- django.contrib.auth – 認證授權系統。
- django.contrib.contenttypes – 內容類型框架。
- django.contrib.sessions – 會話框架。
- django.contrib.messages – 消息框架。
- django.contrib.staticfiles – 管理靜態文件的框架。
這些應用被默認啟用是為了給常規項目提供方便。
默認開啟的某些應用需要至少一個數據表,所以,在使用他們之前需要在數據庫中創建一些表。請執行以下命令:
python manage.py migrate這個 migrate 命令檢查 INSTALLED_APPS 設置,為其中的每個應用創建需要的數據表,至于具體會創建什么,這取決于你的 mysite/settings.py 設置文件和每個應用的數據庫遷移文件(我們稍后會介紹這個)。這個命令所執行的每個遷移操作都會在終端中顯示出來。
就像之前說的,為了方便大多數項目,我們默認激活了一些應用,但并不是每個人都需要它們。如果你不需要某個或某些應用,你可以在運行 migrate 前毫無顧慮地從 INSTALLED_APPS 里注釋或者刪除掉它們。 migrate 命令只會為在 INSTALLED_APPS 里聲明了的應用進行數據庫遷移。
創建模型
在 Django 里寫一個數據庫驅動的 Web 應用的第一步是定義模型 - 也就是數據庫結構設計和附加的其它元數據。
設計哲學
模型是真實數據的簡單明確的描述。它包含了儲存的數據所必要的字段和行為。Django 遵循 DRY(Don’t repeat yourself) 原則 。它的目標是你只需要定義數據模型,然后其它的雜七雜八代碼你都不用關心,它們會自動從模型生成。
來介紹一下遷移 ,Django 的遷移代碼是由你的模型文件自動生成的,它本質上是個歷史記錄,Django 可以用它來進行數據庫的滾動更新,通過這種方式使其能夠和當前的模型匹配。
在這個投票應用中,需要創建兩個模型:問題 Question 和選項 Choice。Question 模型包括問題描述和發布時間。Choice 模型有兩個字段,選項描述和當前得票數。每個選項屬于一個問題。
這些概念可以通過一個 Python 類來描述。按照下面的例子來編輯 polls/models.py 文件:
from django.db import modelsclass Question(models.Model):question_text = models.CharField(max_length=200)pub_date = models.DateTimeField('date published')class Choice(models.Model):question = models.ForeignKey(Question, on_delete=models.CASCADE)choice_text = models.CharField(max_length=200)votes = models.IntegerField(default=0)每個模型被表示為 django.db.models.Model 類的子類。每個模型有許多類變量,它們都表示模型里的一個數據庫字段。
每個字段都是 Field 類的實例 - 比如,字符字段被表示為 CharField ,日期時間字段被表示為 DateTimeField 。這將告訴 Django 每個字段要處理的數據類型。
每個 Field 類實例變量的名字(例如 question_text 或 pub_date )也是字段名,所以最好使用對機器友好的格式。你將會在 Python 代碼里使用它們,而數據庫會將它們作為列名。
你可以使用可選的選項來為 Field 定義一個人類可讀的名字。這個功能在很多 Django 內部組成部分中都被使用了,而且作為文檔的一部分。如果某個字段沒有提供此名稱,Django 將會使用對機器友好的名稱,也就是變量名。在上面的例子中,我們只為 Question.pub_date 定義了對人類友好的名字。對于模型內的其它字段,它們的機器友好名也會被作為人類友好名使用。
定義某些 Field 類實例需要參數。例如 CharField 需要一個 max_length 參數。這個參數的用處不止于用來定義數據庫結構,也用于驗證數據,我們稍后將會看到這方面的內容。
Field 也能夠接收多個可選參數;在上面的例子中:我們將 votes 的 default 也就是默認值,設為0。
注意在最后,我們使用 ForeignKey 定義了一個關系。這將告訴 Django,每個 Choice 對象都關聯到一個 Question 對象。Django 支持所有常用的數據庫關系:多對一、多對多和一對一。
激活模型
上面的一小段用于創建模型的代碼給了 Django 很多信息,通過這些信息,Django 可以:
- 為這個應用創建數據庫 schema(架構)(生成 CREATE TABLE 語句)。
- 創建可以與 Question 和 Choice 對象進行交互的 Python 數據庫 API。
但是首先得把 polls 應用安裝到我們的項目里。
設計哲學
Django 應用是“可插拔”的。你可以在多個項目中使用同一個應用。除此之外,你還可以發布自己的應用,因為它們并不會被綁定到當前安裝的 Django 上。
為了在我們的工程中包含這個應用,我們需要在配置類 INSTALLED_APPS 中添加設置。因為 PollsConfig 類寫在文件 polls/apps.py 中,所以它的點式路徑是 ‘polls.apps.PollsConfig’。在文件 mysite/settings.py 中 INSTALLED_APPS 子項添加點式路徑后,它看起來像這樣:
INSTALLED_APPS = ['polls.apps.PollsConfig','django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles', ]現在你的 Django 項目會包含 polls 應用。接著運行下面的命令:
python manage.py makemigrations polls你將會看到類似于下面這樣的輸出:
Migrations for 'polls':polls/migrations/0001_initial.py- Create model Question- Create model Choice通過運行 makemigrations 命令,Django 會檢測你對模型文件的修改(在這種情況下,你已經取得了新的),并且把修改的部分儲存為一次 遷移。
遷移是 Django 對于模型定義(也就是你的數據庫結構)的變化的儲存形式 - 它們其實也只是一些你磁盤上的文件。如果你想的話,你可以閱讀一下你模型的遷移數據,它被儲存在 polls/migrations/0001_initial.py 里。別擔心,你不需要每次都閱讀遷移文件,但是它們被設計成人類可讀的形式,這是為了便于你手動調整Django的修改方式。
Django 有一個自動執行數據庫遷移并同步管理你的數據庫結構的命令 - 這個命令是 migrate,我們馬上就會接觸它 - 但是首先,讓我們看看遷移命令會執行哪些 SQL 語句。sqlmigrate 命令接收一個遷移的名稱,然后返回對應的 SQL:
python manage.py sqlmigrate polls 0001你將會看到類似下面這樣的輸出(我把輸出重組成了人類可讀的格式):
BEGIN; -- -- Create model Question -- CREATE TABLE "polls_question" ("id" serial NOT NULL PRIMARY KEY,"question_text" varchar(200) NOT NULL,"pub_date" timestamp with time zone NOT NULL ); -- -- Create model Choice -- CREATE TABLE "polls_choice" ("id" serial NOT NULL PRIMARY KEY,"choice_text" varchar(200) NOT NULL,"votes" integer NOT NULL,"question_id" integer NOT NULL ); ALTER TABLE "polls_choice"ADD CONSTRAINT "polls_choice_question_id_c5b4b260_fk_polls_question_id"FOREIGN KEY ("question_id")REFERENCES "polls_question" ("id")DEFERRABLE INITIALLY DEFERRED; CREATE INDEX "polls_choice_question_id_c5b4b260" ON "polls_choice" ("question_id");COMMIT;請注意以下幾點:
- 輸出的內容和你使用的數據庫有關,上面的輸出示例使用的是 PostgreSQL。
- 數據庫的表名是由應用名(polls)和模型名的小寫形式( question 和 choice)連接而來。(如果需要,你可以自定義此行為。)
- 主鍵(IDs)會被自動創建。(當然,你也可以自定義。)
- 默認的,Django 會在外鍵字段名后追加字符串 “_id” 。(同樣,這也可以自定義。)
- 外鍵關系由 FOREIGN KEY 生成。你不用關心 DEFERRABLE 部分,它只是告訴 PostgreSQL,請在事務全都執行完之后再創建外鍵關系。
- 生成的 SQL 語句是為你所用的數據庫定制的,所以那些和數據庫有關的字段類型,比如 auto_increment (MySQL)、 serial (PostgreSQL)和 integer primary key autoincrement (SQLite),Django 會幫你自動處理。那些和引號相關的事情 - 例如,是使用單引號還是雙引號 - 也一樣會被自動處理。
- 這個 sqlmigrate 命令并沒有真正在你的數據庫中的執行遷移 - 相反,它只是把命令輸出到屏幕上,讓你看看 Django 認為需要執行哪些 SQL 語句。這在你想看看 Django 到底準備做什么,或者當你是數據庫管理員,需要寫腳本來批量處理數據庫時會很有用。
如果你感興趣,你也可以試試運行 python manage.py check ;這個命令幫助你檢查項目中的問題,并且在檢查過程中不會對數據庫進行任何操作。
現在,再次運行 migrate 命令,在數據庫里創建新定義的模型的數據表:
python manage.py migrate Operations to perform:Apply all migrations: admin, auth, contenttypes, polls, sessions Running migrations:Rendering model states... DONEApplying polls.0001_initial... OK這個 migrate 命令選中所有還沒有執行過的遷移(Django 通過在數據庫中創建一個特殊的表 django_migrations 來跟蹤執行過哪些遷移)并應用在數據庫上 - 也就是將你對模型的更改同步到數據庫結構上。
遷移是非常強大的功能,它能讓你在開發過程中持續的改變數據庫結構而不需要重新刪除和創建表 - 它專注于使數據庫平滑升級而不會丟失數據。我們會在后面的教程中更加深入的學習這部分內容,現在,你只需要記住,改變模型需要這三步:
數據庫遷移被分解成生成和應用兩個命令是為了讓你能夠在代碼控制系統上提交遷移數據并使其能在多個應用里使用;這不僅僅會讓開發更加簡單,也給別的開發者和生產環境中的使用帶來方便。
初試 API
現在讓我們進入交互式 Python 命令行,嘗試一下 Django 為你創建的各種 API。通過以下命令打開 Python 命令行:
python manage.py shell我們使用這個命令而不是簡單的使用 “Python” 是因為 manage.py 會設置 DJANGO_SETTINGS_MODULE 環境變量,這個變量會讓 Django 根據 mysite/settings.py 文件來設置 Python 包的導入路徑。
當你成功進入命令行后,來試試 database API 吧:
>>> from polls.models import Choice, Question # 導入我們剛剛編寫的模型類。# 系統中還沒有問題。 >>> Question.objects.all() <QuerySet []># 創建一個新問題。 # 默認設置文件中啟用了對時區的支持,因此Django需要一個帶有tzinfo的datetime作為pub_date。 # 使用timezone.now()而不是datetime.datetime.now()。 >>> from django.utils import timezone >>> q = Question(question_text="What's new?", pub_date=timezone.now())# 將對象保存到數據庫中。必須顯式調用save()。 >>> q.save()# 現在它有了ID。 >>> q.id 1# 通過Python屬性訪問模型字段值。 >>> q.question_text "What's new?" >>> q.pub_date datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=<UTC>)# 通過更改屬性,然后調用save()來更改值。 >>> q.question_text = "What's up?" >>> q.save()# 對象.all()顯示數據庫中的所有問題。 >>> Question.objects.all() <QuerySet [<Question: Question object (1)>]>等等。<Question: Question object (1)> 對于我們了解這個對象的細節沒什么幫助。讓我們通過編輯 Question 模型的代碼(位于 polls/models.py 中)來修復這個問題。給 Question 和 Choice 增加 __str__() 方法。
from django.db import modelsclass Question(models.Model):# ...def __str__(self):return self.question_textclass Choice(models.Model):# ...def __str__(self):return self.choice_text給模型增加 __str__() 方法是很重要的,這不僅僅能給你在命令行里使用帶來方便,Django 自動生成的 admin 里也使用這個方法來表示對象。
讓我們再為此模型添加一個自定義方法:
import datetimefrom django.db import models from django.utils import timezoneclass Question(models.Model):# ...def was_published_recently(self):return self.pub_date >= timezone.now() - datetime.timedelta(days=1)新加入的 import datetime 和 from django.utils import timezone 分別導入了 Python 的標準 datetime 模塊和 Django 中和時區相關的 django.utils.timezone 工具模塊。
保存文件然后通過 python manage.py shell 命令再次打開 Python 交互式命令行:
>>> from polls.models import Choice, Question# 確保我們的__str__()添加成功。 >>> Question.objects.all() <QuerySet [<Question: What's up?>]># Django提供了一個完全由關鍵字參數驅動的豐富的數據庫查找API。 >>> Question.objects.filter(id=1) <QuerySet [<Question: What's up?>]> >>> Question.objects.filter(question_text__startswith='What') <QuerySet [<Question: What's up?>]># 得到今年發表的問題。 >>> from django.utils import timezone >>> current_year = timezone.now().year >>> Question.objects.get(pub_date__year=current_year) <Question: What's up?># 請求一個不存在的ID,這將引發異常。 >>> Question.objects.get(id=2) Traceback (most recent call last):... DoesNotExist: Question matching query does not exist.# 按主鍵查找是最常見的情況,因此Django為主鍵精確查找提供了快捷方式。 # 以下內容與Question.objects.get(id=1)相同。 >>> Question.objects.get(pk=1) <Question: What's up?># 確保我們的定制方法有效。 >>> q = Question.objects.get(pk=1) >>> q.was_published_recently() True# 給這個問題幾個選擇。 # create調用構造一個新的Choice對象,執行INSERT語句,將選項添加到可用選項集中并返回新的Choice對象。 # Django創建一個集合來保存ForeignKey關系的“另一面”(例如問題的選擇),該關系可以通過API訪問。 >>> q = Question.objects.get(pk=1)# 顯示相關對象集中的任何選項--目前為止沒有。 >>> q.choice_set.all() <QuerySet []># 創造三個選擇。 >>> q.choice_set.create(choice_text='Not much', votes=0) <Choice: Not much> >>> q.choice_set.create(choice_text='The sky', votes=0) <Choice: The sky> >>> c = q.choice_set.create(choice_text='Just hacking again', votes=0)# Choice對象具有對其相關問題對象的API訪問權限。 >>> c.question <Question: What's up?># 反之亦然:問題對象可以訪問選擇對象。 >>> q.choice_set.all() <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]> >>> q.choice_set.count() 3# API會根據需要自動跟蹤關系。 # 使用雙下劃線分隔關系。 # 這可以工作在你想要的任何深度;沒有限制。 # 找到發布日期在今年的任何問題的所有選項(重復使用我們上面創建的“當前年份”變量)。 >>> Choice.objects.filter(question__pub_date__year=current_year) <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]># 讓我們刪除其中一個選項。為此,請使用delete()。>>> c = q.choice_set.filter(choice_text__startswith='Just hacking') >>> c.delete()介紹 Django 管理頁面
設計哲學
為你的員工或客戶生成一個用戶添加,修改和刪除內容的后臺是一項缺乏創造性和乏味的工作。因此,Django 全自動地根據模型創建后臺界面。
Django 產生于一個公眾頁面和內容發布者頁面完全分離的新聞類站點的開發過程中。站點管理人員使用管理系統來添加新聞、事件和體育時訊等,這些添加的內容被顯示在公眾頁面上。Django 通過為站點管理人員創建統一的內容編輯界面解決了這個問題。
管理界面不是為了網站的訪問者,而是為管理者準備的。
創建一個管理員賬號
首先,我們得創建一個能登錄管理頁面的用戶。請運行下面的命令:
python manage.py createsuperuser鍵入你想要使用的用戶名,然后按下回車鍵:
Username: admin然后提示你輸入想要使用的郵件地址:
Email address: admin@example.com最后一步是輸入密碼。你會被要求輸入兩次密碼,第二次的目的是為了確認第一次輸入的確實是你想要的密碼。
Password: ********** Password (again): ********* Superuser created successfully.啟動開發服務器
Django 的管理界面默認就是啟用的。讓我們啟動開發服務器,看看它到底是什么樣的。
如果開發服務器未啟動,用以下命令啟動它:
python manage.py runserver現在,打開瀏覽器,轉到你本地域名的 “/admin/” 目錄, – 比如 http://127.0.0.1:8000/admin/ 。你應該會看見管理員登錄界面:
由于默認情況下翻譯是打開的,如果您設置了LANGUAGE_CODE,則登錄屏幕將以給定的語言顯示(如果Django有適當的翻譯)。
進入管理站點頁面
現在,試著使用你在上一步中創建的超級用戶來登錄。然后你將會看到 Django 管理頁面的索引頁:
你將會看到幾種可編輯的內容:組和用戶。它們是由 django.contrib.auth 提供的,這是 Django 開發的認證框架。
向管理頁面中加入投票應用
但是我們的投票應用在哪呢?它沒在索引頁面里顯示。
只需要再做一件事:我們得告訴管理員,問題 Question 對象需要一個后臺接口。打開 polls/admin.py 文件,把它編輯成下面這樣:
from django.contrib import adminfrom .models import Questionadmin.site.register(Question)體驗便捷的管理功能
現在我們向管理頁面注冊了問題 Question 類。Django 知道它應該被顯示在索引頁里:
點擊 “Questions” 。現在看到是問題 “Questions” 對象的列表 “change list” 。這個界面會顯示所有數據庫里的問題 Question 對象,你可以選擇一個來修改。這里現在有我們在上一部分中創建的 “What’s up?” 問題。
點擊 “What’s up?” 來編輯這個問題(Question)對象:
注意事項:
- 這個表單是從問題 Question 模型中自動生成的
- 不同的字段類型(日期時間字段 DateTimeField 、字符字段 CharField)會生成對應的 HTML 輸入控件。每個類型的字段都知道它們該如何在管理頁面里顯示自己。
- 每個日期時間字段 DateTimeField 都有 JavaScript 寫的快捷按鈕。日期有轉到今天(Today)的快捷按鈕和一個彈出式日歷界面。時間有設為現在(Now)的快捷按鈕和一個列出常用時間的方便的彈出式列表。
頁面的底部提供了幾個選項:
- 保存(Save) - 保存改變,然后返回對象列表。
- 保存并繼續編輯(Save and continue editing) - 保存改變,然后重新載入當前對象的修改界面。
- 保存并新增(Save and add another) - 保存改變,然后添加一個新的空對象并載入修改界面。
- 刪除(Delete) - 顯示一個確認刪除頁面。
如果顯示的 “發布日期(Date Published)” 和你在 教程 1 里創建它們的時間不一致,這意味著你可能沒有正確的設置 TIME_ZONE 。改變設置,然后重新載入頁面看看是否顯示了正確的值。
通過點擊 “今天(Today)” 和 “現在(Now)” 按鈕改變 “發布日期(Date Published)”。然后點擊 “保存并繼續編輯(Save and add another)”按鈕。然后點擊右上角的 “歷史(History)”按鈕。你會看到一個列出了所有通過 Django 管理頁面對當前對象進行的改變的頁面,其中列出了時間戳和進行修改操作的用戶名:
總結
以上是生活随笔為你收集整理的编写你的第一个 Django 应用,第 2 部分的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 350. Intersection of
- 下一篇: 编写你的第一个 Django 应用,第