2016-12-16 101 views
6

我已經創建了電子郵件地址的模型自定義主鍵如下:如何從自定義主鍵遷移到默認密碼

email = models.EmailField(max_length=255, primary_key=True,) 

現在我意識到,這是不是在我的情況,我是個好主意想要回到自動生成的ID字段作爲主鍵。

如何做到這一點?我以不同的方式嘗試過,但都失敗了。我在Python 3.4.3中使用Django 1.10.4和SQLite數據庫。

  1. 我剛剛用unique = True替換了primary_key = True選項。 python manage.py makemigrations抱怨:

您正在嘗試爲用戶添加一個不可空的字段'id',而沒有默認值;我們不能這樣做(數據庫需要一些東西來填充現有的行)。

如果我指定0爲默認值,python manage.py migrate失敗django.db.utils.IntegrityError: UNIQUE constraint failed: login_user.id

  • 基於該交Change Primary Key field to unique field我試圖手動添加自動字段,如:

    ID = models.AutoField()

  • 現在python manage.py makemigrations失敗:

    login.User.id: (fields.E100) AutoFields must set primary_key=True. 
    

    如果按照錯誤消息的建議執行操作,則會出現與第一次嘗試相同的問題:缺少默認值。

  • 我試圖(在https://docs.djangoproject.com/en/1.10/howto/writing-migrations/#migrations-that-add-unique-fields以下Django文檔),以使一個字段id = IntegerField(唯一=真),然後更改域類型爲下拉列表AutoField(primary_key =真)。同時,我需要將電子郵件字段更改爲unique = True,以避免使用兩個主鍵。這些更改後,makemigrations工作正常,但migrate失敗,並帶有回溯和此錯誤:django.db.utils.OperationalError: duplicate column name: id它似乎試圖做一個額外的'id'列,不知道爲什麼。
  • 這樣做的正確方法是什麼?另外,如果成功,將引用我的用戶的ForeignKey字段是否正確更新?

    +0

    你想介紹一個新列,這應該是一個主鍵和獨特爲此 - 設置默認爲'0'只適用於所有安裝中只有一條記錄的情況 - 您的遷移文件需要爲所有當前記錄填充真實的「id」。文檔應該涵蓋這個主題。 – dahrens

    +0

    是的,我明白用戶0對所有的ID都不起作用,但我無法在文檔中找到如何填充當前記錄的ID。我檢查了https://docs.djangoproject.com/en/1.10/topics/migrations/;還有其他地方我應該看看? – ygramoel

    +0

    要填充id字段,我首先需要創建它。正如上面所解釋的,這是遷移/遷移應該做的(據我瞭解),但是失敗了。我無法在創建之前填充id字段,所以我被卡住了。 – ygramoel

    回答

    0

    這種情況是很難處理特別是其中的SQLite在實際工作甚至沒有一個real ALTER TABLE statement

    SQLite supports a limited subset of ALTER TABLE. The ALTER TABLE command in SQLite allows the user to rename a table or to add a new column to an existing table.

    大多數的類型,Django是通過做一個臨時表的變化。所以,你可以做到這一點

    步驟1:創建一個新的模式,酷似

    class TempModel(models.Model): 
        email = models.EmailField(max_length=255) 
        # other fields from your existing model 
    

    Notet,你並不需要顯式聲明一個主鍵字段。僅僅在電子郵件字段中關閉就足夠了。

    第2步:使遷移和遷移

    第4步:打開您喜歡的datbase客戶,做一個INSERT INTO myapp_tempmodel(場,....)SELECT * FROM myapp_oldmodel

    第4步:刪除舊錶,進行遷移和遷移

    第5步:重命名臨時表,進行遷移和遷移

    +0

    我現在就試試。我看到兩個潛在問題: – ygramoel

    +0

    我現在就試一試。我看到一個潛在的問題:我有一些其他表引用具有foreignkey字段的用戶。我將不得不找到一種方法將這些移動到新表中。 是否可以使用RunPython遷移自動化步驟3(並將其包含在遷移框架中)? – ygramoel

    +0

    如果它只是一個表,那麼刪除並重新創建FK,如果它是多個,那麼在您的遷移中需要一個runpython,因爲這樣做太多了,無法逐個完成。 – e4c5

    -1

    我自己遇到過這個問題,最後寫了一個可重用的(特定於MySQL的)遷移。你可以在這個repo找到代碼。我也在我的blog中寫過。

    作爲總結,所採取的步驟是:

    1. 修改模型類是這樣的:

      class Something(models.Model): 
          email = models.EmailField(max_length=255, unique=True) 
      
    2. 添加新的遷移沿着這些線路:

      app_name = 'app' 
      model_name = 'something' 
      related_model_name = 'something_else' 
      model_table = '%s_%s' % (app_name, model_name) 
      pivot_table = '%s_%s_%ss' % (app_name, related_model_name, model_name) 
      fk_name, index_name = None, None 
      
      
      class Migration(migrations.Migration): 
      
          operations = [ 
           migrations.AddField(
            model_name=model_name, 
            name='id', 
            field=models.IntegerField(null=True), 
            preserve_default=True, 
           ), 
           migrations.RunPython(do_most_of_the_surgery), 
           migrations.AlterField(
            model_name=model_name, 
            name='id', 
            field=models.AutoField(
             verbose_name='ID', serialize=False, auto_created=True, 
             primary_key=True), 
            preserve_default=True, 
           ), 
           migrations.AlterField(
            model_name=model_name, 
            name='email', 
            field=models.EmailField(max_length=255, unique=True), 
            preserve_default=True, 
           ), 
           migrations.RunPython(do_the_final_lifting), 
          ] 
      

      其中

      def do_most_of_the_surgery(apps, schema_editor): 
          models = {} 
          Model = apps.get_model(app_name, model_name) 
      
          # Generate values for the new id column 
          for i, o in enumerate(Model.objects.all()): 
           o.id = i + 1 
           o.save() 
           models[o.email] = o.id 
      
          # Work on the pivot table before going on 
          drop_constraints_and_indices_in_pivot_table() 
      
          # Drop current pk index and create the new one 
          cursor.execute(
           "ALTER TABLE %s DROP PRIMARY KEY" % model_table 
          ) 
          cursor.execute(
           "ALTER TABLE %s ADD PRIMARY KEY (id)" % model_table 
          ) 
      
          # Rename the fk column in the pivot table 
          cursor.execute(
           "ALTER TABLE %s " 
           "CHANGE %s_id %s_id_old %s NOT NULL" % 
           (pivot_table, model_name, model_name, 'VARCHAR(255)')) 
          # ... and create a new one for the new id 
          cursor.execute(
           "ALTER TABLE %s ADD COLUMN %s_id INT(11)" % 
           (pivot_table, model_name)) 
      
          # Fill in the new column in the pivot table 
          cursor.execute("SELECT id, %s_id_old FROM %s" % (model_name, pivot_table)) 
          for row in cursor: 
           id, key = row[0], row[1] 
           model_id = models[key] 
      
           inner_cursor = connection.cursor() 
           inner_cursor.execute(
            "UPDATE %s SET %s_id=%d WHERE id=%d" % 
            (pivot_table, model_name, model_id, id)) 
      
          # Drop the old (renamed) column in pivot table, no longer needed 
          cursor.execute(
           "ALTER TABLE %s DROP COLUMN %s_id_old" % 
           (pivot_table, model_name)) 
      
      def do_the_final_lifting(apps, schema_editor): 
          # Create a new unique index for the old pk column 
          index_prefix = '%s_id' % model_table 
          new_index_prefix = '%s_email' % model_table 
          new_index_name = index_name.replace(index_prefix, new_index_prefix) 
      
          cursor.execute(
           "ALTER TABLE %s ADD UNIQUE KEY %s (%s)" % 
           (model_table, new_index_name, 'email')) 
      
          # Finally, work on the pivot table 
          recreate_constraints_and_indices_in_pivot_table() 
      
    3. 應用新移民
    相關問題