27π
Idea
Basically what you need to do is render your JSON into fields.
- Create field for your model that stores JSON data.
- Create form field
- Create widget that:
- Renders fields as multiple inputs
- Takes data from POST/GET and transforms it back into JSON
You can also skip steps 1, 2 by overriding widget for TextField.
Documentation links
- Widgets: https://docs.djangoproject.com/en/1.3/ref/forms/widgets/
- Django code for reference how to create widgets: https://code.djangoproject.com/browser/django/trunk/django/forms/widgets.py
Proof of concept
I tried coding this solution and here is solution that worked for me without some edge cases.
fields.py
import json
from django.db import models
from django import forms
from django import utils
from django.utils.translation import ugettext_lazy as _
class JSONEditableField(models.Field):
description = _("JSON")
def formfield(self, **kwargs):
defaults = {'form_class': JSONEditableFormField}
defaults.update(kwargs)
return super(JSONEditableField, self).formfield(**defaults)
class JSONEditableWidget(forms.Widget):
def as_field(self, name, key, value):
""" Render key, value as field """
attrs = self.build_attrs(name="%s__%s" % (name, key))
attrs['value'] = utils.encoding.force_unicode(value)
return u'%s: <input%s />' % (key, forms.util.flatatt(attrs))
def to_fields(self, name, json_obj):
"""Get list of rendered fields for json object"""
inputs = []
for key, value in json_obj.items():
if type(value) in (str, unicode, int):
inputs.append(self.as_field(name, key, value))
elif type(value) in (dict,):
inputs.extend(self.to_fields("%s__%s" % (name, key), value))
return inputs
def value_from_datadict(self, data, files, name):
"""
Take values from POST or GET and convert back to JSON..
Basically what this does is it takes all data variables
that starts with fieldname__ and converts
fieldname__key__key = value into json[key][key] = value
TODO: cleaner syntax?
TODO: integer values don't need to be stored as string
"""
json_obj = {}
separator = "__"
for key, value in data.items():
if key.startswith(name+separator):
dict_key = key[len(name+separator):].split(separator)
prev_dict = json_obj
for k in dict_key[:-1]:
if prev_dict.has_key(k):
prev_dict = prev_dict[k]
else:
prev_dict[k] = {}
prev_dict = prev_dict[k]
prev_dict[dict_key[-1:][0]] = value
return json.dumps(prev_dict)
def render(self, name, value, attrs=None):
# TODO: handle empty value (render text field?)
if value is None or value == '':
value = '{}'
json_obj = json.loads(value)
inputs = self.to_fields(name, json_obj)
# render json as well
inputs.append(value)
return utils.safestring.mark_safe(u"<br />".join(inputs))
class JSONEditableFormField(forms.Field):
widget = JSONEditableWidget
models.py
from django.db import models
from .fields import JSONEditableField
class Foo(models.Model):
text = models.TextField()
json = JSONEditableField()
Hope this helps and here is how it looks:
2π
I had similar task. I resolved it by creating Django form widget. You can try it for yours applications django-SplitJSONWidget-form
- Django Rest Framework β AssertionError Fix your URL conf, or set the `.lookup_field` attribute on the view correctly
- DRF + CoreAPIClient + psycopg2 throws exception, Interface error: connection already closed
- Determine empty template variable in Django
1π
Interesting question! Iβd like to see good and elegant solution for it π
But it seems to me, that django-admin is not suitable for your task. Iβd try to play with Forms. Smth like this:
class HmmForm(forms.Form):
name = forms.CharField(max_length = 128)
user_id = forms.IntegerField()
height = forms.IntegerField()
weight = forms.IntegerField()
def test(request, pk):
form = HmmForm()
if pk > 0:
hmm = Hmm.objects.get(pk = pk)
form = HmmForm( initial = {"name": hmm.name} )
return render_to_response("test/test.html", {"form": form})
And then simple render form in template, as you wish:
{{ form.as_table }} or {{ form.as_p }}
- GeoDjango distance filter with distance value stored within model β query
- Django: chaining 'startswith' and 'iexact' query filters?
1π
Itβs looks simple like this:
#Creating custom form
class MyCoolForm(forms.ModelForm):
class Meta:
model = MyModel
exclude = ('field_that_stores_json', )
#field_that_shows_json1 = forms.CharField()
#field_that_shows_jsons = forms.CharField()
def __init__(self, *args, **kwargs):
#Deserizlize field that stores json here
def save(self, *args, **kwargs):
#Serialize fields that shows json here
After all, just set this form as a form for admin.
P.S.: Also you can write your own widget for form, that transforms json object into fields on js level and has textarea underneath.
- Is it ok to catch and reraise an exception inside Django transaction.atomic()?
- Alembic β sqlalchemy initial migration
- Django and postgresql testing schema
1π
Basically it sounds like you want a custom widget for your text field. The snippet on this page gives an example on how to render json key-value pairs. Even if it doesnβt suit your needs entirely, especially as your nested json adds some complexity, it perhaps can give you some ideas.
As for the pure storage and retrieval of json objects into Python dicts, a few reusable JSONField implementations exist, like this one. You might want to add it to the mix.
- How to properly add entries for computed values to the django internationalization messages file?
- Best Practices for running celery on heroku
- What is the different between the get logger functions from celery.utils.log and logging?
0π
Try using YAML as the format for user input, and then deserialize the object and serialize it back to json in the back end. Django already has serializers for that.
0π
django-submodel may help you, although it cannot represent layered key-value now.
Itβs a pity to miss such HUGE bounty =p