Django Migrations¶
我们已经开始运行了一个简单的Django应用,你会注意到为了构建应用数据库,我们使用了命令 python manage.py migrate
。
Migrations概念¶
所谓 Migrations
是指Django将models的修改(添加字段、删除模型)转换到数据库schema(模式)的方法。migrate通常设计是自动的,但是你需要如何创建migrations,何时需要运行migrate,以及出现问题时如何解决。
migrations流程¶
构建 model
在Django中,我们不需要手工去创建数据库,而是先构建一个 model ,即在 models.py
中我们定义数据库结构
from django.db import models
class User(models.Model):
username = models.CharField(max_length=19)
email = models.CharField(max_length=100)
groups = models.CharField(max_length=100)
create_time = models.DateTimeField()
class Meta:
ordering = ('create_time',)
然后我们执行
makemigrations
创建对应的数据库initial.py:
python manage.py makemigrations
此时会自动在 migrations
目录下生成生成一个 0001_initial.py
文件,类似
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='GuestPanic',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('username', models.CharField(max_length=19)),
('email', models.CharField(max_length=100)),
('gruops', models.CharField(max_length=100)),
('create_time', models.DateTimeField()),
],
options={
'ordering': ('create_time',),
},
),
]
执行数据库操作:
python manage.py migrate
这个命令会根据生成的 0001_initial.py
对数据库进行操作,创建表格和对应字段。
检查项目的migrations以及状态:
python manage.py showmigrations
这个命令会输出所有的migrations以及状态(是否执行过)
makemigrations
报错处理¶
我在最近的一次实践中,对一个老项目重新部署,在完成数据库初步准备之后(创建数据库以及设置好账号密码),执行 python manage.py makemigrations
出现报错:
Traceback (most recent call last):
File "/home/admin/onesre/core/manage.py", line 22, in <module>
main()
File "/home/admin/onesre/core/manage.py", line 18, in main
execute_from_command_line(sys.argv)
File "/home/admin/onesre_venv3/lib/python3.11/site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line
utility.execute()
File "/home/admin/onesre_venv3/lib/python3.11/site-packages/django/core/management/__init__.py", line 436, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/home/admin/onesre_venv3/lib/python3.11/site-packages/django/core/management/base.py", line 412, in run_from_argv
self.execute(*args, **cmd_options)
File "/home/admin/onesre_venv3/lib/python3.11/site-packages/django/core/management/base.py", line 453, in execute
self.check()
File "/home/admin/onesre_venv3/lib/python3.11/site-packages/django/core/management/base.py", line 485, in check
all_issues = checks.run_checks(
^^^^^^^^^^^^^^^^^^
File "/home/admin/onesre_venv3/lib/python3.11/site-packages/django/core/checks/registry.py", line 88, in run_checks
new_errors = check(app_configs=app_configs, databases=databases)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/admin/onesre_venv3/lib/python3.11/site-packages/django/core/checks/urls.py", line 14, in check_url_config
return check_resolver(resolver)
^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/admin/onesre_venv3/lib/python3.11/site-packages/django/core/checks/urls.py", line 24, in check_resolver
return check_method()
^^^^^^^^^^^^^^
File "/home/admin/onesre_venv3/lib/python3.11/site-packages/django/urls/resolvers.py", line 494, in check
for pattern in self.url_patterns:
^^^^^^^^^^^^^^^^^
File "/home/admin/onesre_venv3/lib/python3.11/site-packages/django/utils/functional.py", line 57, in __get__
res = instance.__dict__[self.name] = self.func(instance)
^^^^^^^^^^^^^^^^^^^
File "/home/admin/onesre_venv3/lib/python3.11/site-packages/django/urls/resolvers.py", line 715, in url_patterns
patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module)
^^^^^^^^^^^^^^^^^^^
File "/home/admin/onesre_venv3/lib/python3.11/site-packages/django/utils/functional.py", line 57, in __get__
res = instance.__dict__[self.name] = self.func(instance)
^^^^^^^^^^^^^^^^^^^
File "/home/admin/onesre_venv3/lib/python3.11/site-packages/django/urls/resolvers.py", line 708, in urlconf_module
return import_module(self.urlconf_name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<frozen importlib._bootstrap>", line 1204, in _gcd_import
File "<frozen importlib._bootstrap>", line 1176, in _find_and_load
File "<frozen importlib._bootstrap>", line 1147, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 690, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 940, in exec_module
File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
File "/home/admin/onesre/core/core/urls.py", line 21, in <module>
path('api/', include('api.urls')),
^^^^^^^^^^^^^^^^^^^
File "/home/admin/onesre_venv3/lib/python3.11/site-packages/django/urls/conf.py", line 38, in include
urlconf_module = import_module(urlconf_module)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<frozen importlib._bootstrap>", line 1204, in _gcd_import
File "<frozen importlib._bootstrap>", line 1176, in _find_and_load
File "<frozen importlib._bootstrap>", line 1147, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 690, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 940, in exec_module
File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
File "/home/admin/onesre/core/api/urls.py", line 1, in <module>
from django.conf.urls import url, include
ImportError: cannot import name 'url' from 'django.conf.urls' (/home/admin/onesre_venv3/lib/python3.11/site-packages/django/conf/urls/__init__.py)
这个问题在 ImportError: cannot import name ‘url’ from ‘django.conf.urls’ after upgrading to Django 4.0 有解释,原因是Django 3.0升级到Django 4.0+之后已经废弃了 django.conf.urls.url()
。由于是老项目,我采用 pip (Python包管理器) Downgrade Django版本方式来解决
Migrations后端支持¶
Migrations屏蔽了Django使用的数据库后端差异,通过完全相同的 model ,我们可以配置不同的数据库后端,实现对不同数据库的schema构建和修改。
清空数据和重新migrations同步¶
在开发过程中,我们可能会需要清空数据库并重新migrate,步骤如下
删除项目的数据库表,这里举例是
api
项目(如果要保留数据,可以不执行这步)删除项目的migrations目录下所有文件,但保留
__init__.py
重建migrate初始化文件:
python manage.py makemigrations
检查migrate状态(这里
api
是项目名字):python manage.py showmigrations api
因为之前已经执行过migrate命令,所以同名的migrate都是已经执行状态,我们需要重置成空的状态:
python manage.py migrate --fake api zero
然后再次检查migrate状态就会看到 api
对应的migrate状态是空的:
python manage.py showmigrations api
重新生成migrate文件:
python manage.py makemigrations api
此时重新生成的 0001_initial.py
文件会反映修订过的 models.py
内容(假如你调整了数据库表结构)
重新执行数据库同步:
python manage.py migrate
备注
如果你只想重新生成migrate文件,但是不执行到数据库,则使用:
python manage.py --fake-initial api