# 第九節:ORM模型遷移
# ORM模型遷移
## 遷移命令:
1. makemigrations:將模型生成遷移腳本。模型所在的`app`,必須放在`settings.py`中的`INSTALLED_APPS`中。這個命令有以下幾個常用選項:
- app\_label:后面可以跟一個或者多個`app`,那么就只會針對這幾個app生成遷移腳本。如果沒有任何的app\_label,那么會檢查`INSTALLED_APPS`中所有的app下的模型,針對每一個app都生成響應的遷移腳本。
- --name:給這個遷移腳本指定一個名字。
- --empty:生成一個空的遷移腳本。如果你想寫自己的遷移腳本,可以使用這個命令來實現一個空的文件,然后自己再在文件中寫遷移腳本。
2. migrate:將新生成的遷移腳本。映射到數據庫中。創建新的表或者修改表的結構。以下一些常用的選項:
- app\_label:將某個`app`下的遷移腳本映射到數據庫中。如果沒有指定,那么會將所有在`INSTALLED_APPS`中的`app`下的模型都映射到數據庫中。
- app\_label migrationname:將某個`app`下指定名字的`migration`文件映射到數據庫中。
- --fake:可以將指定的遷移腳本名字添加到數據庫中。但是并不會把遷移腳本轉換為SQL語句,修改數據庫中的表。
- --fake-initial:將第一次生成的遷移文件版本號記錄在數據庫中。但并不會真正的執行遷移腳本。
3. showmigrations:查看某個app下的遷移文件。如果后面沒有app,那么將查看`INSTALLED_APPS`中所有的遷移文件。
4. sqlmigrate:查看某個遷移文件在映射到數據庫中的時候,轉換的`SQL`語句。
## migrations中的遷移版本和數據庫中的遷移版本對不上怎么辦?
1. 找到哪里不一致,然后使用`python manage.py --fake [版本名字]`,將這個版本標記為已經映射。
2. 刪除指定`app`下`migrations`和數據庫表`django_migrations`中和這個`app`相關的版本號,然后將模型中的字段和數據庫中的字段保持一致,再使用命令`python manage.py makemigrations`重新生成一個初始化的遷移腳本,之后再使用命令`python manage.py makemigrations --fake-initial`來將這個初始化的遷移腳本標記為已經映射。以后再修改就沒有問題了。
更多關于遷移腳本的。請查看官方文檔:<https://docs.djangoproject.com/en/2.0/topics/migrations/>
## 根據已有的表自動生成模型:
在實際開發中,有些時候可能數據庫已經存在了。如果我們用`Django`來開發一個網站,讀取的是之前已經存在的數據庫中的數據。那么該如何將模型與數據庫中的表映射呢?根據舊的數據庫生成對應的`ORM`模型,需要以下幾個步驟:
1. `Django`給我們提供了一個`inspectdb`的命令,可以非常方便的將已經存在的表,自動的生成模型。想要使用`inspectdb`自動將表生成模型。首先需要在`settings.py`中配置好數據庫相關信息。不然就找不到數據庫。示例代碼如下:
```
<pre class="calibre12">```
DATABASES = {
<span class="hljs-string">'default'</span>: {
<span class="hljs-string">'ENGINE'</span>: <span class="hljs-string">'django.db.backends.mysql'</span>,
<span class="hljs-string">'NAME'</span>: <span class="hljs-string">"migrations_demo"</span>,
<span class="hljs-string">'HOST'</span>: <span class="hljs-string">'127.0.0.1'</span>,
<span class="hljs-string">'PORT'</span>: <span class="hljs-string">'3306'</span>,
<span class="hljs-string">'USER'</span>: <span class="hljs-string">'root'</span>,
<span class="hljs-string">'PASSWORD'</span>: <span class="hljs-string">'root'</span>
}
}
```
```
比如有以下表:
- article表:

- tag表:

- article\_tag表:

- front\_user表:

