天天看點

Django 部落格開發教程 4 - 讓 Django 完成翻譯:遷移資料庫

我們已經編寫了部落格資料庫模型的代碼,但那還隻是 python 代碼而已,django 還沒有把它翻譯成資料庫語言,是以實際上這些資料庫表還沒有真正的在資料庫中建立。

為了讓 django 完成翻譯,建立好這些資料庫表,我們再一次請出我的工程管理助手 manage.py。激活虛拟環境,切換到 manage.py 檔案所在的目錄下,分别運作 <code>python manage.py makemigrations</code> 和 <code>python manage.py migrate</code> 指令:

注意:如果代碼中含有中文注釋,且你使用的是 python 2 開發環境的話,會得到一個編碼錯誤。是以請在含有中文注釋的檔案最開始處加入編碼聲明:# coding: utf-8。

當我們執行了 <code>python manage.py makemigrations</code> 後,django 在 blog 應用的 migrations 目錄下生成了一個 0001_initial.py 檔案,這個檔案是 django 用來記錄我們對模型做了哪些修改的檔案。目前來說,我們在 models.py 檔案裡建立了 3 個模型類,django 把這些變化記錄在了 0001_initial.py 裡。

不過此時還隻是告訴了 django 我們做了哪些改變,為了讓 django 真正地為我們建立資料庫表,接下來又執行了 <code>python manage.py migrate</code> 指令。django 通過檢測應用中 migrations 目錄下的檔案,得知我們對資料庫做了哪些操作,然後它把這些操作翻譯成資料庫操作語言,進而把這些操作作用于真正的資料庫。

你可以看到指令的輸出除了 applying blog.0001_initial... ok 外,django 還對其它檔案做了操作。這是因為除了我們自己建立的 blog 應用外,django 自身還内置了很多應用,這些應用本身也是需要存儲資料的。可以在 settings.py 的 <code>installed_app</code> 設定裡看到這些應用,當然我們目前不必關心這些。

對于了解資料庫語言的人,你可以運作下面的指令看看 django 究竟為我們做了什麼:

你将看到輸出了經 django 翻譯後的資料庫表建立語句,這有助于你了解 django orm 的工作機制。

我們沒有安裝任何的資料庫軟體,django 就幫我們遷移了資料庫。這是因為我們使用了 python 内置的 sqlite3 資料庫。

sqlite3 是一個十分輕巧的資料庫,它僅有一個檔案。你可以看一到項目根目錄下多出了一個 db.sqlite3 的檔案,這就是 sqlite3 資料庫檔案,django 部落格的資料都會儲存在這個資料庫檔案裡。

django 在 settings.py 裡為我們做了一些預設的資料庫配置:

可以看到預設的資料庫引擎就是使用的 sqlite3。

當然一些人傾向于使用 mysql 等大型資料庫,至于 django 如何配置 mysql 這裡就不贅述了,你可以自行使用搜尋引擎或者查閱 django 的官方文檔解決。對于一個小型部落格而言,sqlite3 資料庫足以勝任。

資料庫最主要的操作就是往裡面存入資料、從中取出資料、修改已儲存的資料和删除不再需要的資料。和建立資料庫表一樣,django 為這些操作提供了一整套方法,進而把我們從資料庫語言中解放出來。我們不用學習如何利用資料庫語言去完成這些操作,隻要簡單地調用幾個 python 函數就可以滿足我們的需求。

先在指令行中來探索一下這些函數,感受一下如何用 django 的方式來操作資料庫。在 manage.py 所在目錄下運作 <code>python manage.py shell</code> 指令:

這打開了一個互動式指令行。

首先我們來建立一個分類和一個标簽:

我們首先導入 3 個之前寫好的模型類,然後執行個體化了一個 <code>category</code> 類和一個 <code>tag</code> 類,為他們的屬性 <code>name</code> 賦了值。為了讓 django 把這些資料儲存進資料庫,調用執行個體的 <code>save</code> 方法即可。

再建立一篇文章試試,但建立文章之前,我們需要先建立一個 user,用于指定文章的作者。建立 user 的指令 django 已經幫我們寫好了,依然是通過 manage.py 來運作。首先按住 ctrl + c 退出指令互動欄(一次退不出就連續多按幾次),運作 <code>python manage.py createsuperuser</code> 指令并根據提示建立使用者:

