web-dev-qa-db-fra.com

Django et fieldsets sur ModelForm

Je sais que vous pouvez spécifier des ensembles de champs dans Django pour les assistants de l'administrateur. Cependant, je ne trouve rien d'utile pour ModelForms. Juste quelques patchs que je ne peux pas utiliser. Est-ce que je manque quelque chose? Existe-t-il un moyen de réaliser quelque chose comme un jeu de champs sans écrire manuellement chaque champ de mon modèle dans la balise appropriée?.

J'aimerais idéalement parcourir un ensemble de BoundFields. Cependant, en faisant quelque chose comme ceci à la fin de mon ModelForm:

    fieldsets = []
    fieldsets.append(('Personal Information',
                      [username,password,password2,first_name,last_name,email]),) # add a 2 element Tuple of string and list of fields
    fieldsets.append(('Terms & Conditions',
                      [acceptterms,acceptprivacy]),) # add a 2 element Tuple of string and list of fields

échoue car les éléments contenus dans ma structure de données sont les champs bruts, pas les BoundFields. t ressemble à BoundFields sont générés à la volée ... cela me rend triste. Pourrais-je créer ma propre sous-classe de formulaires.Form qui contient un concept de champs (même approximatif qui n'est pas compatible avec les versions antérieures ... ceci ne concerne que mon propre projet) et si oui, pouvez-vous donner un pointeur? Je ne veux pas jouer avec le code Django.

36
Krystian Cybulski

Les jeux de champs dans les modèles sont toujours au stade de "conception". Il y a un ticket à Django trac avec une faible activité.

C'est quelque chose qui m'intéresse dans mes recherches dans un avenir proche, mais comme je ne l'ai pas encore fait, le meilleur que je puisse offrir sont ces extraits:

Edit: Je viens juste de remarquer à nouveau cette question et je me rends compte qu'il faut une édition pour signaler le projet de Carl Django-form-utils qui contient une classe BetterForm pouvant contenir des champs. Si vous aimez ce projet, donnez-lui un +1 pour sa réponse ci-dessous :)

34
Van Gale

Je pense cet extrait fait exactement ce que vous voulez. Il vous donne une sous-classe de formulaire qui vous permet de subdiviser votre formulaire de manière déclarative en ensembles de champs et de les parcourir dans votre modèle.

Mise à jour: cet extrait fait depuis partie de Django-form-utils

50
Carl Meyer

Une chose que vous pouvez faire est de diviser vos ensembles de champs logiques en classes de formulaire de modèle distinctes.

class PersonalInfoForm (forms.ModelForm):
    class Meta:
        model=MyModel
        fields=('field1', 'field2', ...)

class TermsForm (forms.ModelForm):
    class Meta:
        model=MyModel
        fields=('fieldX', 'fieldY', ...)

Transmettez-les à votre modèle en différentes variables et divisez les formulaires:

<form ...>
   <fieldset><legend>Personal Information</legend>
       {{ personal_info_form }}
   </fieldset>
   <fieldset><legend>Terms and Conditions</legend>
       {{ terms_form }}
   </fieldset>
</form>

En ce sens, chacune de vos classes de formulaire n'est qu'un fragment du formulaire HTML actuel.

Il introduit une touche de complexité lorsque vous appelez enregistrer sur le formulaire. Vous voudrez probablement passer commit = False, puis fusionner les objets résultants. Ou bien évitez simplement d'utiliser ModelForm.save et complétez votre objet de modèle à la main avec 'cleaner_data'

16
Joe Holloway

Daniel Greenfelds Django-uni-form résout ce problème avec la classe d'assistant Layout. J'essaye en ce moment et ça me semble plutôt propre.

Les assistants uniformes peuvent utiliser des objets de mise en page. Une mise en page peut être composée d'ensembles de champs, de lignes, de colonnes, de HTML et de champs .

J'ai initialement choisi Django-uni-form car il est conforme à Section 508 .

4
oivvio

C'est le code que j'ai développé afin de comprendre les balises personnalisées (avec des liens). Je l'ai appliqué pour créer un fieldset.

Disclaimer: J'encourage l'utilisation de l'une des réponses ci-dessus, c'était juste pour apprendre.

templatetags/myextras.py:

from Django import template
from Django.template import Context

register = template.Library()


class FieldsetNode(template.Node):
    """ Fieldset renderer for 'fieldset' tag """
    def __init__(self, nodelist, fieldset_name):
        """ Initialize renderer class
        https://docs.djangoproject.com/en/1.8/howto/custom-template-tags/#writing-the-renderer
        :param nodelist: a list of the template nodes inside a block of 'fieldset'
        :param fieldset_name: the name of the fieldset
        :return: None
        """
        self.nodelist = nodelist
        self.fieldset_name = fieldset_name

    def render(self, context):
        """ Render the inside of a fieldset block based on template file
        https://docs.djangoproject.com/en/1.8/howto/custom-template-tags/#auto-escaping-considerations
        :param context: the previous template context
        :return: HTML string
        """
        t = context.template.engine.get_template('myapp/fieldset.html')
        return t.render(Context({
            'var': self.nodelist.render(context),
            'name': self.fieldset_name,
        }, autoescape=context.autoescape))


@register.tag
def fieldset(parser, token):
    """ Compilation function for fieldset block tag
    Render a form fieldset
    https://docs.djangoproject.com/en/1.8/howto/custom-template-tags/#writing-the-compilation-function
    https://docs.djangoproject.com/en/1.8/howto/custom-template-tags/#parsing-until-another-block-tag
    :param parser: template parser
    :param token: tag name and variables
    :return: HTML string
    """
    try:
        tag_name, fieldset_name = token.split_contents()
    except ValueError:
        raise template.TemplateSyntaxError("%r tag requires a single argument" % token.contents.split()[0])
    if not (fieldset_name[0] == fieldset_name[-1] and fieldset_name[0] in ('"', "'")):
        raise template.TemplateSyntaxError("%r tag's argument should be in quotes" % tag_name)
    nodelist = parser.parse(('endfieldset',))
    parser.delete_first_token()
    return FieldsetNode(nodelist, fieldset_name[1:-1])

templates/myapp/fieldset.html:

<div class="fieldset panel panel-default">
    <div class="panel-heading">{{ name }}</div>
    <div class="panel-body">{{ var }}</div>
</div>

templates/myapp/myform.html:

<form action="{% url 'myapp:myurl' %}" method="post">
    {% csrf_token %}
    {% fieldset 'General' %}
        {{form.myfield1 }}
    {% endfieldset %}
    {# my submit button #}
</form>
0
Wtower