我有一个UserProfile模型,它使用OneToOneField引用我的User模型.我还使用post_save信号在创建用户时自动创建UserProfile.当我收到有关重复配置文件的错误时,除了通过管理员创建用户(我使用内联)时,这很有用. 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.
我目前正在迁移中使用大量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,
),
]