2009-02-21 11 views
31

Quand je définir une classe de formulaire Django semblable à ceci:Comment créer un formulaire Django qui affiche un libellé de case à cocher à droite de la case à cocher?

def class MyForm(forms.Form): 
    check = forms.BooleanField(required=True, label="Check this") 

Il se développe à HTML qui ressemble à ceci:

<form action="." id="form" method=POST> 
<p><label for="check">Check this:</label> <input type="checkbox" name="check" id="check" /></p> 
<p><input type=submit value="Submit"></p> 
</form> 

Je voudrais l'élément d'entrée de case pour avoir une étiquette qui suit la case à cocher, pas l'inverse. Existe-t-il un moyen de convaincre Django de faire ça?

[Modifier]

Merci pour la réponse de Jonas - encore, alors qu'il résout le problème j'ai demandé (étiquettes de cases à cocher sont rendus à la droite de la case à cocher), il introduit un nouveau problème (tous les widgets les étiquettes sont affichées à la droite de leurs widgets ...)

Je voudrais éviter de surcharger _html_output() car il n'est évidemment pas conçu pour cela. La conception que je développerais consisterait à implémenter une méthode de sortie html de champ dans les classes Field, remplacer celle du champ booléen et utiliser cette méthode dans _html_output(). Malheureusement, les développeurs de Django ont choisi d'aller différemment, et j'aimerais travailler autant que possible dans le cadre existant. CSS ressemble à une approche décente, sauf que je ne connais pas assez de CSS pour le retirer ou même pour décider si j'aime cette approche ou non. De plus, je préfère un balisage qui ressemble encore à la sortie finale, au moins dans l'ordre de rendu. De plus, puisqu'il peut être raisonnable d'avoir plus d'une feuille de style pour un balisage particulier, faire ceci en CSS pourrait signifier devoir le faire plusieurs fois pour plusieurs styles, ce qui rend la CSS fausse.

[Modifier]

On dirait que je réponds à ma question ci-dessous. Si quelqu'un a une meilleure idée de comment faire cela, ne soyez pas timide.

Répondre

2

Voici ce que je fini par faire. J'ai écrit un template stringfilter personnalisé pour changer les balises. Maintenant, mon code de modèle ressemble à ceci:

{% load pretty_forms %} 
<form action="." method="POST"> 
{{ form.as_p|pretty_checkbox }} 
<p><input type="submit" value="Submit"></p> 
</form> 

La seule différence par rapport à un modèle Django ordinaire est l'ajout de la balise de template {% de charge%} et le filtre de pretty_checkbox.

Voici une implémentation fonctionnelle mais laide de pretty_checkbox - ce code n'a pas de traitement des erreurs, il suppose que le Django généré attributs sont formatés d'une manière très spécifique, et ce serait une mauvaise idée d'utiliser quoi que ce soit comme ceci dans votre code:

from django import template 
from django.template.defaultfilters import stringfilter 
import logging 

register=template.Library() 

@register.filter(name='pretty_checkbox') 
@stringfilter 
def pretty_checkbox(value): 
    # Iterate over the HTML fragment, extract <label> and <input> tags, and 
    # switch the order of the pairs where the input type is "checkbox". 
    scratch = value 
    output = '' 
    try: 
     while True: 
      ls = scratch.find('<label') 
      if ls > -1: 
       le = scratch.find('</label>') 
       ins = scratch.find('<input') 
       ine = scratch.find('/>', ins) 
       # Check whether we're dealing with a checkbox: 
       if scratch[ins:ine+2].find(' type="checkbox" ')>-1: 
        # Switch the tags 
        output += scratch[:ls] 
        output += scratch[ins:ine+2] 
        output += scratch[ls:le-1]+scratch[le:le+8] 
       else: 
        output += scratch[:ine+2] 
       scratch = scratch[ine+2:] 
      else: 
       output += scratch 
       break 
    except: 
     logging.error("pretty_checkbox caught an exception") 
    return output 

pretty_checkbox scanne son argument de chaîne, trouve des paires de < étiquette > et < entrée > balises, et les commutateurs autour si la balise d'entrée < > de le type est "case à cocher". Il supprime également le dernier caractère de l'étiquette, qui se trouve être le caractère ':'.

Avantages:

  1. Pas futzing avec CSS.
  2. Le balisage finit par ressembler à ce qu'il est censé faire.
  3. Je n'ai pas piraté les internes de Django.
  4. Le template est sympa, compact et idiomatique.

Inconvénients:

  1. Le code filtre doit être testé pour des valeurs intéressantes des étiquettes et des noms de champs d'entrée.
  2. Il y a probablement quelque chose quelque part là-bas qui le fait mieux et plus vite.
  3. Plus de travail que prévu pour un samedi.
+1

Pouvez-vous fournir le code pour pretty_checkbox? Mon propre balisage préféré est

+0

Je n'ai pas le code à portée de main pour le moment. Ce n'était pas très élaboré, si je me souviens bien - c'est simplement une fonction qui est alimentée par un petit fragment de code HTML, et permute l'étiquette et la saisie si l'entrée est une case à cocher. Le balisage est généré par Django, donc toute préférence que vous pourriez avoir est probablement non pertinente. –

1

L'ordre des entrées et des étiquettes est fourni via le paramètre normal_row du formulaire et il n'y a pas de modèles de lignes différents pour les cases à cocher. Donc, il y a deux façons de le faire (dans la version 0,96 exactement): 1.
override _html_output du formulaire
2. Utilisez CSS pour modifier la position de l'étiquette et la case

30

Voici une solution que je suis venu avec (Django V1.1):

{% load myfilters %} 

[...] 

{% for field in form %} 
    [...] 
    {% if field.field.widget|is_checkbox %} 
     {{ field }}{{ field.label_tag }} 
    {% else %} 
     {{ field.label_tag }}{{ field }} 
    {% endif %} 
    [...] 
{% endfor %} 

Vous devez créer une étiquette de modèle personnalisé (dans cet exemple dans un « myfilters.py » fichier) contenant quelque chose comme ceci:

from django import template 
from django.forms.fields import CheckboxInput 

register = template.Library() 

@register.filter(name='is_checkbox') 
def is_checkbox(value): 
    return isinstance(value, CheckboxInput) 

Plus d'informations sur les étiquettes de modèles personnalisés disponibles here.

Modifier: dans l'esprit de sa propre réponse asker:

Avantages:

  1. Pas futzing avec CSS.
  2. Le balisage finit par ressembler à ce qu'il est censé faire.
  3. Je n'ai pas piraté les internes de Django. (mais a dû regarder tout un tas)
  4. Le modèle est agréable, compact et idiomatique.
  5. Le code du filtre est agréable, quelles que soient les valeurs exactes des étiquettes et les noms des champs d'entrée.

Inconvénients:

  1. Il y a probablement quelque chose quelque part là-bas qui fait mieux et plus vite.
  2. peu probable que le client est prêt à payer pour tout le temps consacré à ce juste pour déplacer l'étiquette vers la droite ...
+0

J'aime cette réponse. L'implémentation est simple, ce qui est toujours un plus. Il est clair ce qui se passe juste en lisant le modèle. Le seul inconvénient (dans mon point de vue biaisé et paresseux) est que ma solution fait des modèles plus courts, et est plus proche de ce que vous auriez pu attendre de Django en premier lieu. –

14

Je pris la réponse de romkyns et fait un peu plus

générale
def field_type(field, ftype): 
    try: 
     t = field.field.widget.__class__.__name__ 
     return t.lower() == ftype 
    except: 
     pass 
    return False 

de cette façon, vous pouvez vérifier le type widget directement avec une chaîne

{% if field|field_type:'checkboxinput' %} 
    <label>{{ field }} {{ field.label }}</label> 
{% else %} 
    <label> {{ field.label }} </label> {{ field }} 
{% endif %} 
10

