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

前端 未结 8 1645
野的像风
野的像风 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:19

    To change primary key with south you can use south.db.create_primary_key command in datamigration. To change your custom CharField pk to standard AutoField you should do:

    1) create new field in your model

    class MyModel(Model):
        id = models.AutoField(null=True)
    

    1.1) if you have a foreign key in some other model to this model, create new fake fk field on these model too (use IntegerField, it will then be converted)

    class MyRelatedModel(Model):
        fake_fk = models.IntegerField(null=True)
    

    2) create automatic south migration and migrate:

    ./manage.py schemamigration --auto
    ./manage.py migrate
    

    3) create new datamigration

    ./manage.py datamigration  fill_id
    

    in tis datamigration fill these new id and fk fields with numbers (just enumerate them)

        for n, obj in enumerate(orm.MyModel.objects.all()):
            obj.id = n
            # update objects with foreign keys
            obj.myrelatedmodel_set.all().update(fake_fk = n)
            obj.save()
    
        db.delete_primary_key('my_app_mymodel')
        db.create_primary_key('my_app_mymodel', ['id'])
    

    4) in your models set primary_key=True on your new pk field

    id = models.AutoField(primary_key=True)
    

    5) delete old primary key field (if it is not needed) create auto migration and migrate.

    5.1) if you have foreign keys - delete old foreign key fields too (migrate)

    6) Last step - restore fireign key relations. Create real fk field again, and delete your fake_fk field, create auto migration BUT DO NOT MIGRATE(!) - you need to modify created auto migration: instead of creating new fk and deleting fake_fk - rename column fake_fk

    # in your models
    class MyRelatedModel(Model):
        # delete fake_fk
        # fake_fk = models.InegerField(null=True)
        # create real fk
        mymodel = models.FoeignKey('MyModel', null=True)
    
    # in migration
        def forwards(self, orm):
            # left this without change - create fk field
            db.add_column('my_app_myrelatedmodel', 'mymodel',
                      self.gf('django.db.models.fields.related.ForeignKey')(default=1, related_name='lots', to=orm['my_app.MyModel']),keep_default=False)
    
            # remove fk column and rename fake_fk
            db.delete_column('my_app_myrelatedmodel', 'mymodel_id')
            db.rename_column('my_app_myrelatedmodel', 'fake_fk', 'mymodel_id')
    

    so previously filled fake_fk becomes a column, that contain actual relation data, and it does not get lost after all the steps above.

提交回复
热议问题