为了账号安全,请及时绑定邮箱和手机立即绑定

在保留数据的同时将 Django 外键移动到另一个模型?

在保留数据的同时将 Django 外键移动到另一个模型?

叮当猫咪 2023-06-20 13:50:45
我有一个 Django 应用程序,其中有一个基本用户模型和用户配置文件模型。class Office(models.Model):    name = models.CharField()class User(models.Model):    email = models.EmailField()    offices = models.ManyToManyField(Office)class Profile(models.Model):    user = models.OneToOneField(User)每个用户都有一个配置文件,可以分配给许多不同的办公室。我想要做的是拥有它,以便用户可以拥有多个配置文件,但配置文件可以分配给不同的办公室。目前,这是不可能的,因为 Office 字段是在用户级别而不是配置文件级别分配的。我设想了 2 种方法来解决这个问题:想办法将该offices字段移到配置文件字段。创建一个新模型,其中ProfileNoUser包含用户模型的外键以及offices其中的外键。您认为哪个选项最好?如果#1,我该怎么做?
查看完整描述

1 回答

?
Cats萌萌

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.


查看完整回答
反对 回复 2023-06-20
  • 1 回答
  • 0 关注
  • 75 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信