Definir clase css en formularios django

192

Supongamos que tengo un formulario

class SampleClass(forms.Form):
    name = forms.CharField(max_length=30)
    age = forms.IntegerField()
    django_hacker = forms.BooleanField(required=False)

¿Hay alguna manera de definir las clases css en cada campo de modo que pueda usar jQuery según la clase en mi página renderizada?

Esperaba no tener que construir manualmente el formulario.

ashchristopher
fuente
9
wow, ¿todos votan esto? docs.djangoproject.com/en/dev/ref/forms/widgets/…
Skylar Saveland
10
@skyl Lo tomaría como una indicación de que no es fácil de encontrar en los documentos de django. Navegué e hice varias búsquedas en Google también y no pude encontrar esto, así que me alegro por la pregunta y recibe mi voto positivo.
Nils
Sé que es un hilo antiguo pero en los campos de formulario de Django ahora tienen una clase id_fieldname.
ratsimihah
1
Consulte esta respuesta sobre cómo definir clases para un campo de formulario dentro de una plantilla respetando las clases de campo existentes
dimyG

Respuestas:

182

Otra solución que no requiere cambios en el código de Python y, por lo tanto, es mejor para los diseñadores y cambios de presentación únicos : django-widget-tweaks . Espero que alguien lo encuentre útil.

Mikhail Korobov
fuente
61
La única solución sensata, debo decir. ¡Gracias!. El código de Python, y especialmente en la definición del formulario, es el último lugar para poner cosas para el estilo; definitivamente pertenecen a las plantillas.
Boris Chervenkov
55
Esta es una gran biblioteca! Es una pena que esta respuesta esté enterrada en el fondo.
Chris Lacasse
1
¡Ideal para diseñadores! :-)
hobbes3
1
¡Excelente! Había desarrollado algunos filtros para hacer estas cosas, pero este proyecto es mucho más poderoso. ¡Gracias!
msbrogli
1
en realidad también funciona para Jinja2 :-) Cambié el orden del archivador seguro y agregué paréntesis en lugar de dos puntos {{myform.email | add_class ("css_class_1 css_class_2") | safe}} gracias por escribir esto. debería ser parte de Django.
David Dehghan
92

Respondí mi propia pregunta. Suspiro

http://docs.djangoproject.com/en/dev/ref/forms/widgets/#django.forms.Widget.attrs

No me di cuenta de que se pasó al constructor de widgets.

ashchristopher
fuente
2
¿eso implica que no se puede usar con la clase ModelForm?
xster
2
No, solo necesitaría definir explícitamente el campo de formulario en el formulario modelo para poder definir el widget. O utilice la solución que se detalla a continuación para evitar tener que manipular el campo del formulario.
Tom
78

Aquí hay otra solución para agregar definiciones de clase a los widgets después de declarar los campos en la clase.

def __init__(self, *args, **kwargs):
    super(SampleClass, self).__init__(*args, **kwargs)
    self.fields['name'].widget.attrs['class'] = 'my_class'
ashchristopher
fuente
44
Para ModelForms, esto a menudo es mejor ya que no necesita conocer el campo de formulario predeterminado que se está utilizando y puede establecer dinámicamente diferentes clases en función de las condiciones de tiempo de ejecución. Más limpio que los meta hacks de codificación ...
Daniel Naab
1
Eso supone que desea una entrada para cada campo en un formulario, que a menudo no es el caso. Un formulario no está necesariamente vinculado a un modelo; puede estar vinculado a muchos modelos. En el caso de que los campos del modelo y los campos del formulario tengan una relación 1: 1, entonces sí, ModelForm es una opción mucho mejor.
ashchristopher
44

Use django-widget-tweaks , es fácil de usar y funciona bastante bien.

De lo contrario, esto se puede hacer usando un filtro de plantilla personalizado.

Teniendo en cuenta que representa su formulario de esta manera:

<form action="/contact/" method="post">
    {{ form.non_field_errors }}
    <div class="fieldWrapper">
        {{ form.subject.errors }}
        <label for="id_subject">Email subject:</label>
        {{ form.subject }}
    </div>
</form>

form.subject es una instancia de BoundField que tiene el método as_widget .

puede crear un filtro personalizado "addcss" en "my_app / templatetags / myfilters.py"

from django import template

register = template.Library()

@register.filter(name='addcss')
def addcss(value, arg):
    css_classes = value.field.widget.attrs.get('class', '').split(' ')
    if css_classes and arg not in css_classes:
        css_classes = '%s %s' % (css_classes, arg)
    return value.as_widget(attrs={'class': css_classes})

Y luego aplique su filtro:

{% load myfilters %}
<form action="/contact/" method="post">
    {{ form.non_field_errors }}
    <div class="fieldWrapper">
        {{ form.subject.errors }}
        <label for="id_subject">Email subject:</label>
        {{ form.subject|addcss:'MyClass' }}
    </div>
</form>

Los sujetos de formulario se representarán con la clase css "MyClass".

Espero que esto ayude.

EDITAR 1

  • Actualice el filtro de acuerdo con la respuesta de dimyG

  • Añadir enlace django-widget-tweak

EDITAR 2

  • Actualizar filtro de acuerdo con el comentario de Bhyd
Charlesthk
fuente
3
¡Esto es genial! Está SECO y separa la capa de visualización de la capa de control. ¡+1 de mi parte!
alekwisnia
1
Sin embargo, ahora he notado un inconveniente. Si defino la clase en los atributos del widget, entonces este filtro 'addcss' los anula. ¿Tienes alguna idea de cómo fusionar eso?
alekwisnia
1
Quiero decir, as_widget () anula los atributos. ¿Cómo asegurarse de que usa los atributos existentes y los extiende con uno nuevo?
alekwisnia
Vea esta respuesta sobre cómo respetar las clases de campo existentes
dimyG
get('class', None).split(' ')fallará si la etiqueta no tiene el atributo de clase, como Nonese devuelve. Si lo cambia a get('class', '').split(' ')obras
dtasev
32

Ampliando el método señalado en docs.djangoproject.com:

class MyForm(forms.Form): 
    comment = forms.CharField(
            widget=forms.TextInput(attrs={'size':'40'}))

Pensé que era problemático tener que saber el tipo de widget nativo para cada campo, y pensé que era divertido anular el valor predeterminado solo para poner un nombre de clase en un campo de formulario. Esto parece funcionar para mí:

class MyForm(forms.Form): 
    #This instantiates the field w/ the default widget
    comment = forms.CharField()

    #We only override the part we care about
    comment.widget.attrs['size'] = '40'

Esto me parece un poco más limpio.

Dave
fuente
Esto es precisamente lo que otros han dicho, hace años, excepto que lo estás haciendo con 'tamaño' en lugar de 'clase' que se solicitó.
Chris Morgan
3
@Chris Morgan En realidad, es el primero en sugerir el uso de "comment.widget.attrs" en la declaración del formulario (en lugar de meterse con init o hacerlo en la vista).
DarwinSurvivor
29

Si desea que todos los campos en el formulario hereden una determinada clase, simplemente defina una clase principal, que herede de forms.ModelForm, y luego herede de ella

class BaseForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(BaseForm, self).__init__(*args, **kwargs)
        for field_name, field in self.fields.items():
            field.widget.attrs['class'] = 'someClass'


class WhateverForm(BaseForm):
    class Meta:
        model = SomeModel

Esto me ayudó a agregar la 'form-control'clase a todos los campos en todos los formularios de mi aplicación automáticamente, sin agregar replicación de código.

Oleg Belousov
fuente
1
por favor comience los nombres de clase con mayúsculas (también, BaseForm parece un nombre mucho mejor para él)
Alvaro
16

Aquí hay una manera simple de alterar a la vista. agregue a continuación a la vista justo antes de pasarlo a la plantilla.

form = MyForm(instance = instance.obj)
form.fields['email'].widget.attrs = {'class':'here_class_name'}
hombre araña
fuente
14

