2020-12-08 Django Migrations 初探

問題背景

在對django項目進行版本控制的時候,因為在編寫app的某些階段會涉及到一些manage=False的舊數(shù)據(jù)表,這時必須手動使用navicat/執(zhí)行sql語句進行數(shù)據(jù)庫遷移
這樣一來導致一部分models使用migrations可以執(zhí)行自動遷移,另一部分models則需要手動同步數(shù)據(jù)庫,由于對django項目數(shù)據(jù)庫遷移多人協(xié)作最佳的工作流程目前還不是很明確,以及不同models之間相互的引用耦合,在下拉更新后執(zhí)行migrate的時候總會出現(xiàn)一些奇奇怪怪的依賴問題。這次希望通過重新構(gòu)建以及整理一遍migrations的流程,探索一下在版本控制下的最佳的遷移流程。

問題一:如何重置migrations,讓每個app的migrations的依賴關(guān)系更加明確

1. 查看當前遷移狀態(tài)

python manage.py showmigrations
其中展示的信息為當前項目INSTALLED_APPS中所有的app的遷移文件的列表, 其中[X]代表已經(jīng)migrate了的migration
優(yōu)先列出項目內(nèi)創(chuàng)建的app的migrations(字母順序) ,其次列出引用外部的app的migrations(字母順序)

# my_project/my_project/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'rest_framework.authtoken', # TOKEN 驗證
    'django_filters', # 查詢
    
    'django_celery_results', # Celery result backend
    'django_celery_beat', # Celery beat
    
    'my_app_1',
    'my_app_2',
]
$ python manage.py showmigrations
my_app_1
 [X] 0001_initial
 [X] 0002_some_migrations
 [ ] 0003_some_migrations
 [ ] 0004_some_migrations
 [ ] 0005_some_migrations
my_app_2
 [X] 0001_initial
 [X] 0002_some_migrations
 [X] 0003_some_migrations
 [ ] 0004_some_migrations
admin
 [X] 0001_initial
auth
 [X] 0001_initial
authtoken
 [X] 0001_initial
contenttypes
 [X] 0001_initial
django_celery_beat
 [X] 0001_initial
django_celery_results
 [X] 0001_initial
sessions
 [X] 0001_initial

2. 重置遷移狀態(tài)

$ python manage.py runserver 確保程序在當前狀態(tài)是可以執(zhí)行的
如果報錯則先對某些指定的app進行migrate$ python manage.py migrate <APP_NAME>之后再進行下一步操作
$ python manage.py migrate --fake <APP_NAME> zero 將某個app的遷移狀態(tài)全部標記為未執(zhí)行,即把上面所有的[X]全部變?yōu)?code>[ ]

3. 刪除遷移文件

將所有的遷移文件刪除

  • 對于項目內(nèi)構(gòu)建的app,默認位置在my_project/<APP_NAME>/migrations/內(nèi),可以將整個migrations文件夾刪除,或者建議用文件夾改名的方式之后如果報錯還可以還原
    之后觀察$ python manage.py showmigrations 在對應(yīng)的app位置會顯示沒有任何migtrations
  • 對于第三方app,則在當前運行環(huán)境的site-packages/<APP_FULL_PATH>/migrations/`內(nèi),可以將整個migrations文件夾刪除,或者建議用文件夾改名的方式之后如果報錯還可以還原

注: 引用的app的APP_NAME為實際app的名稱,不需要加全地址,例如django.contrib.admin 的APP_NAME為 admin

4. 重新構(gòu)建遷移文件

$ python manage.py makemigrations / $ python manage.py makemigrations <APP_NAME>
注: 引用的app的APP_NAME為實際app的名稱,不需要加全地址,例如django.contrib.admin 的APP_NAME為 admin
對所有app進行構(gòu)建之后變成這樣:每個app只有一個initial的migration,舒服了!

$ python manage.py showmigrations
my_app_1
 [ ] 0001_initial
my_app_2
 [ ] 0001_initial
admin
 [ ] 0001_initial
auth
 [ ] 0001_initial
authtoken
 [ ] 0001_initial
contenttypes
 [ ] 0001_initial
django_celery_beat
 [ ] 0001_initial
django_celery_results
 [ ] 0001_initial
sessions
 [ ] 0001_initial

5. 偽造遷移記錄

$ python manage.py migrate --fake-initial [<APP_NAME>] # 這條命令只會將每個app/指定app第一條初始化的migration標記為[X],并不實際執(zhí)行migration操作
$ python manage.py migrate --fake [<APP_NAME>] # 這條命令會將所每個app/指定app的所有migrations標記為[X],并不實際執(zhí)行migration操作
參數(shù)備注: <...> 表示里面的內(nèi)容為變量, 需要替換為實際的字符串, [...] 表示該參數(shù)可選可不選

Tips:

已經(jīng)執(zhí)行的遷移記錄會被記錄在settings.DATABASES, "default"alias定義的數(shù)據(jù)庫connection里的django_migrations里,這個記錄大致上就是showmigratons判斷哪條migration需要標記[X]的依據(jù),從而決定在migrate時需要執(zhí)行哪些沒有的
總的來說:

  • migrations files -> 位于不同app的位置,用于定義每個migration的詳細操作
  • django_migrations數(shù)據(jù)庫表記錄 -> 標記migrations files哪些被執(zhí)行了,以及執(zhí)行時間,僅此而已

問題二:如何對引用外部app(非在項目內(nèi)創(chuàng)建的app)的migrations進行版本控制

TODO

參考

How to Reset Migrations
How to store third party apps migrations in django

官方文檔 settings.MIGRATION_MODULES
TODO:
簡化對managed=False的Model的測試流程:
https://www.caktusgroup.com/blog/2010/09/24/simplifying-the-testing-of-unmanaged-database-models-in-django/

系列教程: Django Migrations詳細解釋
https://realpython.com/django-migrations-a-primer/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容