2016-12-16 102 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. 应用新移民
    相关问题