3

我有一個UserProfile模型,該模型引用了我的User模型並使用OneToOneField。在創建用戶時,我也使用post_save信號自動創建UserProfile。當我得到一個關於重複配置文件的錯誤時,除了通過管理員創建用戶(我使用內聯)之外,這很有效。 This answer recommends setting the primary key to be the OneToOneField referring to user如何使用ManyToManyField編寫遷移以更改模型的主鍵

所以前:

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。

+1

我想說的解決方案是不正確的。您的信號應該修改爲不要嘗試創建重複的配置文件。主鍵解決方案對我來說似乎很難受。 –

+0

@Kye - 信號處理程序在管理內聯嘗試創建配置文件之前創建配置文件**。因此,信號處理程序必須確定用戶是由管理員創建的,以便知道管理員然後會嘗試創建用戶 - 它不能簡單地測試以查看數據庫中是否已存在配置文件。如果配置文件在創建之前已經存在,那麼可以選擇管理內聯測試,但這對我來說很不好。 –

+0

@HamishDowner看起來像它的一個bug:https://code.djangoproject.com/ticket/25012 – Anupam

回答

2

所以我最終用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, 
     ), 
    ] 
+1

感謝@Hamish - 你的解決方案幫助我決定不使用自定義主鍵與具有m2m字段的模型:)面對一些錯誤([#25012](https://code.djangoproject.com/ticket/25012),[#24030](https://code.djangoproject.com/ticket/24030),[#22997] (https://code.djangoproject.com/ticket/22997))。詳情[here](http://stackoverflow.com/questions/43253602/django-short-non-linear-non-predictable-id-in-the-url)。 – Anupam

相關問題