Псевдоформа в админке Django, которая генерирует объект json при сохранении - PullRequest
11 голосов
/ 03 марта 2012

У меня есть модель с полем для объекта 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 ]

26 голосов
/ 08 марта 2012

Идея

По сути, вам нужно преобразовать JSON в поля.

  1. Создайте поле для вашей модели, в котором будут храниться данные JSON.
  2. Создать поле формы
  3. Создать виджет, который:
    1. Отображает поля как несколько входов
    2. Получает данные из POST / GET и преобразует их обратно в JSON

Вы также можете пропустить шаги 1, 2, переопределив виджет для TextField.

Ссылки на документацию

Подтверждение концепции

Я пытался кодировать это решение, и вот решение, которое работало для меня без каких-либо крайних случаев.

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

2 голосов
/ 20 мая 2013

У меня была похожая задача.Я решил это, создав виджет Django.Вы можете попробовать его для своих приложений django-SplitJSONWidget-form

1 голос
/ 08 марта 2012

В основном это звучит так, как будто вы хотите собственный виджет для вашего текстового поля. Фрагмент на этой странице дает пример того, как визуализировать пары ключ-значение json. Даже если он не полностью соответствует вашим потребностям, особенно если ваш вложенный json добавляет сложности, он может дать вам некоторые идеи.

Что касается чистого хранения и извлечения объектов json в Python, существует несколько многократно используемых реализаций JSONField, таких как эта . Возможно, вы захотите добавить его в смесь.

1 голос
/ 06 марта 2012

Это выглядит просто так:

#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

В конце концов, просто установите эту форму как форму для администратора.

PS: Также вы можете написать свой собственный виджет для формы, который преобразует объект json в поля на уровне js и имеет текстовое поле внизу.

1 голос
/ 05 марта 2012

Интересный вопрос!Я хотел бы видеть хорошее и элегантное решение для этого :) Но мне кажется, что 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 }}
0 голосов
/ 15 марта 2012

django-submodel может вам помочь, хотя сейчас она не может представлять многослойное значение ключа.

Жаль, что такое ОГРОМНОЕ вознаграждение = p

0 голосов
/ 09 марта 2012

Попробуйте использовать YAML в качестве формата для пользовательского ввода, а затем десериализовать объект и сериализовать его обратно в json на заднем конце.Джанго уже имеет сериализаторы для этого.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...