python / expert
Snippet
Field-Level Encryption via Custom Model Field Descriptors
Python descriptors can be combined with Django custom model fields to perform transparent field-level encryption and decryption directly on model instance attribute read and write operations.
snippet.py
python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from cryptography.fernet import Fernetfrom django.conf import settingsfrom django.db import modelsclass EncryptedFieldDescriptor:def __init__(self, field):self.field = fielddef __get__(self, instance, owner=None):if instance is None:return selfvalue = instance.__dict__.get(self.field.name)if value is None or not isinstance(value, str):return valueif value.startswith('gAAAAA'):cipher = Fernet(settings.SECRET_KEY.encode()[:32].ljust(32, b'='))return cipher.decrypt(value.encode()).decode()return valuedef __set__(self, instance, value):if value is not None and isinstance(value, str):cipher = Fernet(settings.SECRET_KEY.encode()[:32].ljust(32, b'='))encrypted = cipher.encrypt(value.encode()).decode()instance.__dict__[self.field.name] = encryptedelse:instance.__dict__[self.field.name] = valueclass EncryptedCharField(models.CharField):def contribute_to_class(self, cls, name, **kwargs):super().contribute_to_class(cls, name, **kwargs)setattr(cls, name, EncryptedFieldDescriptor(self))
django
Breakdown
1
class EncryptedFieldDescriptor:
Defines a custom Python descriptor to manage attribute access on model instances.
2
def __get__(self, instance, owner=None):
Intercepts read access, decrypting string values if they match cipher signature format.
3
def __set__(self, instance, value):
Intercepts assignment, encrypting raw strings before persisting them to instance __dict__.
4
def contribute_to_class(self, cls, name, **kwargs):
Attaches descriptor instance to the model class during Django model construction.