Django管理员中的伪形式,在保存时生成一个json对象

时间:2012-03-02 23:43:03

标签: django django-forms django-admin

我有一个带有json对象字段的模型。该对象在网站上用于控制一些css变量等。

现在在管理员中,我有一个文本字段,用户可以在其中保存json对象。我想展示一个包含所有属性的表单,在保存后,它将生成一个json对象。

基本上,用户看到并存储数据,如下所示:

{
    "name":"hookedonwinter",
    "user-id":123,
    "basics":{
        "height":150,
        "weight":150
        }
}

我宁愿让用户看到这个:

Name: <input field>
User Id: <input field>
Height: <input field>
Weight: <input field>

并且数据仍然存储在json中。

任何指导都将不胜感激。链接到文档解释这一点,倍加赞赏。

谢谢!

7 个答案:

答案 0 :(得分:26)

基本上您需要做的是将JSON渲染到字段中。

  1. 为存储JSON数据的模型创建字段。
  2. 创建表单字段
  3. 创建小部件:
    1. 将字段呈现为多个输入
    2. 从POST / GET获取数据并将其转换回JSON
  4. 您还可以通过覆盖TextField的小部件来跳过步骤1,2。

    文档链接

    概念证明

    我尝试编写这个解决方案,这里的解决方案对我有用而没有一些边缘情况。

    <强> 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()
    

    希望这会有所帮助,以下是它的外观: Result

答案 1 :(得分:2)

我有类似的任务。我通过创建Django表单小部件来解决它。您可以为您的应用程序django-SplitJSONWidget-form

试用它

答案 2 :(得分:1)

有趣的问题!我希望看到它的优秀和优雅的解决方案:) 但在我看来,django-admin不适合你的任务。我会尝试使用Forms。像这样的Smth:

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})

然后在模板中简单渲染表单,如您所愿:

{{ form.as_table }} or {{ form.as_p }}

答案 3 :(得分:1)

这看起来很简单:

#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

毕竟,只需将此表单设置为admin的表单即可。

P.S。:你也可以为表单编写自己的小部件,将json对象转换为js级别的字段,并在下面有textarea。

答案 4 :(得分:1)

基本上,您需要为文本字段设置自定义窗口小部件。 The snippet on this page给出了一个如何呈现json键值对的示例。即使它完全不适合你的需求,特别是当你的嵌套json增加了一些复杂性时,它也许会给你一些想法。

对于json对象的纯存储和检索到Python dicts,存在一些可重用的JSONField实现,如this one。您可能希望将其添加到组合中。

答案 5 :(得分:0)

尝试使用YAML作为用户输入的格式,然后反序列化对象并将其序列化为后端的json。 Django已经为has序列化了。

答案 6 :(得分:0)

django-submodel可能会对您有所帮助,但它现在无法表示分层键值。

很遗憾错过这样巨额的赏金= p