如何使用ManyToManyField编写更改模型主键的迁移



我有一个UserProfile模型,它引用了带有OneToOneFieldUser模型。当创建用户时,我还使用post_save信号来自动创建UserProfile。当我收到关于重复配置文件的错误时,除了通过管理员创建用户(我使用内联)之外,这一点非常有效。此答案建议将主键设置为引用用户的OneToOneField。

所以之前:

class UserProfile(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL)
    # ...
    subjects = models.ManyToManyField(Subject, null=True, blank=True)

之后

class UserProfile(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL, primary_key=True)
    # ...
    subjects = models.ManyToManyField(Subject, null=True, blank=True)

我正试图使用Django 1.7中的迁移来实现这一点,但由于概要文件有许多ManyToManyField,所以生活变得复杂起来——所以它们都引用了UserProfile模型的id字段。使用makemigrations创建迁移,使用户成为主键,并删除旧的id字段,但它忽略了ManyToManyField。

我目前正在经历一个兔子洞,在迁移中使用大量RunSQL语句来修改ManyToManyField的直通表。我刚刚遇到另一个错误,其中约束的名称在一个表中与另一个表不同。

所以我的问题是:Django迁移中是否有一种方法可以更改直通表,使其引用新的主键,更新所有约束、键等?如果没有,处理这种情况的最佳方法是什么?

我使用Django 1.7和MySQL。

所以我最终用SQL来修复它。我的解决方案的核心如下-基本上是

  • 在新配置文件中对user_id创建索引
    • 该索引必须存在,我才能将其作为外键引用
  • 创建新的直通表
    • 我从SHOW CREATE TABLE userprofile_userprofile_subjects(MySQL特定)的输出开始
    • 我稍微修改了键名称和约束名称
  • 将所有数据复制到新的直通表中
  • 把旧桌子放下
  • 重命名新的直通表,使其具有旧直通表的名称
  • 最后做django迁移自动为我生成的操作

我希望这能帮助其他人。我仍然有兴趣了解一个更好的解决方案。

from django.db import migrations
class Migration(migrations.Migration):
    dependencies = [
        # ...
    ]
    operations = [
        migrations.RunSQL(
            'ALTER TABLE userprofile_userprofile '
            'ADD INDEX `userprofile_userprofile_1234abcd` (user_id)'
        ),
        migrations.RunSQL (
            'CREATE TABLE userprofile_temp_table ('
            '`id` int(11) NOT NULL AUTO_INCREMENT, '
            '`userprofile_id` int(11) NOT NULL, '
            '`subject_id` int(11) NOT NULL, '
            'PRIMARY KEY (`id`), '
            'UNIQUE KEY `userprofile_userprofile_subjects_userprofile_us_7ded3060_uniq` (`userprofile_id`,`subject_id`), '
            'KEY `userprofile_userprofile_subject_1be9924f` (`userprofile_id`), '
            'KEY `userprofile_userprofile_subject_e5a9504a` (`subject_id`), '
            'CONSTRAINT `subject_id_refs_id_69796996` FOREIGN KEY (`subject_id`) REFERENCES `otherapp_subject` (`id`), '
            'CONSTRAINT `userprofile_user_id_refs_user_id_1234abcd` FOREIGN KEY (`userprofile_id`) REFERENCES `userprofile_userprofile` (`user_id`) '
            ') ENGINE=InnoDB AUTO_INCREMENT=35500 DEFAULT CHARSET=utf8 '
        ),
        migrations.RunSQL (
            'INSERT INTO userprofile_temp_table '
            '(userprofile_id, subject_id) '
            '('
            '  SELECT userprofile_userprofile.user_id, userprofile_userprofile_subjects.subject_id'
            '    FROM userprofile_userprofile_subjects'
            '    INNER JOIN userprofile_userprofile'
            '    ON userprofile_userprofile_subjects.userprofile_id ='
            '        userprofile_userprofile.id'
            ')'
        ),
        migrations.RunSQL (
            'DROP TABLE `userprofile_userprofile_subjects`'
        ),
        migrations.RunSQL (
            'RENAME TABLE `userprofile_temp_table` TO `userprofile_userprofile_subjects`'
        ),
        migrations.RemoveField(
            model_name='userprofile',
            name='id',
        ),
        migrations.AlterField(
            model_name='userprofile',
            name='user',
            field=models.OneToOneField(
                primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL
            ),
            preserve_default=True,
        ),
    ]

相关内容

  • 没有找到相关文章

最新更新