Skip to content

Instantly share code, notes, and snippets.

@tomfa
Last active June 9, 2023 11:21
Show Gist options
  • Save tomfa/665f8a655a9218e0b4e9bd394d459934 to your computer and use it in GitHub Desktop.
Save tomfa/665f8a655a9218e0b4e9bd394d459934 to your computer and use it in GitHub Desktop.
Django compressed json field / compressed binary field
from django.utils.text import compress_string
from django.core.serializers.json import DjangoJSONEncoder
class CompressedBinaryField(models.BinaryField):
compress = compress_string
@staticmethod
def uncompress(s):
zbuf = io.BytesIO(s)
zfile = gzip.GzipFile(fileobj=zbuf)
ret = zfile.read()
zfile.close()
return ret
def get_db_prep_save(self, value, connection=None, prepared=False):
if value is not None and prepared is False:
value = CompressedBinaryField.compress(value)
return models.BinaryField.get_db_prep_save(self, value, connection)
def is_binary(self, value):
return value and (type(value) == bytes or isinstance(value, memoryview))
def _get_val_from_obj(self, obj):
val = obj and getattr(obj, self.attname)
if self.is_binary(val):
return CompressedBinaryField.uncompress(val)
if val is None:
return self.get_default()
return val
def post_init(self, instance=None, **kwargs):
value = self._get_val_from_obj(instance)
setattr(instance, self.attname, value)
def contribute_to_class(self, cls, name, private_only=False):
super(CompressedBinaryField, self).contribute_to_class(cls, name)
models.signals.post_init.connect(self.post_init, sender=cls)
def get_internal_type(self):
return 'BinaryField'
class CompressedJSONField(CompressedBinaryField):
encoder = DjangoJSONEncoder
def __init__(self, *args, **kwargs):
self.encoder = kwargs.pop('encoder', OtovoJsonEncoder)
super().__init__(*args, **kwargs)
def get_db_prep_save(self, value, connection=None, prepared=False):
if value is not None and prepared is False:
value = json.dumps(value, cls=self.encoder).encode('utf-8')
return super().get_db_prep_save(value, connection, prepared)
def _get_val_from_obj(self, obj):
if obj:
val = super()._get_val_from_obj(obj)
if self.is_binary(val):
return json.loads(val.decode('utf-8'))
return val
else:
return self.get_default()
from django.db import models
from fields import CompressedJSONField
class MyModel(models.Model):
price_points = CompressedJSONField(
null=True,
help_text='Accepts JSON, and stores compressed in database',
# encoder=MyCustomJSONEncoder
)
@tomfa
Copy link
Author

tomfa commented Jun 9, 2023

For the record, feel free to use the above snippet for anything you wish. MIT License

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