12 votes

Pseudo-formulaire dans l'administration de Django qui génère un objet json à la sauvegarde

J'ai un modèle avec un champ pour un objet json. Cet objet est utilisé sur le site pour contrôler certaines variables css, entre autres choses.

Actuellement, dans l'administration, j'ai un champ de texte dans lequel un utilisateur peut enregistrer un objet json. J'aimerais afficher un formulaire avec tous les attributs qui, lors de l'enregistrement, génère un objet json.

En gros, l'utilisateur voit, et les données sont stockées, comme ceci :

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

Et je préfère que l'utilisateur voie ça :

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

et les données seront toujours stockées en json.

Tout conseil serait apprécié. Les liens vers les documents qui expliquent cela seront doublement appréciés.

Merci !

27voto

Marius Grigaitis Points 2400

Idée

Ce que vous devez faire, c'est rendre votre JSON en champs.

  1. Créez un champ pour votre modèle qui stocke des données JSON.
  2. Créer un champ de formulaire
  3. Créez un widget qui :
    1. Rendre les champs comme des entrées multiples
    2. Prend les données de POST/GET et les retransforme en JSON.

Vous pouvez également sauter les étapes 1 et 2 en remplaçant le widget de TextField.

Liens vers la documentation

Preuve de concept

J'ai essayé de coder cette solution et voici la solution qui a fonctionné pour moi sans quelques cas limites.

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

J'espère que cela vous aidera et voici à quoi cela ressemble : Result

2voto

Abbasov Alexander Points 649

J'ai eu une tâche similaire. Je l'ai résolu en créant un widget de formulaire Django. Vous pouvez l'essayer pour vos applications django-SplitJSONWidget-form

1voto

Aleksej Vasinov Points 1499

Question intéressante ! J'aimerais voir une bonne et élégante solution pour cela :) Mais il me semble que django-admin n'est pas adapté à votre tâche. J'essaierais de jouer avec Forms. Quelque chose comme ça :

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

Et ensuite, rendez simplement le formulaire dans le modèle, comme vous le souhaitez :

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

1voto

Nikolay Fominyh Points 2739

C'est simple comme bonjour :

#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

Après tout, il suffit de définir ce formulaire comme un formulaire pour l'administrateur.

P.S. : Vous pouvez également écrire votre propre widget pour le formulaire, qui transforme l'objet json en champs au niveau js et possède une zone de texte en dessous.

1voto

Dirk Eschler Points 1124

En fait, il semble que vous vouliez un widget personnalisé pour votre champ de texte. L'extrait de cette page donne un exemple sur la façon de rendre des paires clé-valeur en json. Même s'il ne répond pas entièrement à vos besoins, d'autant plus que votre json imbriqué ajoute une certaine complexité, il peut peut-être vous donner quelques idées.

En ce qui concerne le stockage et la récupération purs d'objets json dans des dicts Python, il existe quelques implémentations réutilisables de JSONField, telles que celui-ci . Vous pourriez vouloir l'ajouter au mélange.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X