是否可以重新创建从 Django 模型中意外删除的 table 字段,而无需拆除所有迁移?
Is it possible to recreate a table field that was accidentally removed from Django Model, without tearing down all migrations?
假设以下模型:
class Country(models.Model):
name = models.TextField(null=True, blank=True)
continent = models.TextField(null=True, blank=True)
population = models.PositiveIntegerField(null=True, blank=True)
然后我:
- 从
models.py
中删除字段 population
。
- 创建迁移:
python manage.py makemigrations countries_app
- 执行迁移,将字段标记为已删除:
python3 manage.py migrate countries_app
这是迁移文件 0006
,它是上次创建的迁移 (0005
) 的序列:
class Migration(migrations.Migration):
dependencies = [
('countries_app', '0005_auto_20210723_0007'),
]
operations = [
migrations.RemoveField(
model_name='country',
name='population',
),
]
现在,我的数据库中没有 population
字段,但我意识到从 Country
模型中删除 population
字段是一个错误。
为了让事情恢复正常,我在模型上再次定义了该字段,我取消了迁移 0006
,并且还删除了 0006
迁移文件,其中定义了删除的领域,我也执行 python manage.py migrate countries_app
,它说 nothing to migrate
,这是有道理的。
$ python3 manage.py migrate countries_app
Operations to perform:
Apply all migrations: countries_app
Running migrations:
Applying countries_app.0006_remove_country_population... OK
$ rm countries_app/migrations/0006_remove_country_population.py
$ python manage.py migrate countries_app
但是当我查看数据库table时,仍然没有字段population
。我认为 Django 会重新创建它,因为我未应用迁移 0006
并且该字段再次在我的模型上定义。
我在 SO 上看到了一些答案,指示如下:取消应用所有迁移(python manage.py migrate countries_app zero
),删除所有迁移文件,再次创建初始迁移然后迁移。
这对我来说太残酷了。我想指示 Django 重新创建不再存在的字段。有没有有效和更温和的方法来做到这一点?我对 Django Migrations 不是很了解,所以请多多包涵。谢谢。
您当然可以将该字段添加回模型并再次使用 makemigrations
,然后向前迁移 (0006 -> 0007)。这会将列定义回数据库,但您必须用有效数据重新填充它。一个简单的迁移只会用默认值填充它。
经过一些调查和测试,从技术上讲,通过取消应用迁移 0006
(回滚到 0005
),Django 重新创建了曾经被删除的字段。我用另一个项目做了本地测试,是的,是的。
为什么回滚到迁移 0005
在我的案例中没有成功,这是未知的。我可能在我的 real 项目中搞砸了一些东西。因此,作为回答,我声明,通过回滚到以前的迁移,通常是您在这些情况下所需要的。
但是,如果事情真的一团糟并且 Django 没有重新创建曾经被删除但现在又回到您的模型中的字段,我看到 2 个备选方案:
Nigel222 的回答。它会起作用,但仍然会带来额外的迁移,只是为了重新创建字段。
连接到数据库并手动创建字段。这将修复发生的异常,并且不会有额外的迁移来单独重新创建一个字段,该字段应该在迁移回滚时再次返回:
ALTER TABLE countries_app_country ADD population INTEGER;
假设以下模型:
class Country(models.Model):
name = models.TextField(null=True, blank=True)
continent = models.TextField(null=True, blank=True)
population = models.PositiveIntegerField(null=True, blank=True)
然后我:
- 从
models.py
中删除字段population
。 - 创建迁移:
python manage.py makemigrations countries_app
- 执行迁移,将字段标记为已删除:
python3 manage.py migrate countries_app
这是迁移文件 0006
,它是上次创建的迁移 (0005
) 的序列:
class Migration(migrations.Migration):
dependencies = [
('countries_app', '0005_auto_20210723_0007'),
]
operations = [
migrations.RemoveField(
model_name='country',
name='population',
),
]
现在,我的数据库中没有 population
字段,但我意识到从 Country
模型中删除 population
字段是一个错误。
为了让事情恢复正常,我在模型上再次定义了该字段,我取消了迁移 0006
,并且还删除了 0006
迁移文件,其中定义了删除的领域,我也执行 python manage.py migrate countries_app
,它说 nothing to migrate
,这是有道理的。
$ python3 manage.py migrate countries_app
Operations to perform:
Apply all migrations: countries_app
Running migrations:
Applying countries_app.0006_remove_country_population... OK
$ rm countries_app/migrations/0006_remove_country_population.py
$ python manage.py migrate countries_app
但是当我查看数据库table时,仍然没有字段population
。我认为 Django 会重新创建它,因为我未应用迁移 0006
并且该字段再次在我的模型上定义。
我在 SO 上看到了一些答案,指示如下:取消应用所有迁移(python manage.py migrate countries_app zero
),删除所有迁移文件,再次创建初始迁移然后迁移。
这对我来说太残酷了。我想指示 Django 重新创建不再存在的字段。有没有有效和更温和的方法来做到这一点?我对 Django Migrations 不是很了解,所以请多多包涵。谢谢。
您当然可以将该字段添加回模型并再次使用 makemigrations
,然后向前迁移 (0006 -> 0007)。这会将列定义回数据库,但您必须用有效数据重新填充它。一个简单的迁移只会用默认值填充它。
经过一些调查和测试,从技术上讲,通过取消应用迁移 0006
(回滚到 0005
),Django 重新创建了曾经被删除的字段。我用另一个项目做了本地测试,是的,是的。
为什么回滚到迁移 0005
在我的案例中没有成功,这是未知的。我可能在我的 real 项目中搞砸了一些东西。因此,作为回答,我声明,通过回滚到以前的迁移,通常是您在这些情况下所需要的。
但是,如果事情真的一团糟并且 Django 没有重新创建曾经被删除但现在又回到您的模型中的字段,我看到 2 个备选方案:
Nigel222 的回答。它会起作用,但仍然会带来额外的迁移,只是为了重新创建字段。
连接到数据库并手动创建字段。这将修复发生的异常,并且不会有额外的迁移来单独重新创建一个字段,该字段应该在迁移回滚时再次返回:
ALTER TABLE countries_app_country ADD population INTEGER;