1 回答

TA贡献1805条经验 获得超9个赞
您的问题是关于数据迁移吗?
如果是这样,您应该阅读链接的文档。
在我看来,您的第一个提案确实是最好的。
这是如何做的。
第 1 步:对模型进行更改
您更改模型以匹配您的目标:
# models.py
from django.db import models
class Office(models.Model):
name = models.CharField(max_length=1000)
class User(models.Model):
email = models.EmailField()
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
offices = models.ManyToManyField(Office)
运行makemigrations:
(.venv) ~/c/s/m/mysite> ./manage.py makemigrations
Migrations for 'so':
so/migrations/0002_auto_20200825_1311.py
- Remove field offices from user
- Add field offices to profile
第 2 步:编辑迁移文件以迁移现有数据
打开上一步创建的文件。它看起来像:
# Generated by Django 3.1 on 2020-08-25 13:11
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('so', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='user',
name='offices',
),
migrations.AddField(
model_name='profile',
name='offices',
field=models.ManyToManyField(to='so.Office'),
),
]
你需要在这里做两件事:
您可以看到,对于 Django 生成的命令,它将首先删除旧的 FK,然后添加新的。你应该改变它,所以它是相反的。
然后你将不得不编写一些 python 代码,迁移将在正确的时间执行
正确的时间是在创建新 FK 之后和旧 FK 被删除之前,这样您就可以访问这两个 FK 来迁移数据:
# Generated by Django 3.1 on 2020-08-25 13:11
from django.db import migrations, models
def migrate_office_to_profile(apps, schema_editor):
User = apps.get_model('so', 'User')
for user in User.objects.all():
for office in user.office_set:
user.profile.add(office)
def migrate_office_to_user(apps, schema_editor):
Profile = apps.get_model('so', 'Profile')
for profile in Profile.objects.all():
profile.user.add(profile.offices)
for office in profile.office_set:
profile.user.add(office)
class Migration(migrations.Migration):
dependencies = [
('so', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='profile',
name='offices',
field=models.ManyToManyField(to='so.Office'),
),
# This is where your python code will be called
migrations.RunPython(
# This is called in the forward migration
migrate_office_to_profile,
# This is called in the backward migration
reverse_code=migrate_office_to_user
),
migrations.RemoveField(
model_name='user',
name='offices',
),
]
我还没有在真实数据上测试过这个。这是你的部分。
第 3 步:运行和调试您的迁移
复制您的数据库,运行迁移并查看它是如何工作的:
(.venv) ~/c/s/m/mysite> ./manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions, so
Running migrations:
Applying so.0002_auto_20200825_1311... OK
在处理数据迁移时,您应该始终编写和调试反向迁移:
(.venv) ~/c/s/m/mysite> ./manage.py migrate so 0001
Operations to perform:
Target specific migration: 0001_initial, from so
Running migrations:
Rendering model states... DONE
Unapplying so.0002_auto_20200825_1311... OK
请记住,迁移文件被添加到版本控制和软件的一部分。
===编辑===
阐明反向或向后迁移的概念。
您的 Django 迁移通常只以一种方式运行:增加迁移数量。
但是对于您在开发过程中可能应用的所有测试和仔细思考,您不能总是考虑生产数据。代码很“简单”,因为它在生产中与在您的开发服务器上是一样的。但生产数据通常不同。
在部署期间 Django 迁移可能会失败。例如,因为您正在添加一个您认为只能为真的显式唯一约束。但是不知何故,对于生产数据,迁移会引发完整性错误。
然后,您将陷入半完成的迁移和没有迁移就无法运行的新代码。如果您花时间确保您的迁移可以前后运行而不会丢失数据,您可以安全地撤消迁移并恢复到以前的代码并返回以找出问题所在,而不必修复它当场。
对于模型结构,前向和后向迁移由 Django 自动处理,但是对于数据迁移,您必须编写给migrations.RunPython.
添加回答
举报