使用makemigration,使Django自動產生修改DB的script,意外發現有個坑,會讓Django誤判,導致原本只是改名稱,變成新增欄位。
在用Django
,可以很快速使用manage.py
去產生相對應的.py檔
,讓我們可以不用動SQL script就可以修改DB的資料結構。
就用的很開心的情況下,他背叛了我的期待 Q口Q
遇到的情況是:
我修改了兩個欄位名稱,包含在程式碼和MySQL Database內的欄位名稱(因為當初沒有想好,所以欄位名稱就改動了XD),結果發現,資料都不見了,只有預設的資料存在DB裡面。到底為啥會這樣呢?
細查發現,原來是「name
」的問題!
解答:
先說結果:因為同時修改models.py
內的變數名稱,以及Table的欄位名稱,所以導致Django
認為這是要移除舊的欄位,新增欄位。
狀況是這樣,因為我一開始的直接修改models.py
裡面的資料,把customer_type1
改成customer_type
,customer_type2
改成customer_sub_type
,而且也改了db_column
。
class Customer(models.Model):
customer_type = models.CharField(
max_length=2, default="1", db_column="CUSTOMER_TYPE"
)
customer_type2 = models.CharField(
max_length=1, default="0", db_column="CUSTOMER_TYPE2"
)
然後自動產生的.py
檔如下:
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('account', '0007_customer_customer_order_no'),
]
operations = [
migrations.RemoveField(
model_name='customer',
name='customer_type1',
),
migrations.AddField(
model_name='customer',
name='customer_type',
field=models.CharField(db_column='CUSTOMER_TYPE', default='1', max_length=2),
),
migrations.RemoveField(
model_name='customer',
name='customer_type2',
),
migrations.AddField(
model_name='customer',
name='customer_sub_type',
field=models.CharField(db_column='CUSTOMER_SUB_TYPE', default='0', max_length=1),
),
]
當初就是對他太信任,沒有注意到奇怪的地方,就直接進行:python manage.py migrate
,之後我的資料就都不見了,原本的customer_type1
和customer_type2
,轉換後都變成預設值0
和1
。
仔細研究發現,他居然是RemoveField
舊的欄位,然後再AddField
成新的欄位,所以並不是改名字,而是新增欄位,因此舊的資料都不見,全部變成預設值。
所以我這邊後續,要做的就是把資料結構和資料還原,重新製作步驟八(0008
)。
重新製作
首先,進行還原作業,包含格式和資料。
用manage.py
把資料結構回到上個migration
。會需要這樣做的原因是,Django
會在MySQL
中記錄現在執行到哪一步,他會把缺的部份自己補齊,所以我們要經由manage.py
回復到我們需要的版本,並且跟MySQL
說要更換記錄點。程式碼格式為:python + manage.py + migrate + {app_name} + {還原點}
,我的案例的話就會如下:
python manage.py migrate account 0007_customer_customer_order_no
Database的資料結構回去後,再從備份資料的地方把資料回倒,確定資料都是齊全的(還好我先上測試環境,不然直接上正式,資料都不見了XD)。
接著重新製作我們的script。這邊的作法就是一次修改一個部分的name
。
- 會先修改的是
db_column
,把CUSTOMER_TYPE1
變成CUSTOMER_TYPE
,CUSTOMER_TYPE2
變成CUSTOMER_SUB_TYPE
- 接著是python程式碼內的
name
。customer_type1
變成customer_type
,customer_type2
變成customer_sub_type
然後執行:python manage.py makemigrations
可以看出來Django
製作出這樣的指令:
account/migrations/0008_auto_20201118_1644.py
- Alter field customer_type1 on customer
- Alter field customer_type2 on customer
接著檢查我們的.py
檔,沒錯,就是只是改欄位而已:
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('account', '0007_customer_customer_order_no'),
]
operations = [
migrations.AlterField(
model_name='customer',
name='customer_type1',
field=models.CharField(db_column='CUSTOMER_TYPE', default='1', max_length=2),
),
migrations.AlterField(
model_name='customer',
name='customer_type2',
field=models.CharField(db_column='CUSTOMER_SUB_TYPE', default='0', max_length=1),
)
]
接下來,改我們程式內的名稱,customer_type1
變成customer_type
,customer_type2
變成customer_sub_type
:
class Customer(models.Model):
...
customer_type = models.CharField(
max_length=2, default="1", db_column="CUSTOMER_TYPE"
)
customer_sub_type = models.CharField(
max_length=1, default="0", db_column="CUSTOMER_SUB_TYPE"
)
...
接著再執行:python manage.py makemigrations
,就會顯示兩個改名的提示:(為啥之前沒注意到沒有跳出來呢?QQ)
Did you rename customer.customer_type2 to customer.customer_sub_type (a CharField)? [y/N]
Did you rename customer.customer_type1 to customer.customer_type (a CharField)? [y/N]
回答完後,Django
就會自行作業囉~
account/migrations/0009_auto_20201118_1649.py
- Rename field customer_type2 on customer to customer_sub_type
- Rename field customer_type1 on customer to customer_type
作業完畢後,檢查一下.py
檔
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('account', '0008_auto_20201118_1644'),
]
operations = [
migrations.RenameField(
model_name='customer',
old_name='customer_type2',
new_name='customer_sub_type',
),
migrations.RenameField(
model_name='customer',
old_name='customer_type1',
new_name='customer_type',
),
]
就會有:old_name='customer_type2', new_name='customer_sub_type'
等等的字樣,就表示真的是改名了。接著跑:python manage.py migrate
來把我們這兩個0008
和0009
的.py
檔改變MySQL
的欄位名稱。
當然要檢查一下結果囉!
看起來很棒!資料都有順利過去了,這樣就大功告成!!
心得:
Django可以讓我們很方便,但是在真的執行前,需要看一下改變DB的script是否正確,不然會跟我一樣,還好有備份,不然就欲哭無淚....
~Copyright by Eyelash500~
IT技術文章:EY*研究院
iT邦幫忙:eyelash*睫毛
Blog:睫毛*Relax
Facebook:睫毛*Relax