Skip to content

Instantly share code, notes, and snippets.

@tomfa
Created January 22, 2019 17:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tomfa/2040e8158c1c095852ba05ba226c7b38 to your computer and use it in GitHub Desktop.
Save tomfa/2040e8158c1c095852ba05ba226c7b38 to your computer and use it in GitHub Desktop.
Django CompressedJSONField
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
)
@cddumanov
Copy link

Hey @tomfa ! Hope you are doing well.

We used you field but within time we noticed a bug that was provoked with a field. So I have made my own version. Changes:

  • fixed a bug that provoked recursion during delete
  • simplified logic

I would advise everyone who is reading this and willing to use, please use my fork.https://gist.github.com/cddumanov/682d96b49f49ddef02ffc4552b0fbe0e

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