Simplemente agregue las clases a su formulario de la siguiente manera.

class UserLoginForm(forms.Form):
    username = forms.CharField(widget=forms.TextInput(
        attrs={
        'class':'form-control',
        'placeholder':'Username'
        }
    ))
    password = forms.CharField(widget=forms.PasswordInput(
        attrs={
        'class':'form-control',
        'placeholder':'Password'
        }
    ))
paulc
fuente
5

Aquí hay una variación de lo anterior que dará a todos los campos la misma clase (por ejemplo, jquery esquinas redondeadas agradables).

  # Simple way to assign css class to every field
  def __init__(self, *args, **kwargs):
    super(TranslatedPageForm, self).__init__(*args, **kwargs)
    for myField in self.fields:
      self.fields[myField].widget.attrs['class'] = 'ui-state-default ui-corner-all'
Timlinux
fuente
5

Puedes probar esto ...

class SampleClass(forms.Form):
  name = forms.CharField(max_length=30)
  name.widget.attrs.update({'class': 'your-class'})
...

Puede ver más información en: Django Widgets

Andres Quiroga
fuente
2

En caso de que desee agregar una clase al campo de un formulario en una plantilla (no en view.py o form.py), por ejemplo, en los casos en que desea modificar aplicaciones de terceros sin anular sus vistas, entonces un filtro de plantilla como se describe en Charlesthk la respuesta es muy conveniente. Pero en esta respuesta, el filtro de plantilla anula cualquier clase existente que pueda tener el campo.

Traté de agregar esto como una edición, pero se sugirió que se escribiera como una nueva respuesta.

Entonces, aquí hay una etiqueta de plantilla que respeta las clases existentes del campo:

from django import template

register = template.Library()


@register.filter(name='addclass')
def addclass(field, given_class):
    existing_classes = field.field.widget.attrs.get('class', None)
    if existing_classes:
        if existing_classes.find(given_class) == -1:
            # if the given class doesn't exist in the existing classes
            classes = existing_classes + ' ' + given_class
        else:
            classes = existing_classes
    else:
        classes = given_class
    return field.as_widget(attrs={"class": classes})
dimyG
fuente
Gracias por tu sugerencia. Edité mi respuesta en consecuencia con un enfoque más "pitónico".
Charlesthk
genial !!, eso es lo que estaba buscando
ugali soft
1

Como resultado, puede hacer esto en el constructor de formularios ( función init ) o después de que se inició la clase de formulario. Esto a veces es necesario si no está escribiendo su propio formulario y ese formulario proviene de otro lugar:

def some_view(request):
    add_css_to_fields = ['list','of','fields']
    if request.method == 'POST':
        form = SomeForm(request.POST)
        if form.is_valid():
            return HttpResponseRedirect('/thanks/')
    else:
        form = SomeForm()

    for key in form.fields.keys():
        if key in add_css_to_fields:
            field = form.fields[key]
            css_addition = 'css_addition '
            css = field.widget.attrs.get('class', '')
            field.widget.attrs['class'] = css_addition + css_classes

    return render(request, 'template_name.html', {'form': form})
Niki.py
fuente
1

También puede usar Django Crispy Forms , es una gran herramienta para definir formularios en caso de que desee usar un marco CSS como Bootstrap o Foundation. Y es fácil especificar clases para sus campos de formulario allí.

Su clase de formulario quisiera esto entonces:

from django import forms

from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Div, Submit, Field
from crispy_forms.bootstrap import FormActions

class SampleClass(forms.Form):
    name = forms.CharField(max_length=30)
    age = forms.IntegerField()
    django_hacker = forms.BooleanField(required=False)

    helper = FormHelper()
    helper.form_class = 'your-form-class'
    helper.layout = Layout(
        Field('name', css_class='name-class'),
        Field('age', css_class='age-class'),
        Field('django_hacker', css-class='hacker-class'),
        FormActions(
            Submit('save_changes', 'Save changes'),
        )
    )
Dmitrii Mikhailov
fuente