Pseudo-form in Django admin that generates a json object on save

前端 未结 7 1083
佛祖请我去吃肉
佛祖请我去吃肉 2020-12-23 18:07

I have a model with a field for a json object. This object is used on the site to control some css variables, among other things.

Right now in the admin, I have a t

相关标签:
7条回答
  • 2020-12-23 19:02

    Idea

    Basically what you need to do is render your JSON into fields.

    1. Create field for your model that stores JSON data.
    2. Create form field
    3. Create widget that:
      1. Renders fields as multiple inputs
      2. 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: Result

    0 讨论(0)
提交回复
热议问题