Migrating existing dataΒΆ

See also

If you are unfamiliar with migrations in Django, please consult the Django Migrations documentation.

To migrate an unencrypted database field to an encrypted field the following steps must be followed. Each step is labeled with its Django migration type of schema or data.

  1. Rename existing field using a prefix such as old_ (schema)
  2. Add new encrypted field with name of the original field (schema)
  3. Copy data from the old field into the new field (data)
  4. Remove the old field (schema)

The steps are illustrated bellow for the following model:

class EncryptedCharModel(models.Model):
    field = encrypt(models.CharField(max_length=15))

Create the initial migration for the EncryptedCharModel.

class Migration(migrations.Migration):

    initial = True

    dependencies = []

    operations = [
        migrations.CreateModel(
            name='EncryptedCharModel',
            fields=[
                ('id', models.AutoField(
                    auto_created=True,
                    primary_key=True,
                    serialize=False,
                    verbose_name='ID')),
                ('field', models.CharField(max_length=15)),
            ],
        ),
    ]

Rename the old field by pre-fixing as old_field from field

class Migration(migrations.Migration):

    dependencies = [
        ('fields', '0001_initial'),
    ]

    operations = [
        migrations.RenameField(
            model_name='encryptedcharmodel',
            old_name='field',
            new_name='old_field',
        ),
    ]

Add the new encrypted field using the original name from our field.

class Migration(migrations.Migration):

    dependencies = [
        ('fields', '0002_rename_fields'),
    ]

    operations = [
        migrations.AddField(
            model_name='encryptedcharmodel',
            name='field',
            field=django_cryptography.fields.encrypt(
                models.CharField(default=None, max_length=15)),
            preserve_default=False,
        ),
    ]

Copy the data from the old field into the new field using the ORM. Providing forwards and reverse methods will allow restoring the field to its unencrypted form.

def forwards_encrypted_char(apps, schema_editor):
    EncryptedCharModel = apps.get_model("fields", "EncryptedCharModel")

    for row in EncryptedCharModel.objects.all():
        row.field = row.old_field
        row.save(update_fields=["field"])


def reverse_encrypted_char(apps, schema_editor):
    EncryptedCharModel = apps.get_model("fields", "EncryptedCharModel")

    for row in EncryptedCharModel.objects.all():
        row.old_field = row.field
        row.save(update_fields=["old_field"])


class Migration(migrations.Migration):

    dependencies = [
        ("fields", "0003_add_encrypted_fields"),
    ]

    operations = [
        migrations.RunPython(forwards_encrypted_char, reverse_encrypted_char),
    ]

Delete the old field now that the data has been copied into the new field

class Migration(migrations.Migration):

    dependencies = [
        ('fields', '0004_migrate_data'),
    ]

    operations = [
        migrations.RemoveField(
            model_name='encryptedcharmodel',
            name='old_field',
        ),
    ]