如何编写迁移以使用ManyToManyField更改模型的主键 [英] How to write migration to change primary key of model with ManyToManyField

查看:161
本文介绍了如何编写迁移以使用ManyToManyField更改模型的主键的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 UserProfile 模型,指的是我的用户模型,一个 OneToOneField 。创建用户时,我还使用 post_save 信号自动创建 UserProfile 。当我收到有关重复的配置文件的错误时,除了通过管理员创建用户(在我使用内联的方式)之外,还有很大的作用。 此回答建议将主键设置为参考用户的OneToOneField

I have a UserProfile model that refers to my User model with a OneToOneField. I also use the post_save signal to auto create the UserProfile when the user is created. This works great apart from when creating a user through the admin (where I use an inline) when I get an error about duplicate profile. This answer recommends setting the primary key to be the OneToOneField referring to user.

所以之前:

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。

I'm trying to do that using the migrations in Django 1.7, but life is complicated by the fact that the profile has a number of ManyToManyField - so they all refer to the id field of the UserProfile model. Using makemigrations creates the migrations for making the user the primary key, and dropping the old id field, but it ignores the ManyToManyField.

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

I'm currently going down a rabbit hole of using lots of RunSQL statements in the migration to modify the through table for the ManyToManyField. I've just hit another error where the name of the constraint is not the same in one table as another.

所以我的问题是:Django迁移中有没有方法这样做会改变通过表的工作,所以它指的是新的主键,更新所有的约束,键等等?如果没有,最好的方法是处理这种情况?

So my question is: is there a method in Django migrations that will do the work of changing the through table so it refers to the new primary key, updating all the constraints, keys etc? If not, what's the best way to handle this situation?

我在使用Django 1.7与MySQL。

I'm using Django 1.7 with MySQL.

推荐答案

所以我最后用SQL来修复它。我的解决方案的核心在于 - 基本上我

So I ended up with SQL to fix it. The core of my solution is below - basically I


  • 在新配置文件中为user_id创建一个索引


    • 此索引需要存在之前,我可以将其引用为外键


    • 我从的输出开始。SHOW CREATE TABLE userprofile_userprofile_subjects (MySQL specific)

    • 我稍微修改了密钥名称和约束名称

    • I started with the output of SHOW CREATE TABLE userprofile_userprofile_subjects (MySQL specific)
    • I modified the key names and constraint names slightly

    我希望这有助于别人。我仍然有兴趣了解更好的解决方案。

    I hope this helps someone else. And I'd still be interested to know about a better solution.

    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,
            ),
        ]
    

    这篇关于如何编写迁移以使用ManyToManyField更改模型的主键的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