運作 <code>python manage.py createsuperuser</code> 開始建立使用者,之後會提示你輸入使用者名、郵箱、密碼和确認密碼,按照提示輸入即可。注意一點的是密碼輸入過程中不會有任何字元顯示,不要誤以為你的鍵盤出問題了,正常輸入即可。最後出現 superuser created successfully. 說明使用者建立成功了。

再次運作 <code>python manage.py shell</code> 進入 python 指令互動欄,開始建立文章:

由于我們重新開機了 shell,是以需要重新導入了 <code>category</code>、<code>tag</code>、<code>post</code> 以及 <code>user</code>。我們還導入了一個 django 提供的輔助子產品 timezone,這是因為我們需要調用它的 <code>now()</code> 方法為 <code>created_time</code> 和 <code>modified_time</code> 指定時間,容易了解 <code>now</code> 方法傳回目前時間。然後我們根據使用者名和分類名,通過 <code>get</code> 方法取出了存在資料庫中的 <code>user</code> 和 <code>category</code>(取資料的方法将在下面介紹)。接着我們為文章指定了 <code>title</code>、<code>body</code> 、<code>created_time</code>、<code>modified_time</code>值,并把它和前面建立的 category 以及 user 關聯了起來。允許為空 <code>excerpt</code>、<code>tags</code> 我們就沒有為它們指定值了。

注意:我們這裡使用 <code>get</code> 方法根據 <code>category</code> 的 <code>name</code> 屬性的值擷取分類的一條記錄。<code>category.objects.get(name='category test')</code> 的含義是從資料庫中取出 <code>name</code> 的值為 category test 的分類記錄。確定資料庫中隻有一條值為 category test 的記錄,否則 <code>get</code> 方法将傳回一個 <code>multipleobjectsreturned</code> 異常。如果你不小心已經存了多條記錄,請删掉多餘的記錄。如何删除資料請看下文。

資料已經存入資料庫了,現在要把它們取出來看看:

<code>objects</code> 是我們的模型管理器,它為我們提供一系列從資料庫中取資料方法,這裡我們使用了 <code>all</code> 方法,表示我們要把對應的資料全部取出來。可以看到 <code>all</code> 方法都傳回了資料,這些資料應該是我們之前存進去的,但是顯示的字元串有點奇怪,無法看出究竟是不是我們之前存入的資料。為了讓顯示出來的資料更加人性化一點,我們為 3 個模型分别增加一個 <code>__str__</code> 方法:

定義好 <code>__str__</code> 方法後,解釋器顯示的内容将會是 <code>__str__</code> 方法傳回的内容。這裡 <code>category</code> 傳回分類名 <code>name</code> ,<code>tag</code> 傳回标簽名,而 <code>post</code> 傳回它的 <code>title</code>。

<code>python_2_unicode_compatible</code> 裝飾器用于相容 python2。如果你使用的 python3 開發環境,去掉這個裝飾器不會有任何影響。如果你使用的 python2 開發環境,而又不想使用這個裝飾器,則将 <code>__str__</code> 方法改為 <code>__unicode__</code> 方法即可。

先按 ctrl + c 退出 shell,再重新運作 <code>python manage.py shell</code> 進入 shell。

可以看到傳回的是我們之前存入的資料。

此外我們在建立文章時提到了通過 <code>get</code> 方法來擷取資料,這裡 <code>all</code> 方法和 <code>get</code> 方法的差別是:<code>all</code> 方法傳回全部資料,是一個類似于清單的資料結構(queryset);而 <code>get</code> 傳回一條記錄資料,如有多條記錄或者沒有記錄,<code>get</code> 方法均會抛出相應異常。

嘗試修改資料:

首先通過 <code>get</code> 方法根據分類名 <code>name</code> 擷取值為 category test 到分類,修改它的 <code>name</code> 屬性為新的值 category test new,然後調用 <code>save</code> 方法把修改儲存到資料庫,之後可以看到資料庫傳回的資料已經是修改後的值了。<code>tag</code>、<code>post</code> 的修改也一樣。

删除掉資料:

先根據标題 <code>title</code> 的值從資料庫中取出 <code>post</code>,儲存在變量 <code>p</code> 中,然後調用它的<code>delete</code> 方法,最後看到 <code>post.objects.all()</code> 傳回了一個空的 queryset(類似于一個清單),表明資料庫中已經沒有 post,post 已經被删除了。

如果遇到問題,請通過下面的方式尋求幫助。

将問題的較長的描述通過郵件發送到 [email protected],一般會在 24 小時内回複。