问题背景
在对django项目进行版本控制的时候,因为在编写app的某些阶段会涉及到一些manage=False的旧数据表,这时必须手动使用navicat/执行sql语句进行数据库迁移
这样一来导致一部分models使用migrations可以执行自动迁移,另一部分models则需要手动同步数据库,由于对django项目数据库迁移多人协作最佳的工作流程目前还不是很明确,以及不同models之间相互的引用耦合,在下拉更新后执行migrate的时候总会出现一些奇奇怪怪的依赖问题。这次希望通过重新构建以及整理一遍migrations的流程,探索一下在版本控制下的最佳的迁移流程。
问题一:如何重置migrations,让每个app的migrations的依赖关系更加明确
1. 查看当前迁移状态
python manage.py showmigrations
其中展示的信息为当前项目INSTALLED_APPS中所有的app的迁移文件的列表, 其中[X]
代表已经migrate了的migration
优先列出项目内创建的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. 重置迁移状态
$ python manage.py runserver
确保程序在当前状态是可以执行的
如果报错则先对某些指定的app进行migrate$ python manage.py migrate
之后再进行下一步操作
$ python manage.py migrate --fake
将某个app的迁移状态全部标记为未执行,即把上面所有的[X]
全部变为[ ]
3. 删除迁移文件
将所有的迁移文件删除
- 对于项目内构建的app,默认位置在
my_project/
内,可以将整个migrations文件夹删除,或者建议用文件夹改名的方式之后如果报错还可以还原/migrations/
之后观察$ python manage.py showmigrations
在对应的app位置会显示没有任何migtrations - 对于第三方app,则在当前运行环境的site-packages/
/migrations/`内,可以将整个migrations文件夹删除,或者建议用文件夹改名的方式之后如果报错还可以还原
注: 引用的app的APP_NAME为实际app的名称,不需要加全地址,例如django.contrib.admin 的APP_NAME为 admin
4. 重新构建迁移文件
$ python manage.py makemigrations
/ $ python manage.py makemigrations
注: 引用的app的APP_NAME为实际app的名称,不需要加全地址,例如django.contrib.admin 的APP_NAME为 admin
对所有app进行构建之后变成这样:每个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/指定app第一条初始化的migration标记为[X]
,并不实际执行migration操作
$ python manage.py migrate --fake [
# 这条命令会将所每个app/指定app的所有migrations标记为[X]
,并不实际执行migration操作
参数备注: <...> 表示里面的内容为变量, 需要替换为实际的字符串, [...] 表示该参数可选可不选
Tips:
已经执行的迁移记录会被记录在settings.DATABASES, "default"alias定义的数据库connection里的django_migrations里,这个记录大致上就是showmigratons判断哪条migration需要标记[X]
的依据,从而决定在migrate时需要执行哪些没有的
总的来说:
- migrations files -> 位于不同app的位置,用于定义每个migration的详细操作
- django_migrations数据库表记录 -> 标记migrations files哪些被执行了,以及执行时间,仅此而已
问题二:如何对引用外部app(非在项目内创建的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/