Created
July 19, 2019 10:39
-
-
Save NeuronQ/dd741c581a5417feaef78c1e4192645d to your computer and use it in GitHub Desktop.
Django models code identical for postgres JSONField and sqlite TextField
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## AUTHOR: Andrei Anton | |
## LICENSE: MIT | |
## USAGE EXAMPLE: | |
# @model_with_json_fields | |
# class MyThing(models.Model): | |
# JSON_FIELDS = ( | |
# ['data', dict(default=dict, blank=True)], | |
# ['errors', dict(null=True, blank=True)], | |
# ) | |
# ... | |
def model_with_json_fields(klass): | |
""" | |
Django model class decorator for adding json fields that are implemented as Postgres native JSONField when using postgres and as text fields with (de)serializing getters and setters on other DBs. | |
NOTE: when text fields are used, their column names get the '_str' suffix to not clash with the setters/getters | |
IMPORTANT: it's your job to tweak the migrations code, using `connection.vendor == 'postgresql'` in migrations to switch field types between django.contrib.postgres.fields.jsonb.JSONField and django.db.models.TextField | |
RATIONALE: all other more full-featured solutions for this problem are un-maintained and over-engineered / too heavy to timely debug - this requires some manual setup (migrations) and looks uglier, but has the advantage of simplicity | |
""" | |
for fld_name, ctor_args in klass.JSON_FIELDS: | |
if connection.vendor == 'postgresql': | |
# native postgres json field | |
JSONField(**ctor_args).contribute_to_class(klass, fld_name) | |
else: | |
# plain text field | |
str_fld_name = fld_name + '_str' | |
text_fld_ctor_args = ctor_args.copy() | |
if ctor_args.get('default', None) is dict: | |
text_fld_ctor_args['default'] = '{}' | |
elif ctor_args.get('default', None) is list: | |
text_fld_ctor_args['default'] = '[]' | |
models.TextField(**text_fld_ctor_args).contribute_to_class(klass, str_fld_name) | |
# but with property getter/setter (de)serializing to/from json | |
def getter(self): | |
return json.loads(getattr(self, str_fld_name)) | |
prop_getter = property(getter) | |
def setter(self, val): | |
setattr(self, str_fld_name, json.dumps(val)) | |
prop_getter_setter = prop_getter.setter(setter) | |
setattr(klass, fld_name, prop_getter_setter) | |
return klass |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment