Skip to content

Instantly share code, notes, and snippets.

@webjunkie
Last active February 4, 2023 22:12
Show Gist options
  • Save webjunkie/4ed9c3d132a58a51d857543eb24faaee to your computer and use it in GitHub Desktop.
Save webjunkie/4ed9c3d132a58a51d857543eb24faaee to your computer and use it in GitHub Desktop.
JSONFieldFormMixin for Django ModelForm with JSON field
# the mixin
class JSONFieldFormMixin(object):
"""
Given that a model has some kind of TextField ``json_storage_field`` with
a string of JSON formatted data inside, this mixin adds handling of this
field to a ModelForm.
It will read the text field, convert to Python dict, fill the form fields
that are given via ``json_fields`` and on saving will write this back to
the JSON field, updating just the fields that are included in the ModelForm.
Attributes:
json_storage_field: which field of the model holds the JSON data, should only be text
Attributes on form.Meta:
json_fields: the fields to read and save, need to be model fields defined on ModelForm
"""
json_storage_field = "meta"
def get_from_json_field(self):
return json.loads(getattr(self.instance, self.json_storage_field) or "{}")
def __init__(self, *args, **kwargs):
super(JSONFieldFormMixin, self).__init__(*args, **kwargs)
if self.instance:
meta_json = self.get_from_json_field()
for field in self.Meta.json_fields:
if meta_json.get(field):
self.fields[field].initial = meta_json.get(field)
def save(self, *args, **kwargs):
meta_json = self.get_from_json_field()
for field in self.Meta.json_fields:
meta_json[field] = self.cleaned_data[field]
setattr(self.instance, self.json_storage_field, json.dumps(meta_json))
return super(JSONFieldFormMixin, self).save(*args, **kwargs)
# example model
class Solution(models.Model):
name = models.CharField(max_length=255)
description = models.TextField(blank=True)
url = models.URLField("URL", blank=True)
meta = models.TextField(blank=True)
# example ModelForm
class SolutionForm(JSONFieldFormMixin, ModelForm):
currency = forms.CharField(required=False)
info = forms.CharField(required=False)
class Meta:
model = Solution
json_fields = ('currency', 'info')
fields = ('name', 'description', 'url') + json_fields
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment