2009-02-21 20 views
31

Wenn ich definieren eine Klasse Django Form ähnlich wie diese:Wie erstelle ich ein Django-Formular, das eine Checkbox-Beschriftung rechts neben dem Kontrollkästchen anzeigt?

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

Es HTML erweitert, die wie folgt aussieht:

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

ich das Kontrollkästchen Eingabeelement möchte ein Label haben, die die folgenden Kontrollkästchen, nicht umgekehrt. Gibt es eine Möglichkeit, Django zu überzeugen, das zu tun?

[Bearbeiten]

Danke für die Antwort von Jonas - nach wie vor, während es das Problem behebt I (nach rechts von der Checkbox gemacht Checkbox Etiketten) gefragt es ein neues Problem stellt (alle Widget Labels werden rechts neben ihren Widgets gerendert ...)

Ich möchte vermeiden, überschreiben _html_output(), da es offensichtlich nicht dafür entworfen ist. Der Entwurf, den ich entwickeln würde, wäre, eine Feld-HTML-Ausgabemethode in den Field-Klassen zu implementieren, die für das boolesche Feld zu überschreiben und diese Methode in _html_output() zu verwenden. Leider entschieden sich die Django-Entwickler für einen anderen Weg und ich würde gerne so weit wie möglich innerhalb des bestehenden Rahmens arbeiten.

CSS klingt nach einem anständigen Ansatz, außer dass ich nicht genug CSS weiß, um das durchzuziehen oder sogar zu entscheiden, ob ich diesen Ansatz mag oder nicht. Außerdem bevorzuge ich Markup, das zumindest in der Reihenfolge der Ausgabe immer noch der endgültigen Ausgabe ähnelt.

Da es darüber hinaus sinnvoll sein kann mehr als ein Stylesheet für eine bestimmte Markup zu haben, hat dies zu tun in CSS bedeuten könnte es mehrmals für mehrere Stile zu tun, das ist ziemlich viel CSS macht die falsche Antwort.

[Bearbeiten]

Scheint, wie ich unter meine eigene Frage zu beantworten. Wenn jemand eine bessere Idee hat, wie man das macht, sei nicht schüchtern.

Antwort

2

Hier ist, was ich am Ende tun. Ich schrieb einen benutzerdefinierten Template Stringfilter, um die Tags umzuschalten. Nun sieht mein Template-Code wie folgt aus:

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

Der einzige Unterschied zu einem normalen Django-Vorlage ist die Zugabe der {% Last%} Template-Tag und die pretty_checkbox Filter.

Hier ist eine funktionale, aber hässlich Umsetzung pretty_checkbox - dieser Code keine Fehlerbehandlung hat, es wird davon ausgegangen, dass die Django erzeugte Attribute in einer ganz bestimmten Weise formatiert werden, und es wäre eine schlechte Idee sein, etwas zu verwenden wie dies im 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 tastet seine String-Argument, findet Paare <Label> und <Eingang> Tags und schaltet sie um, wenn der <Eingang> -Tags Geben Sie "Checkbox" ein. Es löscht auch das letzte Zeichen des Etiketts, das zufällig das Zeichen ":" ist.

Vorteile:

  1. Kein futzing mit CSS.
  2. Das Markup endet so, wie es soll.
  3. Ich habe Django Interna nicht gehackt.
  4. Die Vorlage ist schön, kompakt und idiomatisch.

Nachteile:

  1. Der Filtercode muss für spannende Werte der Etiketten und Eingabefeldnamen getestet werden.
  2. Da ist wahrscheinlich etwas, das es besser und schneller macht.
  3. Mehr Arbeit als geplant an einem Samstag.
+1

Können Sie Stellen Sie den Code für pretty_checkbox? Mein eigenes bevorzugtes Markup dafür ist EoghanM

+0

Ich habe den Code gerade nicht griffbereit. Es war nicht sehr aufwendig, wenn ich mich recht erinnere - es ist einfach eine Funktion, die ein kurzes HTML-Snippet gespeist hat, und tauscht das Label und die Eingabe um, wenn die Eingabe ein Kontrollkästchen ist. Das Markup wird von Django generiert, so dass Ihre Präferenz wahrscheinlich irrelevant ist. –

1

