What is the best approach to change primary keys in an existing Django app?

前端 未结 8 1633
野的像风
野的像风 2020-11-28 06:23

I have an application which is in BETA mode. The model of this app has some classes with an explicit primary_key. As a consequence Django use the fields and doesn\'t create

8条回答
  •  隐瞒了意图╮
    2020-11-28 07:09

    I managed to do this with django 1.10.4 migrations and mysql 5.5, but it wasn't easy.

    I had a varchar primary key with several foreign keys. I added an id field, migrated data and foreign keys. This is how:

    1. Adding future primary key field. I added an id = models.IntegerField(default=0) field to my main model and generated an auto migration.
    2. Simple data migration to generate new primary keys:

      def fill_ids(apps, schema_editor):
         Model = apps.get_model('', '')
         for id, code in enumerate(Model.objects.all()):
             code.id = id + 1
             code.save()
      
      class Migration(migrations.Migration):
          dependencies = […]
          operations = [migrations.RunPython(fill_ids)]
      
    3. Migrating existing foreign keys. I wrote a combined migration:

      def change_model_fks(apps, schema_editor):
          Model = apps.get_model('', '')  # Our model we want to change primary key for
          FkModel = apps.get_model('', '')  # Other model that references first one via foreign key
      
          mapping = {}
          for model in Model.objects.all():
              mapping[model.old_pk_field] = model.id  # map old primary keys to new
      
          for fk_model in FkModel.objects.all():
              if fk_model.model_id:
                  fk_model.model_id = mapping[fk_model.model_id]  # change the reference
                  fk_model.save()
      
      class Migration(migrations.Migration):
          dependencies = […]
          operations = [
              # drop foreign key constraint
              migrations.AlterField(
                  model_name='',
                  name='model',
                  field=models.ForeignKey('', blank=True, null=True, db_constraint=False)
              ),
      
              # change references
              migrations.RunPython(change_model_fks),
      
              # change field from varchar to integer, drop index
              migrations.AlterField(
                  model_name='',
                  name='model',
                  field=models.IntegerField('', blank=True, null=True)
              ),
          ]
      
    4. Swapping primary keys and restoring foreign keys. Again, a custom migration. I auto-generated the base for this migration when I a) removed primary_key=True from the old primary key and b) removed id field

      class Migration(migrations.Migration):
          dependencies = […]
          operations = [
              # Drop old primary key
              migrations.AlterField(
                  model_name='',
                  name='',
                  field=models.CharField(max_length=100),
              ),
      
              # Create new primary key
              migrations.RunSQL(
                  ['ALTER TABLE  CHANGE id id INT (11) NOT NULL PRIMARY KEY AUTO_INCREMENT'],
                  ['ALTER TABLE 
      CHANGE id id INT (11) NULL', 'ALTER TABLE
      DROP PRIMARY KEY'], state_operations=[migrations.AlterField( model_name='', name='id', field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), )] ), # Recreate foreign key constraints migrations.AlterField( model_name='', name='model', field=models.ForeignKey(blank=True, null=True, to='.'), ]

      提交回复
      热议问题