那么通過`python manage.py inspectdb`,就會將表轉換為模型后的代碼,顯示在終端:
```
<pre class="calibre12">```
<span class="hljs-keyword">from</span> django.db <span class="hljs-keyword">import</span> models
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ArticleArticle</span><span class="hljs-params">(models.Model)</span>:</span>
title = models.CharField(max_length=<span class="hljs-params">100</span>)
content = models.TextField(blank=<span class="hljs-keyword">True</span>, null=<span class="hljs-keyword">True</span>)
create_time = models.DateTimeField(blank=<span class="hljs-keyword">True</span>, null=<span class="hljs-keyword">True</span>)
author = models.ForeignKey(<span class="hljs-string">'FrontUserFrontuser'</span>, models.DO_NOTHING, blank=<span class="hljs-keyword">True</span>, null=<span class="hljs-keyword">True</span>)
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Meta</span>:</span>
managed = <span class="hljs-keyword">False</span>
db_table = <span class="hljs-string">'article_article'</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ArticleArticleTags</span><span class="hljs-params">(models.Model)</span>:</span>
article = models.ForeignKey(ArticleArticle, models.DO_NOTHING)
tag = models.ForeignKey(<span class="hljs-string">'ArticleTag'</span>, models.DO_NOTHING)
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Meta</span>:</span>
managed = <span class="hljs-keyword">False</span>
db_table = <span class="hljs-string">'article_article_tags'</span>
unique_together = ((<span class="hljs-string">'article'</span>, <span class="hljs-string">'tag'</span>),)
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ArticleTag</span><span class="hljs-params">(models.Model)</span>:</span>
name = models.CharField(max_length=<span class="hljs-params">100</span>)
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Meta</span>:</span>
managed = <span class="hljs-keyword">False</span>
db_table = <span class="hljs-string">'article_tag'</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FrontUserFrontuser</span><span class="hljs-params">(models.Model)</span>:</span>
username = models.CharField(max_length=<span class="hljs-params">100</span>)
telephone = models.CharField(max_length=<span class="hljs-params">11</span>)
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Meta</span>:</span>
managed = <span class="hljs-keyword">False</span>
db_table = <span class="hljs-string">'front_user_frontuser'</span>
```
```
以上代碼只是顯示在終端。如果想要保存到文件中。那么可以使用`>`重定向輸出到指定的文件。比如讓他輸出到`models.py`文件中。示例命令如下:
```
<pre class="calibre12">```
python manage.py inspectdb > models.py
```
```
以上的命令,只能在終端執行,不能在`pycharm->Tools->Run manage.py Task...`中使用。
如果只是想要轉換一個表為模型。那么可以指定表的名字。示例命令如下:
```
<pre class="calibre12">```
python manage.py inspectdb article_article > models.py
```
```
2. 修正模型:新生成的`ORM`模型有些地方可能不太適合使用。比如模型的名字,表之間的關系等等。那么以下選項還需要重新配置一下:
- 模型名:自動生成的模型,是根據表的名字生成的,可能不是你想要的。這時候模型的名字你可以改成任何你想要的。
- 模型所屬app:根據自己的需要,將相應的模型放在對應的app中。放在同一個app中也是沒有任何問題的。只是不方便管理。
- 模型外鍵引用:將所有使用`ForeignKey`的地方,模型引用都改成字符串。這樣不會產生模型順序的問題。另外,如果引用的模型已經移動到其他的app中了,那么還要加上這個app的前綴。
- 讓Django管理模型:將`Meta`下的`managed=False`刪掉,如果保留這個,那么以后這個模型有任何的修改,使用`migrate`都不會映射到數據庫中。
- 當有多對多的時候,應該也要修正模型。將中間表注視了,然后使用`ManyToManyField`來實現多對多。并且,使用`ManyToManyField`生成的中間表的名字可能和數據庫中那個中間表的名字不一致,這時候肯定就不能正常連接了。那么可以通過`db_table`來指定中間表的名字。示例代碼如下:
```
<pre class="calibre12">```
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Article</span><span class="hljs-params">(models.Model)</span>:</span>
title = models.CharField(max_length=<span class="hljs-params">100</span>, blank=<span class="hljs-keyword">True</span>, null=<span class="hljs-keyword">True</span>)
content = models.TextField(blank=<span class="hljs-keyword">True</span>, null=<span class="hljs-keyword">True</span>)
author = models.ForeignKey(<span class="hljs-string">'front.User'</span>, models.SET_NULL, blank=<span class="hljs-keyword">True</span>, null=<span class="hljs-keyword">True</span>)
<span class="hljs-title"># 使用ManyToManyField模型到表,生成的中間表的規則是:article_tags</span>
<span class="hljs-title"># 但現在已經存在的表的名字叫做:article_tag</span>
<span class="hljs-title"># 可以使用db_table,指定中間表的名字</span>
tags = models.ManyToManyField(<span class="hljs-string">"Tag"</span>,db_table=<span class="hljs-string">'article_tag'</span>)
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Meta</span>:</span>
db_table = <span class="hljs-string">'article'</span>
```
```
- 表名:切記不要修改表的名字。不然映射到數據庫中,會發生找不到對應表的錯誤。
3. 執行命令`python manage.py makemigrations`生成初始化的遷移腳本。方便后面通過`ORM`來管理表。這時候還需要執行命令`python manage.py migrate --fake-initial`,因為如果不使用`--fake-initial`,那么會將遷移腳本會映射到數據庫中。這時候遷移腳本會新創建表,而這個表之前是已經存在了的,所以肯定會報錯。此時我們只要將這個`0001-initial`的狀態修改為已經映射,而不真正執行映射,下次再`migrate`的時候,就會忽略他。
4. 將`Django`的核心表映射到數據庫中:`Django`中還有一些核心的表也是需要創建的。不然有些功能是用不了的。比如`auth`相關表。如果這個數據庫之前就是使用`Django`開發的,那么這些表就已經存在了。可以不用管了。如果之前這個數據庫不是使用`Django`開發的,那么應該使用`migrate`命令將`Django`中的核心模型映射到數據庫中。
- Introduction
- 第一章:學前準備
- 第一節:虛擬環境
- 第二節:準備工作
- 第三節:Django介紹
- 第四節:URL組成部分
- 第二章:URL與視圖
- 第一節:第一個Django項目
- 第二節:視圖與URL分發器
- 第三章:模板
- 第一節:模板介紹
- 第二節:模板變量
- 第三節:常用標簽
- 第四節:常用過濾器
- 第五節:自定義過濾器
- 第七節:模版結構優化
- 第八節:加載靜態文件
- 第四章:數據庫
- 第一節:MySQL相關軟件
- 第二節:數據庫操作
- 第三節:ORM模型
- 第四節:模型常用字段
- 第五節:外鍵和表關系
- 第六節:增刪改查操作
- 第七節:查詢操作
- 第八節:QuerySet API
- 第九節:ORM模型遷移
- 第十節:ORM作業
- 第十一節:ORM作業參考答案
- 第十二節:Pycharm連接數據庫
- 第五章:視圖高級
- 第一節:限制請求method
- 第二節:頁面重定向
- 第三節:HttpRequest對象
- 第四節:HttpResponse對象
- 第五節:生成CSV文件
- 第六節:類視圖
- 第七節:錯誤處理
- 第六章:表單
- 第一節:表單概述
- 第二節:用表單驗證數據
- 第三節:ModelForm
- 第四節:文件上傳
- 第七章:cookie和session
- 第八章:上下文處理器和中間件
- 第一節:上下文處理器
- 第二節:中間件
- 第九章:安全
- 第一節:CSRF攻擊
- 第二節:XSS攻擊
- 第三節:點擊劫持攻擊
- 第四節:SQL注入
- 第十章:信號
- 第一節:什么是信號
- 第十一章:驗證和授權
- 第一節:概述
- 第二節:用戶對象
- 第三節:權限和分組
- 第十二章:Admin系統
- 第十三章:Django的緩存
- 第十四章:memcached
- 第十五章:Redis