2009-02-21 17 views
31

Cuando definir una clase Django forma similar a esto:¿Cómo creo un formulario de Django que muestra una etiqueta de casilla de verificación a la derecha de la casilla de verificación?

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

Se expande a HTML que se ve así:

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

Me gustaría que el elemento de entrada de casilla de verificación para tener una etiqueta que sigue a la casilla de verificación, no al revés. ¿Hay alguna manera de convencer a Django para que haga eso?

[Editar]

Gracias por la respuesta de Jonas - todavía, mientras que corrige el problema Pregunté por (etiquetas casilla se prestan a la derecha de la casilla de verificación) que introduce un nuevo problema (todo el widget de las etiquetas se representan a la derecha de sus widgets ...)

Me gustaría evitar anular _html_output() ya que obviamente no está diseñado para eso. El diseño que se me ocurriría sería implementar un método de salida html de campo en las clases de Campo, anular el del campo Booleano y usar ese método en _html_output(). Lamentablemente, los desarrolladores de Django optaron por ir de una manera diferente, y me gustaría trabajar dentro del marco existente tanto como sea posible.

CSS suena como un enfoque decente, excepto que no sé suficiente CSS para llevarlo a cabo o incluso para decidir si me gusta este enfoque o no. Además, prefiero el marcado que todavía se asemeja al resultado final, al menos en orden de procesamiento.

Además, como puede ser razonable tener más de una hoja de estilo para un marcado en particular, hacer esto en CSS podría significar tener que hacerlo varias veces para varios estilos, lo que hace que CSS sea la respuesta incorrecta.

[Editar]

Parece como si estuviera respondiendo a mi propia pregunta a continuación. Si alguien tiene una mejor idea de cómo hacer esto, no seas tímido.

Respuesta

2

Esto es lo que terminé haciendo. Escribí un stringfilter de plantilla personalizado para cambiar las etiquetas. Ahora, mi código de la plantilla se ve así:

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

La única diferencia con una plantilla de Django llanura es la adición de la {}%% de carga etiqueta de plantilla y la pretty_checkbox filtro.

He aquí una aplicación funcional, pero fea de pretty_checkbox - este código no tiene ningún control de errores, se supone que el Django genera atributos tienen el formato de una manera muy específica, y sería una mala idea usar nada como esto en su código:

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 escanea su argumento de cadena, encuentra pares de <etiqueta> y <entrada> etiquetas, y los interruptores en torno a si la entrada < > etiqueta de tipo es "casilla de verificación". También elimina el último carácter de la etiqueta, que es el carácter ':'.

Ventajas:

  1. No pelearse con CSS.
  2. El marcado termina luciendo como se supone que debe.
  3. No hacké las partes internas de Django.
  4. La plantilla es agradable, compacta e idiomática.

Desventajas:

código
  1. El filtro tiene que ser probado para valores interesantes de las etiquetas y los nombres de los campos de entrada.
  2. Probablemente hay algo en algún lugar que lo hace mejor y más rápido.
  3. Más trabajo de lo que pensaba hacer un sábado.
+1

¿Puede usted proporcionar el código para pretty_checkbox? Mi propio marcado preferido para esto es EoghanM

+0

No tengo el código a mano ahora. No fue muy elaborado, si no recuerdo mal, es simplemente una función que alimenta un pequeño fragmento de HTML, y cambia la etiqueta y la entrada si la entrada es una casilla de verificación. El marcado lo genera Django, por lo que cualquier preferencia que pueda tener es probablemente irrelevante. –

1

El orden de las entradas y las etiquetas se proporciona a través del parámetro normal_row del formulario y no hay patrones de filas diferentes para las casillas de verificación. Así que hay dos maneras de hacer esto (en la versión 0.96 exactamente):
1. anulación _html_output de la forma
2. Uso de CSS para cambiar la posición de la etiqueta y casilla

30

he aquí una solución que he llegado con (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 %} 

Tendrá que crear una etiqueta nueva plantilla (en este ejemplo en un "myfilters.py" archivo) que contiene algo como esto:

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) 

Más información sobre etiquetas de plantilla personalizados disponibles here.

Editar: en el espíritu de la propia respuesta de autor de la pregunta:

Ventajas:

  1. No pelearse con CSS.
  2. El marcado termina luciendo como se supone que debe.
  3. No hacké las partes internas de Django. (pero tuvo que mirar bastante)
  4. La plantilla es agradable, compacta e idiomática.
  5. El código de filtro funciona bien independientemente de los valores exactos de las etiquetas y los nombres de los campos de entrada.

Desventajas:

  1. Probablemente hay algo en algún lugar por ahí que lo hace mejor y más rápido.
  2. poco probable que el cliente esté dispuesto a pagar por todo el tiempo dedicado a esto sólo para mover la etiqueta a la derecha ...
+0

Me gusta esta respuesta. La implementación es simple, lo cual siempre es un plus. Está claro lo que sucede solo por leer la plantilla. El único inconveniente (en mi opinión sesgada y ciertamente floja) es que mi solución hace que las plantillas sean más cortas, y está más cerca de lo que cabría esperar que dijera Django en primer lugar. –

14

Tomé la respuesta de romkyns y lo hizo un poco más general

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

de esta manera se puede comprobar el tipo de widget directamente con una cadena

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

Todas las soluciones presentadas implican modificaciones de plantilla, que son en g general bastante ineficaz en cuanto al rendimiento. He aquí un widget personalizado que hace el trabajo:

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

que tienen PrettyCheckboxWidget y PrettyCheckboxField en un archivo extra, por lo que se puede importar donde sea necesario. Si no necesita traducciones, puede eliminar las partes ugettext. Este código funciona en Django 1.5 y no ha sido probado para versiones inferiores.

Ventajas:

  • gran rendimiento, no necesita modificaciones de plantilla
  • fácil de usar como un widget personalizado

Desventajas:

  • "as_table" hacer s la casilla de verificación + etiqueta dentro de la segunda columna
  • {{field.label}} dentro de la plantilla está vacía. La etiqueta se une a su lugar {{}}
  • campo
  • más trabajo que tenía planeado en hacer un sábado ;-)
2

Sé que los excluidos de usuario CSS, pero teniendo en cuenta las respuestas más tardar alrededor de media hora de trabajo para hacer algo tan pequeño, pero sabiendo que detalles como estos son importantes en un sitio web, me conformaría con la solución 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 }} 

Ventajas:

  • rápido!
  • sin pelearse con etiquetas de plantilla (solo form.as_p)
  • no hay nuevos widgets malditas
  • el archivo CSS se incluye automáticamente en todas las formas

Desventajas:

  • el código HTML no lo hace reflejar la presentación (¡pero es lo suficientemente bueno!)
  • su especialista en frontend podría quejarse
+0

También puede agregar este fragmento de css en su archivo css actual y no necesitará usar '{{form.media}}' – Caumons

0

Cambio de la posición casilla de verificación en Django administrador puede ser bastante complicado, pero por suerte hay una solución sencilla utilizando widget personalizado:

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 usa posición izquierda sólo cuando widget es subclase de CheckboxInput

Cuestiones relacionadas