Skip to content

Instantly share code, notes, and snippets.

@thismatters
Last active August 11, 2024 10:48
Show Gist options
  • Save thismatters/53787f2d021fa5a1df640cd7b98d1185 to your computer and use it in GitHub Desktop.
Save thismatters/53787f2d021fa5a1df640cd7b98d1185 to your computer and use it in GitHub Desktop.
Migrating existing columns to use django-cryptography
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
from django_cryptography.fields import encrypt
app_with_model = 'account'
model_with_column = 'User'
column_to_encrypt = 'email_address'
column_field_class = models.CharField
column_attrs = {'max_length': 150}
column_null_status = False
temporary_column = f'temp_{column_to_encrypt}'
def replicate_to_temporary(apps, schema_editor):
Model = apps.get_model(app_with_model, model_with_column)
for row in Model.objects.all():
setattr(row, temporary_column, getattr(row, column_to_encrypt, None))
setattr(row, column_to_encrypt, None)
row.save(update_fields=[temporary_column, column_to_encrypt])
def replicate_to_real(apps, schema_editor):
Model = apps.get_model(app_with_model, model_with_column)
for row in Model.objects.all():
setattr(row, column_to_encrypt, getattr(row, temporary_column))
row.save(update_fields=[column_to_encrypt])
class Migration(migrations.Migration):
dependencies = [
(app_with_model, '0000_whichever'),
]
operations = [
# create temporary column
migrations.AddField(
model_name=model_with_column.lower(),
name=temporary_column,
field=column_field_class(
verbose_name=temporary_column, null=True, **column_attrs),
),
# allow null entries in the real column
migrations.AlterField(
model_name=model_with_column.lower(),
name=column_to_encrypt,
field=column_field_class(
verbose_name=column_to_encrypt, null=True, **column_attrs),
),
# push all data from real to temporary
migrations.RunPython(replicate_to_temporary),
# encrypt the real column (still allowing null values)
migrations.AlterField(
model_name=model_with_column.lower(),
name=column_to_encrypt,
field=encrypt(column_field_class(
verbose_name=column_to_encrypt, null=True, **column_attrs)),
),
# push all data from temporary to real (encrypting in the processes)
migrations.RunPython(replicate_to_real),
# remove the temporary column
migrations.RemoveField(
model_name=model_with_column.lower(),
name=temporary_column),
# disallow null values (if applicable)
migrations.AlterField(
model_name=model_with_column.lower(),
name=column_to_encrypt,
field=encrypt(column_field_class(
verbose_name=column_to_encrypt, null=column_null_status,
**column_attrs)),
),
]
@Kop3sh
Copy link

Kop3sh commented Aug 11, 2024

@thismatters
I keep getting this error

Unsupported lookup 'exact' for EncryptedPhoneNumberField or join on the field not permitted, perhaps you meant exact or iexact?
--

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment