Lorsque je définis une classe de formulaire Django similaire à celle-ci:
def class MyForm(forms.Form):
check = forms.BooleanField(required=True, label="Check this")
Il se développe en 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>
J'aimerais que l'élément d'entrée case à cocher ait une étiquette qui suit la case à cocher, et non l'inverse. Y a-t-il un moyen de convaincre Django de le faire?
[Modifier]
Merci pour la réponse de Jonas - toujours, même s'il résout le problème que j'ai demandé (les étiquettes des cases à cocher sont affichées à droite de la case à cocher), il introduit un nouveau problème (toutes les étiquettes des widgets sont affichées à la droite de leurs widgets ...)
J'aimerais éviter de surcharger _html_output () car ce n'est évidemment pas conçu pour cela. La conception que je proposerais 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 une approche différente et j'aimerais travailler autant que possible dans le cadre existant.
CSS sonne comme une approche décente, sauf que je ne connais pas assez de CSS pour réussir cela ou même pour décider si j'aime cette approche ou non. En outre, je préfère un balisage qui ressemble toujours à la sortie finale, du moins dans l’ordre de rendu.
De plus, comme il peut être raisonnable d'avoir plus d'une feuille de style pour un balisage particulier, le faire en CSS pourrait signifier qu'il faille le faire plusieurs fois pour plusieurs styles, ce qui fait du CSS une mauvaise réponse.
[Modifier]
On dirait que je réponds à ma propre question ci-dessous. Si quelqu'un a une meilleure idée de comment faire cela, ne soyez pas timide.
Voici ce que j'ai fini par faire. J'ai écrit un templatefilter personnalisé pour changer les tags. 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 modèle {% load%} et du filtre pretty_checkbox .
Voici une implémentation fonctionnelle mais moche de pretty_checkbox - ce code ne gère pas les erreurs, il suppose que les attributs générés par Django sont formatés de manière très spécifique, et il serait déconseillé d’utiliser ce genre de chose. 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 analyse son argument de chaîne, trouve des paires de balises <label> et <input>, et les bascule si le type de la balise <input> est "checkbox". Il supprime également le dernier caractère de l'étiquette, qui se trouve être le caractère ':'.
Avantages:
Désavantages:
Voici une solution que j'ai mise au point (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 devrez créer une balise de modèle personnalisé (dans cet exemple dans un fichier "myfilters.py") contenant quelque chose comme:
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 balises de modèles personnalisés disponibles ici .
Edit : dans l'esprit de la réponse du demandeur:
Avantages:
Désavantages:
J'ai pris la réponse de Romkyns et l'ai rendue 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 de widget directement avec une chaîne
{% if field|field_type:'checkboxinput' %}
<label>{{ field }} {{ field.label }}</label>
{% else %}
<label> {{ field.label }} </label> {{ field }}
{% endif %}
Toutes les solutions présentées impliquent des modifications de modèles, qui sont généralement plutôt inefficaces en termes de performances. 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, afin qu'ils puissent être importés si nécessaire. 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 antérieures.
Avantages:
Désavantages:
Je sais que l'utilisateur a exclu CSS, mais si l'on considère les réponses les plus longues, cela prend environ une demi-heure de travail, mais sachant que de tels détails 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:
form.as_p
Désavantages:
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èle de ligne différent pour les cases à cocher. Donc, il y a deux façons de faire cela (dans la version 0.96 exactement):
1. redéfinir _html_output du formulaire
2. utiliser CSS pour changer la position de l'étiquette et de la case à cocher
Changer la case de la case dans Django admin peut être assez délicat, mais heureusement, il existe une solution simple 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 la position de gauche uniquement lorsque le widget est une sous-classe de CheckboxInput