Skip to content

Instantly share code, notes, and snippets.

@december1981
Created November 29, 2021 17:26
Show Gist options
  • Save december1981/ba2a3bb281f7fd4428a178f1d228e68b to your computer and use it in GitHub Desktop.
Save december1981/ba2a3bb281f7fd4428a178f1d228e68b to your computer and use it in GitHub Desktop.
A "JSONTextField" based on the PGPSymmetricKeyFieldMixin from pgcrypto https://github.com/incuna/django-pgcrypto-fields
import json
from django.db import models
from django.core import exceptions
from django.utils.translation import gettext_lazy as _
from pgcrypto.mixins import PGPSymmetricKeyFieldMixin
class JSONTextFieldMixin:
"""Simplified JSON field based on a text field (no jsonb support)"""
empty_strings_allowed = False
description = _('JSON interpreted text field')
default_error_messages = {
'invalid': _('Value must be valid JSON.'),
}
_default_hint = ('dict', '{}')
def __init__(
self, verbose_name=None, name=None, encoder=None, decoder=None,
**kwargs,
):
if encoder and not callable(encoder):
raise ValueError('The encoder parameter must be a callable object.')
if decoder and not callable(decoder):
raise ValueError('The decoder parameter must be a callable object.')
self.encoder = encoder
self.decoder = decoder
super().__init__(verbose_name, name, **kwargs)
def deconstruct(self):
name, path, args, kwargs = super().deconstruct()
if self.encoder is not None:
kwargs['encoder'] = self.encoder
if self.decoder is not None:
kwargs['decoder'] = self.decoder
return name, path, args, kwargs
def from_db_value(self, value, expression, connection):
if value is None:
return value
try:
return json.loads(value, cls=self.decoder)
except json.JSONDecodeError:
return value
def get_prep_value(self, value):
if value is None:
return value
return json.dumps(value, cls=self.encoder)
def validate(self, value, model_instance):
super().validate(value, model_instance)
try:
json.dumps(value, cls=self.encoder)
except TypeError:
raise exceptions.ValidationError(
self.error_messages['invalid'],
code='invalid',
params={'value': value},
)
def value_to_string(self, obj):
return json.dumps(self.value_from_object(obj), cls=self.encoder)
class JSONTextPGPSymmetricKeyField(PGPSymmetricKeyFieldMixin, JSONTextFieldMixin, models.TextField):
"""JSON Text PGP symmetric key encrypted field for postgres."""
@december1981
Copy link
Author

Adding a public key variant as simple as importing PGPPublicKeyFieldMixin and declaring the field class as so:

class JSONTextPGPPublicKeyField(PGPPublicKeyFieldMixin, JSONTextFieldMixin, models.TextField):
    """JSON Text PGP public key encrypted field for postgres."""

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