Toutes les solutions présentées impliquent des modifications de modèles, qui sont en g général plutôt inefficace en matière de performance. Voici un widget personnalisé qui fait le travail:

from django import forms 
from django.forms.fields import BooleanField 
from django.forms.util import flatatt 
from django.utils.encoding import force_text 
from django.utils.html import format_html 
from django.utils.translation import ugettext as _ 


class PrettyCheckboxWidget(forms.widgets.CheckboxInput): 
    def render(self, name, value, attrs=None): 
     final_attrs = self.build_attrs(attrs, type='checkbox', name=name) 
     if self.check_test(value): 
      final_attrs['checked'] = 'checked' 
     if not (value is True or value is False or value is None or value == ''): 
      final_attrs['value'] = force_text(value) 
     if 'prettycheckbox-label' in final_attrs: 
      label = _(final_attrs.pop('prettycheckbox-label')) 
     else: 
      label = '' 
     return format_html('<label for="{0}"><input{1} /> {2}</label>', attrs['id'], flatatt(final_attrs), label) 


class PrettyCheckboxField(BooleanField): 
    widget = PrettyCheckboxWidget 
    def __init__(self, *args, **kwargs): 
     if kwargs['label']: 
      kwargs['widget'].attrs['prettycheckbox-label'] = kwargs['label'] 
      kwargs['label'] = '' 
     super(PrettyCheckboxField, self).__init__(*args, **kwargs) 


# usage in form 
class MyForm(forms.Form): 
    my_boolean = PrettyCheckboxField(label=_('Some label'), widget=PrettyCheckboxWidget()) 

J'ai PrettyCheckboxWidget et PrettyCheckboxField dans un fichier supplémentaire, donc ils peuvent être importés en cas de besoin. Si vous n'avez pas besoin de traductions, vous pouvez supprimer les parties ugettext. Ce code fonctionne sur Django 1.5 et n'est pas testé pour les versions inférieures.

Avantages:

  • besoins très performants, aucune modification du modèle
  • Facile à utiliser comme un widget personnalisé

Inconvénients:

  • "as_table" rendre s la case à cocher + l'étiquette à l'intérieur de la deuxième colonne
  • {{field.label}} à l'intérieur du modèle est vide. L'étiquette est plutôt lié à {{champ}}
  • Plus de travail que je comptais faire un samedi ;-)
2

Je sais que l'utilisateur exclu CSS, mais compte tenu des meilleures réponses prennent environ une demi-heure de travail pour faire une si petite chose, mais sachant que des détails comme ceux-ci sont importants sur un site Web, je me contenterais de la solution CSS.

checkbox.css

input[type="checkbox"] { 
    float: left; 
    margin-right: 10px; 
    margin-top: 4px; 
} 

forms.py

class MyForm(forms.ModelForm): 
    # ... 
    class Media: 
    css = { 
     'all': 'checkbox.css', 
    } 

template.html

{{ form.media }} 
{{ form.as_p }} 

Avantages:

  • rapide!
  • pas futzing avec des balises de modèle (juste form.as_p)
  • Pas de nouveaux widgets damnés
  • le fichier CSS est automatiquement inclus dans toutes les formes

Inconvénients:

  • le HTML ne refléter la présentation (mais est assez bon!)
  • votre frontendist pourrait se plaindre
+0

Vous pouvez également ajouter cet extrait de css dans votre fichier css actuel et vous n'aurez pas besoin d'utiliser {{form.media}} ' – Caumons

0

Modification de la position de case à cocher dans l'admin Django peut être assez difficile, mais heureusement, il y a une solution simple en utilisant un widget personnalisé:

from django.forms.widgets import Widget, CheckboxInput, boolean_check 

class RightCheckbox(Widget): 
    render = CheckboxInput().render 

    def __init__(self, attrs=None, check_test=None): 
     super(RightCheckbox, self).__init__(attrs) 
     self.check_test = boolean_check if check_test is None else check_test 

Django utilise position gauche uniquement lorsque widget est sous-classe de CheckboxInput

Questions connexes