【问题标题】:Pseudo-form in Django admin that generates a json object on saveDjango admin 中的伪表单,在保存时生成 json 对象
【发布时间】:2012-03-21 11:10:20
【问题描述】:

我有一个带有 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中。

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

谢谢!

【问题讨论】:

  • 假设您可以控制模型的架构方式,为什么不将 json 属性存储为模型中的字段,然后向模型添加一个方法,例如 to_json() 来转换模型成字符串?您有什么用例需要这种看似“落后”的设置?
  • 我的字段比此处列出的要多得多,并且不希望数据库中有 60 多个字段。另外,选项可能会改变,存储 json 对象似乎比运行迁移更稳定。
  • 所以您只是在寻找一段将 JSON 对象转换为输入列表的 Javascript?
  • @jpic 不,我可以处理那部分。我正在寻找一种将文本字段的默认表单项更改为具有许多输入的自定义字段的方法,然后在保存时将结果转换为 json 对象。

标签: django django-forms django-admin


【解决方案1】:

想法

基本上你需要做的就是将你的 JSON 渲染成字段。

  1. 为存储 JSON 数据的模型创建字段。
  2. 创建表单域
  3. 创建小部件:
    1. 将字段呈现为多个输入
    2. 从 POST/GET 获取数据并将其转换回 JSON

您也可以通过覆盖 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()

希望这会有所帮助,它的外观如下:

【讨论】:

  • 这非常可靠。我正在玩的保存有一个问题,但这是一个坚实的开端。让赏金通过,但我很确定它是你的。
  • 我无法让它与 Django 1.5 一起工作,知道可能有什么问题吗? (@hookdonwinter 也是)
【解决方案2】:

我有类似的任务。我通过创建 Django 表单小部件解决了这个问题。您可以在您的应用程序中试用它django-SplitJSONWidget-form

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

      看起来很简单:

      #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
      

      毕竟,只需将此表单设置为管理员表单即可。

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

      【讨论】:

        【解决方案5】:

        基本上听起来您想要为您的文本字段定制一个小部件。 The snippet on this page 给出了如何呈现 json 键值对的示例。即使它不能完全满足您的需求,尤其是当您的嵌套 json 增加了一些复杂性时,它也可能会给您一些想法。

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

        【讨论】:

          【解决方案6】:

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

          【讨论】:

            【解决方案7】:

            django-submodel 可能会对您有所帮助,尽管它现在不能表示分层键值。

            很遗憾错过这么大的赏金=p

            【讨论】:

              猜你喜欢
              • 2011-02-24
              • 1970-01-01
              • 2018-09-21
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-01-01
              • 1970-01-01
              • 2022-01-06
              相关资源
              最近更新 更多