Django 博客開發教程 4 - 讓 Django 完成翻譯:遷移數據庫
我們已經編寫了博客數據庫模型的代碼,但那還隻是 Python 代碼而已,Django 還沒有把它翻譯成數據庫語言,因此實際上這些數據庫表還沒有真正的在數據庫中創建。
遷移數據庫
為了讓 Django 完成翻譯,創建好這些數據庫表,我們再一次請出我的工程管理助手 manage.py。**激活虛擬環境**,切換到 manage.py 文件所在的目錄下,分別運行 python manage.py makemigrations
和 python manage.py migrate
命令:
C:\WINDOWS\system32>C:\Users\yangxg\Envs\blogproject_env\Scripts\activate
(blogproject_env) C:\WINDOWS\system32>cd C:\Users\yangxg\Workspace\blogproject
(blogproject_env) C:\Users\yangxg\Workspace\blogproject>python manage.py makemigrations
Migrations for 'blog':
blog\migrations\0001_initial.py:
- Create model Category
- Create model Post
- Create model Tag
- Add field tags to post
(blogproject_env) C:\Users\yangxg\Workspace\blogproject>python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, blog, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying blog.0001_initial... OK
Applying sessions.0001_initial... OK
注意:如果代碼中含有中文注釋,且你使用的是 Python 2 開發環境的話,會得到一個編碼錯誤。因此請在含有中文注釋的文件最開始處加入編碼聲明:# coding: utf-8。
當我們執行了 python manage.py makemigrations
後,Django 在 blog 應用的 migrations\ 目錄下生成了一個 0001_initial.py 文件,這個文件是 Django 用來記錄我們對模型做了哪些修改的文件。目前來說,我們在 models.py 文件裏創建了 3 個模型類,Django 把這些變化記錄在了 0001_initial.py 裏。
不過此時還隻是告訴了 Django 我們做了哪些改變,為了讓 Django 真正地為我們創建數據庫表,接下來又執行了 python manage.py migrate
命令。Django 通過檢測應用中 migrations\ 目錄下的文件,得知我們對數據庫做了哪些操作,然後它把這些操作翻譯成數據庫操作語言,從而把這些操作作用於真正的數據庫。
你可以看到命令的輸出除了 Applying blog.0001_initial... OK 外,Django 還對其它文件做了操作。這是因為除了我們自己建立的 blog 應用外,Django 自身還內置了很多應用,這些應用本身也是需要存儲數據的。可以在 settings.py 的 INSTALLED_APP
設置裏看到這些應用,當然我們目前不必關心這些。
blogproject/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog',
]
對於了解數據庫語言的人,你可以運行下麵的命令看看 Django 究竟為我們做了什麼:
python manage.py sqlmigrate blog 0001
你將看到輸出了經 Django 翻譯後的數據庫表創建語句,這有助於你理解 Django ORM 的工作機製。
選擇數據庫版本
我們沒有安裝任何的數據庫軟件,Django 就幫我們遷移了數據庫。這是因為我們使用了 Python 內置的 SQLite3 數據庫。
SQLite3 是一個十分輕巧的數據庫,它僅有一個文件。你可以看一到項目根目錄下多出了一個 db.sqlite3 的文件,這就是 SQLite3 數據庫文件,Django 博客的數據都會保存在這個數據庫文件裏。
Django 在 settings.py 裏為我們做了一些默認的數據庫配置:
blogproject/settings.py
## 其它配置選項...
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
## 其它配置選項...
可以看到默認的數據庫引擎就是使用的 SQLite3。
當然一些人傾向於使用 MySQL 等大型數據庫,至於 Django 如何配置 MySQL 這裏就不贅述了,你可以自行使用搜索引擎或者查閱 Django 的官方文檔解決。對於一個小型博客而言,SQLite3 數據庫足以勝任。
用 Django 的方式操作數據庫
數據庫最主要的操作就是往裏麵存入數據、從中取出數據、修改已保存的數據和刪除不再需要的數據。和創建數據庫表一樣,Django 為這些操作提供了一整套方法,從而把我們從數據庫語言中解放出來。我們不用學習如何利用數據庫語言去完成這些操作,隻要簡單地調用幾個 Python 函數就可以滿足我們的需求。
存數據
先在命令行中來探索一下這些函數,感受一下如何用 Django 的方式來操作數據庫。在 manage.py 所在目錄下運行 python manage.py shell
命令:
(blogproject_env) C:\Users\yangxg\Workspace\blogproject>python manage.py shell
Python 3.5.2 (v3.5.2:4def2a2901a5, Jun 25 2016, 22:18:55) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>
這打開了一個交互式命令行。
首先我們來創建一個分類和一個標簽:
>>> from blog.models import Category, Tag, Post
>>> c = Category(name='category test')
>>> c.save()
>>> t = Tag(name='tag test')
>>> t.save()
我們首先導入 3 個之前寫好的模型類,然後實例化了一個 Category
類和一個 Tag
類,為他們的屬性 name
賦了值。為了讓 Django 把這些數據保存進數據庫,調用實例的 save
方法即可。
再創建一篇文章試試,但創建文章之前,我們需要先創建一個 User,用於指定文章的作者。創建 User 的命令 Django 已經幫我們寫好了,依然是通過 manage.py 來運行。首先按住 Ctrl + c 退出命令交互欄(一次退不出就連續多按幾次),運行 python manage.py createsuperuser
命令並根據提示創建用戶:
(blogproject_env) C:\Users\yangxg\Workspace\blogproject>python manage.py createsuperuser
Username (leave blank to use 'zmrenwu@163.com'): myuser
Email address: a@aa.com
Password:
Password (again):
Superuser created successfully.
運行 python manage.py createsuperuser
開始創建用戶,之後會提示你輸入用戶名、郵箱、密碼和確認密碼,按照提示輸入即可。**注意一點的是密碼輸入過程中不會有任何字符顯示,不要誤以為你的鍵盤出問題了,正常輸入即可。**最後出現 Superuser created successfully. 說明用戶創建成功了。
再次運行 python manage.py shell
進入 Python 命令交互欄,開始創建文章:
>>> from blog.models import Category, Tag, Post
>>> from django.utils import timezone
>>> from django.contrib.auth.models import User
>>> user = User.objects.get(username='myuser')
>>> c = Category.objects.get(name='category test')
>>> p = Post(title='title test', body='body test', created_time=timezone.now(), modified_time=timezone.now(), category=c, author=user)
>>> p.save()
由於我們重啟了 shell,因此需要重新導入了 Category
、Tag
、Post
以及 User
。我們還導入了一個 Django 提供的輔助模塊 timezone,這是因為我們需要調用它的 now()
方法為 created_time
和 modified_time
指定時間,容易理解 now
方法返回當前時間。然後我們根據用戶名和分類名,通過 get
方法取出了存在數據庫中的 User
和 Category
(取數據的方法將在下麵介紹)。接著我們為文章指定了 title
、body
、created_time
、modified_time
值,並把它和前麵創建的 Category 以及 User 關聯了起來。允許為空 excerpt
、tags
我們就沒有為它們指定值了。
注意:我們這裏使用 get
方法根據 Category
的 name
屬性的值獲取分類的一條記錄。Category.objects.get(name='category test')
的含義是從數據庫中取出 name
的值為 category test 的分類記錄。確保數據庫中隻有一條值為 category test 的記錄,否則 get
方法將返回一個 MultipleObjectsReturned
異常。如果你不小心已經存了多條記錄,請刪掉多餘的記錄。如何刪除數據請看下文。
取數據
數據已經存入數據庫了,現在要把它們取出來看看:
>>> Category.objects.all()
<QuerySet [<Category: Category object>]>
>>> Tag.objects.all()
<QuerySet [<Tag: Tag object>]>
>>> Post.objects.all()
<QuerySet [<Post: Post object>]>
>>>
objects
是我們的模型管理器,它為我們提供一係列從數據庫中取數據方法,這裏我們使用了 all
方法,表示我們要把對應的數據全部取出來。可以看到 all
方法都返回了數據,這些數據應該是我們之前存進去的,但是顯示的字符串有點奇怪,無法看出究竟是不是我們之前存入的數據。為了讓顯示出來的數據更加人性化一點,我們為 3 個模型分別增加一個 __str__
方法:
blog/models.py
from django.utils.six import python_2_unicode_compatible
# python_2_unicode_compatible 裝飾器用於兼容 Python2
@python_2_unicode_compatible
class Category(models.Model):
...
def __str__(self):
return self.name
@python_2_unicode_compatible
class Tag(models.Model):
...
def __str__(self):
return self.name
@python_2_unicode_compatible
class Post(models.Model):
...
def __str__(self):
return self.title
定義好 __str__
方法後,解釋器顯示的內容將會是 __str__
方法返回的內容。這裏 Category
返回分類名 name
,Tag
返回標簽名,而 Post
返回它的 title
。
python_2_unicode_compatible
裝飾器用於兼容 Python2。如果你使用的 Python3 開發環境,去掉這個裝飾器不會有任何影響。如果你使用的 Python2 開發環境,而又不想使用這個裝飾器,則將 __str__
方法改為 __unicode__
方法即可。
先按 Ctrl + c 退出 Shell,再重新運行 python manage.py shell
進入 Shell。
>>> from blog.models import Category, Tag, Post
>>> Category.objects.all()
<QuerySet [<Category: category test>]>
>>> Tag.objects.all()
<QuerySet [<Tag: tag test>]>
>>> Post.objects.all()
<QuerySet [<Post: title test>]>
>>> Post.objects.get(title='title test')
<Post: title test>
可以看到返回的是我們之前存入的數據。
此外我們在創建文章時提到了通過 get
方法來獲取數據,這裏 all
方法和 get
方法的區別是:all
方法返回全部數據,是一個類似於列表的數據結構(QuerySet);而 get
返回一條記錄數據,如有多條記錄或者沒有記錄,get
方法均會拋出相應異常。
改數據
嚐試修改數據:
>>> c = Category.objects.get(name='category test')
>>> c.name = 'category test new'
>>> c.save()
>>> Category.objects.all()
<QuerySet [<Category: test category new>]>
首先通過 get
方法根據分類名 name
獲取值為 category test 到分類,修改它的 name
屬性為新的值 category test new,然後調用 save
方法把修改保存到數據庫,之後可以看到數據庫返回的數據已經是修改後的值了。Tag
、Post
的修改也一樣。
刪數據
刪除掉數據:
>>> p = Post.objects.get(title='title test')
>>> p
<Post: title test>
>>> p.delete()
(1, {'blog.Post_tags': 0, 'blog.Post': 1})
>>> Post.objects.all()
<QuerySet []>
先根據標題 title
的值從數據庫中取出 Post
,保存在變量 p
中,然後調用它的delete
方法,最後看到 Post.objects.all()
返回了一個空的 QuerySet(類似於一個列表),表明數據庫中已經沒有 Post,Post 已經被刪除了。
這就是 Django 對數據庫增、刪、改、查的操作。除了上述演示的方法外,Django 還為我們提供了大量其它的方法,這些方法有一部分會在教程中使用,用到時我會講解它們的用法。但以後你開發自己的項目時,你就需要通過閱讀 Django 的官方文檔 來了解有哪些方法可用以及如何使用它們。
總結
本章節的代碼位於:Step4: make migrations and migrate。
如果遇到問題,請通過下麵的方式尋求幫助。
- 在 讓 Django 完成翻譯:遷移數據庫 - 追夢人物的博客 的評論區留言。
- 將問題的詳細描述通過郵件發送到 djangostudyteam@163.com,一般會在 24 小時內回複。
更多Django 教程,請訪問 追夢人物的博客。
最後更新:2017-05-26 18:31:30