Die Reihenfolge der Eingaben und Labels wird über den Parameter normal_row des Formulars bereitgestellt, und für die Kontrollkästchen gibt es keine unterschiedlichen Zeilenmuster. So gibt es zwei Möglichkeiten, dies zu tun (in 0,96-Version genau):
1. Überschreibung _html_output des Formulars
2. Verwendung CSS Position des Etiketts und Kontrollkästchen ändern

30

Hier ist eine Lösung, die ich mit (Django v1.1) habe kommen:

{% load myfilters %} 

[...] 

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

Sie werden ein eigenes Template-Tag (in diesem Beispiel in einem "myfilters.py" erstellen müssen Datei), die so etwas wie dieses:

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) 

Weitere Informationen über benutzerdefinierte Vorlagen-Tags vorhanden here.

bearbeiten: im Geiste der eigenen Antwort der Fragesteller:

Vorteile:

  1. Kein futzing mit CSS.
  2. Das Markup endet so, wie es soll.
  3. Ich habe Django Interna nicht gehackt. (aber musste sich einiges anschauen)
  4. Die Vorlage ist schön, kompakt und idiomatisch.
  5. Der Filtercode spielt unabhängig von den genauen Werten der Bezeichnungen und Eingabefeldnamen.

Nachteile:

  1. Es ist wahrscheinlich irgendwo etwas da draußen, dass es besser funktioniert und schneller.
  2. unwahrscheinlich, dass der Kunde bereit sein wird für die ganze Zeit dafür ausgegeben zu zahlen nur das Etikett nach rechts zu bewegen ...
+0

Ich mag diese Antwort. Die Implementierung ist einfach, was immer ein Plus ist. Es ist klar, was gerade los ist, wenn man die Vorlage liest. Der einzige Nachteil (in meiner voreingenommenen und zugegebenermaßen faulen Haltung) ist, dass meine Lösung kürzere Vorlagen enthält und näher an dem ist, was man von Django erwartet haben könnte. –

14

nahm ich die Antwort von romkyns und machte es ein wenig allgemeinen

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

diese Weise können Sie das Widget-Typ direkt mit einem String überprüfen

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

Alle vorgestellten Lösungen Vorlage Modifikationen umfassen, die in g sind In Bezug auf die Leistung ist die Leistung ziemlich ineffizient. Hier ist eine individuelle Widget, das die Arbeit erledigt:

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

Ich habe PrettyCheckboxWidget und PrettyCheckboxField in einer extra Datei, so können sie importiert werden, wo nötig. Wenn Sie keine Übersetzungen benötigen, können Sie die Ugettext-Teile entfernen. Dieser Code funktioniert unter Django 1.5 und ist für niedrigere Versionen nicht getestet.

Vorteile:

  • Hoch performante, brauchen keine Vorlage Änderungen
  • einfach als benutzerdefinierten Widget

Nachteile:

  • "as_table" machen s Das Kontrollkästchen + Label in der zweiten Spalte
  • {{field.label}} innerhalb der Vorlage ist leer. Das Etikett wird stattdessen gebunden {{field}}
  • mehr Arbeit, als ich tun, an einem Samstag ;-)
2

geplant Ich weiß, dass die CSS-Benutzer ausgeschlossen, aber die Top-Antworten dauern etwa eine halbe Stunde unter Berücksichtigung von Arbeit, um so eine kleine Sache zu machen, aber wissend, dass Details wie diese auf einer Website wichtig sind, würde ich mich mit der CSS-Lösung begnügen.

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

Vorteile:

  • schnell!
  • kein futzing mit Template-Tags (nur form.as_p)
  • keinen neuen Verdammten Widgets
  • die CSS-Datei in jeder Form automatisch enthalten ist

Nachteile:

  • die HTML nicht spiegeln Sie die Präsentation (ist aber gut genug!)
  • Ihr Frontendist könnte sich beschweren
+0

Sie können dieses css-Snippet auch in Ihre aktuelle css-Datei einfügen, ohne '{{form.media}}' zu verwenden – Caumons

0

Ändern Checkbox Position in Django Admin kann ziemlich schwierig sein, aber zum Glück gibt es eine einfache Lösung individuelle Widget mit:

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 linke Position verwendet nur wenn Widget ist Unterklasse von CheckboxInput