Estilo CSS en formularios Django

150

Me gustaría diseñar lo siguiente:

formas.py:

from django import forms

class ContactForm(forms.Form):
    subject = forms.CharField(max_length=100)
    email = forms.EmailField(required=False)
    message = forms.CharField(widget=forms.Textarea)

contact_form.html:

<form action="" method="post">
  <table>
    {{ form.as_table }}
  </table>
  <input type="submit" value="Submit">
</form>

Por ejemplo, ¿cómo se ajusta una clase o ID para el subject, email, messagepara proporcionar una hoja de estilos externa a?

David542
fuente

Respuestas:

192

Tomado de mi respuesta a: Cómo marcar campos de formulario con <div class = 'field_type'> en Django

class MyForm(forms.Form):
    myfield = forms.CharField(widget=forms.TextInput(attrs={'class' : 'myfieldclass'}))

o

class MyForm(forms.ModelForm):
    class Meta:
        model = MyModel

    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        self.fields['myfield'].widget.attrs.update({'class' : 'myfieldclass'})

o

class MyForm(forms.ModelForm):
    class Meta:
        model = MyModel
        widgets = {
            'myfield': forms.TextInput(attrs={'class': 'myfieldclass'}),
        }

--- EDITAR ---
Lo anterior es el cambio más fácil de realizar en el código de la pregunta original que cumple con lo que se solicitó. También evita que se repita si reutiliza el formulario en otros lugares; sus clases u otros atributos simplemente funcionan si utiliza los métodos de formulario as_table / as_ul / as_p de Django. Si necesita control total para una representación completamente personalizada, esto está claramente documentado

- EDITAR 2 --- Se
agregó una nueva forma de especificar widget y atributos para un ModelForm.

shadfc
fuente
27
Aunque no se recomienda mezclar la presentación con la lógica empresarial.
Torsten Engelbrecht
8
¿Cómo es esta presentación? Le está dando una clase al elemento, que es solo un identificador o una categorización. Todavía tienes que definir lo que eso hace en otra parte
shadfc
9
Si y no. Las primeras clases de CSS se usan por convención para diseñar, si necesita un identificador único, es mejor usarlo id. En segundo lugar, suele ser la responsabilidad del lado de la plantilla hacer exactamente esto, especialmente si va a acceder a esta clase a través de métodos frontend (js, css). No dije que tu respuesta es incorrecta. En mi opinión, es una mala práctica (especialmente cuando trabajas en un equipo con desarrolladores frontend y backend).
Torsten Engelbrecht
66
Esto parece ridículo, solo para agregar una clase necesita tanto código? Parece que sería más fácil codificar el HTML / CSS en estas áreas (especialmente para un sitio con mucho CSS).
David542
9
¡Es una locura que Django lo haga tan incómodo!
Bryce
103

Esto se puede hacer usando un filtro de plantilla personalizado. Considere presentar su formulario de esta manera:

<form action="/contact/" method="post">
  {{ form.non_field_errors }}
  <div class="fieldWrapper">
    {{ form.subject.errors }}
    {{ form.subject.label_tag }}
    {{ form.subject }}
    <span class="helptext">{{ form.subject.help_text }}</span>
  </div>
</form>

form.subjectes una instancia de la BoundFieldcual tiene el as_widget()método

Puede crear un filtro personalizado addclassen my_app / templatetags / myfilters.py :

from django import template

register = template.Library()

@register.filter(name='addclass')
def addclass(value, arg):
    return value.as_widget(attrs={'class': arg})

Y luego aplique su filtro:

{% load myfilters %}

<form action="/contact/" method="post">
  {{ form.non_field_errors }}
  <div class="fieldWrapper">
    {{ form.subject.errors }}
    {{ form.subject.label_tag }}
    {{ form.subject|addclass:'MyClass' }}
    <span class="helptext">{{ form.subject.help_text }}</span>
  </div>
</form>

form.subjectsluego se representará con la MyClassclase CSS.

Charlesthk
fuente
55
Esta es una de las soluciones más limpias y fáciles de implementar
usuario del
55
¡Esta respuesta debería ser la mejor respuesta! ¡Es realmente más limpio que la solución propuesta por Django! Bien hecho @Charlesthk
David D.
44
Súper servicial Al principio no era obvio para mí, pero también puedes usar esto para agregar varias clases:{{ form.subject|addclass:'myclass1 myclass2' }}
smg
Me gusta que esto permita mantener las clases HTML en los archivos HTML. Al trabajar con el estilo, salto de un lado a otro entre las hojas de estilo y la estructura, no entre modelos y / o formas.
Kevin
29

Si no desea agregar ningún código al formulario (como se menciona en los comentarios a la respuesta de @ shadfc), ciertamente es posible, aquí hay dos opciones.

Primero, solo hace referencia a los campos individualmente en el HTML, en lugar de todo el formulario a la vez:

<form action="" method="post">
    <ul class="contactList">
        <li id="subject" class="contact">{{ form.subject }}</li>
        <li id="email" class="contact">{{ form.email }}</li>
        <li id="message" class="contact">{{ form.message }}</li>
    </ul>
    <input type="submit" value="Submit">
</form>

(Tenga en cuenta que también lo cambié a una lista sin ordenar ).

En segundo lugar, tenga en cuenta en los documentos sobre la salida de formularios como HTML , Django:

El id del campo se genera anteponiendo 'id_' al nombre del campo. Los atributos de identificación y las etiquetas se incluyen en la salida de forma predeterminada.

Todos sus campos de formulario ya tienen una identificación única . Por lo tanto, debe hacer referencia a id_subject en su archivo CSS para diseñar el campo de asunto . Debo señalar que así es como se comporta el formulario cuando toma el HTML predeterminado , lo que requiere simplemente imprimir el formulario, no los campos individuales:

<ul class="contactList">
    {{ form }}  # Will auto-generate HTML with id_subject, id_email, email_message 
    {{ form.as_ul }} # might also work, haven't tested
</ul>

Consulte el enlace anterior para ver otras opciones al generar formularios (puede hacer tablas, etc.).

Nota: Me doy cuenta de que esto no es lo mismo que agregar una clase a cada elemento (si agregó un campo al Formulario, también necesitaría actualizar el CSS), pero es bastante fácil hacer referencia a todos los campos por ID en tu CSS así:

#id_subject, #id_email, #email_message 
{color: red;}
John C
fuente
Intenté su segunda solución pero no funcionó. Creé la clase para id_email y no produjo ningún resultado.
Casi un principiante
@almostabeginner una cosa que puedo sugerir para la depuración: una vez que vea la página en un navegador, use Ver código fuente de la página (generalmente haciendo clic con el botón derecho) y observe la página completa real que está generando Django. Vea si los campos existen, con la identificación o el identificador de clase que espera. Además, la mayoría de los navegadores (posiblemente mediante la instalación de un complemento) pueden ejecutar un depurador que muestra el CSS que se aplica a una página, también útil para ver qué está pasando.
John C
@almostabeginner también tenga en cuenta que agregué un poco de código de muestra. En caso de que no esté claro solo en el texto, debe hacer referencia al formulario en sí, no a los campos individuales, en cuyo punto el formulario genera automáticamente HTML que contiene identificadores , como se describe. Espero que eso ayude.
John C
1
Gracias por la ayuda, el problema no era mi CSS en absoluto, el problema estaba relacionado con el caché. Así que mi antiguo CSS se almacenó, por lo tanto, ninguno de los cambios se mostraría. Acabo de borrar el caché de Chrome y todas las actualizaciones comenzaron a mostrarse.
casi un principiante
15

Según esta publicación de blog, puede agregar clases css a sus campos utilizando un filtro de plantilla personalizado.

from django import template
register = template.Library()

@register.filter(name='addcss')
def addcss(field, css):
    return field.as_widget(attrs={"class":css})

Ponga esto en la plantilla / carpeta de plantillas de su aplicación y ahora puede hacer

{{field|addcss:"form-control"}}
aashanand
fuente
2
esto debería haber sido aceptada como la verdadera respuesta a este post :)
MVDB
La mejor solución con diferencia.
Mods Vs Rockers
1
Brillante, gracias! No olvides cargar la etiqueta. Además, en Django 2.1, la única forma en que podía hacer que Django encontrara la plantilla era agregando una opción en settings.py: 'bibliotecas': {'add_css': 'app.templatetags.tag_name',}
simonbogarde
11

Puedes hacer así:

class ContactForm(forms.Form):
    subject = forms.CharField(max_length=100)
    subject.widget.attrs.update({'id' : 'your_id'})

Espero que funcione.

Ignas

Ignas Butėnas
fuente
Gracias ignas. Respuesta precisa!
Rudresh Ajgaonkar
9

Puede usar esta biblioteca: https://pypi.python.org/pypi/django-widget-tweaks

Le permite hacer lo siguiente:

{% load widget_tweaks %}
<!-- add 2 extra css classes to field element -->
{{ form.title|add_class:"css_class_1 css_class_2" }}
Eamonn Faherty
fuente
1
Eche un vistazo a la solución Charlesthk, es lo mismo sin agregar una biblioteca adicional :)
David D.
@DavidW .: Sí, pero Widget Tweaks tiene muchos más filtros, como render_field.
mrdaliri
5

Tu puedes hacer:

<form action="" method="post">
    <table>
        {% for field in form %}
        <tr><td>{{field}}</td></tr>
        {% endfor %}
    </table>
    <input type="submit" value="Submit">
</form>

Luego puede agregar clases / id a, por ejemplo, la <td>etiqueta. Por supuesto, puede usar cualquier otra etiqueta que desee. Marque Trabajar con formularios de Django como ejemplo de lo que está disponible para cada uno fielden el formulario ( {{field}}por ejemplo, solo está generando la etiqueta de entrada, no la etiqueta, etc.).

Torsten Engelbrecht
fuente
3

Una solución es usar JavaScript para agregar las clases CSS necesarias una vez que la página esté lista. Por ejemplo, estilo de salida de formulario django con clases bootstrap (jQuery se usa por brevedad):

<script type="text/javascript">
    $(document).ready(function() {
        $('#some_django_form_id').find("input[type='text'], select, textarea").each(function(index, element) {
            $(element).addClass("form-control");
        });
    });
</script>

Esto evita la fealdad de mezclar detalles de estilo con la lógica de su negocio.

Simon Feltman
fuente
3

Escribe tu formulario como:

    class MyForm(forms.Form):
         name = forms.CharField(widget=forms.TextInput(attr={'class':'name'}),label="Your Name")
         message = forms.CharField(widget=forms.Textarea(attr={'class':'message'}), label="Your Message")

En su campo HTML, haga algo como:

{% for field in form %}
      <div class="row">
        <label for="{{ field.name}}">{{ field.label}}</label>{{ field }}
     </div>
{% endfor %}

Luego, en su CSS, escriba algo como:

.name{
      /* you already have this class so create it's style form here */
}
.message{
      /* you already have this class so create it's style form here */
}
label[for='message']{
      /* style for label */
}

¡Espero que valga la pena probar esta respuesta! Tenga en cuenta que debe haber escrito sus vistas para representar el archivo HTML que contiene el formulario.

Aula
fuente
Gracias. pero, ¿cómo puedo escribir un texto de etiqueta específico?
gakeko betsi
2

Es posible que no necesite anular su clase de formulario ' __init__, porque Django establece namey idatributos en los HTML input. Puedes tener CSS como este:

form input[name='subject'] {
    font-size: xx-large;
}
tehfink
fuente
1
Para agregar a esto. Dado "subject = formas ...", id = "id_subject" y name = "subject" es la convención de Django para estos atributos. Por lo tanto, también debería poder hacer #id_subject {...}
solartic
@solartic: Tienes razón, gracias. No he mencionado esto porque el idcampo creado por Django para formsets pone muy peluda ...
tehfink
2

No vi este realmente ...

https://docs.djangoproject.com/en/1.8/ref/forms/api/#more-granular-output

Salida más granular

Los métodos as_p (), as_ul () y as_table () son simplemente atajos para desarrolladores perezosos: no son la única forma en que se puede mostrar un objeto de formulario.

clase BoundField Se usa para mostrar HTML o acceder a atributos para un solo campo de una instancia de Form.

El método str () ( unicode en Python 2) de este objeto muestra el HTML para este campo.

Para recuperar un único BoundField, use la sintaxis de búsqueda del diccionario en su formulario usando el nombre del campo como clave:

>>> form = ContactForm()
>>> print(form['subject'])
<input id="id_subject" type="text" name="subject" maxlength="100" />

Para recuperar todos los objetos BoundField, itere el formulario:

>>> form = ContactForm()
>>> for boundfield in form: print(boundfield)
<input id="id_subject" type="text" name="subject" maxlength="100" />
<input type="text" name="message" id="id_message" />
<input type="email" name="sender" id="id_sender" />
<input type="checkbox" name="cc_myself" id="id_cc_myself" />

La salida específica del campo respeta la configuración auto_id del objeto de formulario:

>>> f = ContactForm(auto_id=False)
>>> print(f['message'])
<input type="text" name="message" />
>>> f = ContactForm(auto_id='id_%s')
>>> print(f['message'])
<input type="text" name="message" id="id_message" />
BilliAm
fuente
2

Hay una herramienta muy fácil de instalar y excelente hecha para Django que utilizo para diseñar y se puede usar para todos los marcos frontend como Bootstrap, Materialise, Foundation, etc. Se llama widget-tweaks Documentation: Widget Tweaks

  1. Puedes usarlo con las vistas genéricas de Django
  2. O con tus propios formularios:

de los formularios de importación de django

class ContactForm(forms.Form):
    subject = forms.CharField(max_length=100)
    email = forms.EmailField(required=False)
    message = forms.CharField(widget=forms.Textarea)

En lugar de usar el predeterminado:

{{ form.as_p }} or {{ form.as_ul }}

Puede editarlo a su manera utilizando el atributo render_field que le brinda una forma más html de diseñarlo como en este ejemplo:

template.html

{% load widget_tweaks %}

<div class="container">
   <div class="col-md-4">
      {% render_field form.subject class+="form-control myCSSclass" placeholder="Enter your subject here" %}
   </div>
   <div class="col-md-4">
      {% render_field form.email type="email" class+="myCSSclassX myCSSclass2" %}
   </div>
   <div class="col-md-4">
      {% render_field form.message class+="myCSSclass" rows="4" cols="6" placeholder=form.message.label %}
   </div>
</div>

Esta biblioteca le brinda la oportunidad de separar bien su front-end de su backend

Alam Téllez
fuente
1

En Django 1.10 (posiblemente también antes) puede hacerlo de la siguiente manera.

Modelo:

class Todo(models.Model):
    todo_name = models.CharField(max_length=200)
    todo_description = models.CharField(max_length=200, default="")
    todo_created = models.DateTimeField('date created')
    todo_completed = models.BooleanField(default=False)

    def __str__(self):
        return self.todo_name

Formar:

class TodoUpdateForm(forms.ModelForm):
    class Meta:
        model = Todo
        exclude = ('todo_created','todo_completed')

Modelo:

<form action="" method="post">{% csrf_token %}
    {{ form.non_field_errors }}
<div class="fieldWrapper">
    {{ form.todo_name.errors }}
    <label for="{{ form.name.id_for_label }}">Name:</label>
    {{ form.todo_name }}
</div>
<div class="fieldWrapper">
    {{ form.todo_description.errors }}
    <label for="{{ form.todo_description.id_for_label }}">Description</label>
    {{ form.todo_description }}
</div>
    <input type="submit" value="Update" />
</form>
MadPhysicist
fuente
0

Editar: Otra forma (ligeramente mejor) de hacer lo que sugiero se responde aquí: estilo de campo de entrada de formulario Django

Todas las opciones anteriores son increíbles, solo pensé en agregar esta porque es diferente.

Si desea un estilo personalizado, clases, etc. en sus formularios, puede hacer una entrada html en su plantilla que coincida con su campo de formulario. Para un CharField, por ejemplo, (el widget predeterminado es TextInput), supongamos que desea una entrada de texto con aspecto de bootstrap. Harías algo como esto:

<input type="text" class="form-control" name="form_field_name_here">

Y siempre y cuando se pone el nombre de campo de formulario coincide con el html nameattribue, (y probablemente el widget debe coincidir con el tipo de entrada también) Django ejecutará todos los validadores mismos en ese campo cuando se ejecuta validateo form.is_valid()y

Diseñar otras cosas como etiquetas, mensajes de error y texto de ayuda no requiere mucha solución porque puedes hacer algo así form.field.error.as_texty estilizarlos como quieras. Los campos reales son los que requieren un poco de violín.

No sé si esta es la mejor manera, o la forma en que recomendaría, pero es una forma, y ​​podría ser adecuada para alguien.

Aquí hay un tutorial útil de formularios de estilo e incluye la mayoría de las respuestas enumeradas en SO (como usar el atributo attr en los widgets y ajustes de widgets). https://simpleisbetterthancomplex.com/article/2017/08/19/how-to-render-django-form-manually.html

kimbo
fuente
0

Instancias de widget de estilo

Si desea que una instancia de widget se vea diferente de otra, deberá especificar atributos adicionales en el momento en que el objeto de widget se instancia y se asigna a un campo de formulario (y quizás agregue algunas reglas a sus archivos CSS).

https://docs.djangoproject.com/en/2.2/ref/forms/widgets/

Para hacer esto, utiliza el argumento Widget.attrs al crear el widget:

class CommentForm(forms.Form):
    name = forms.CharField(widget=forms.TextInput(attrs={'class': 'special'}))
    url = forms.URLField()
    comment = forms.CharField(widget=forms.TextInput(attrs={'size': '40'}))

También puede modificar un widget en la definición del formulario:

class CommentForm(forms.Form):
    name = forms.CharField()
    url = forms.URLField()
    comment = forms.CharField()

    name.widget.attrs.update({'class': 'special'})
    comment.widget.attrs.update(size='40')

O si el campo no se declara directamente en el formulario (como los campos de formulario de modelo), puede usar el atributo Form.fields:

class CommentForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['name'].widget.attrs.update({'class': 'special'})
        self.fields['comment'].widget.attrs.update(size='40')

Django incluirá los atributos adicionales en la salida representada:

>>> f = CommentForm(auto_id=False)
>>> f.as_table()
<tr><th>Name:</th><td><input type="text" name="name" class="special" required></td></tr>
<tr><th>Url:</th><td><input type="url" name="url" required></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" size="40" required></td></tr>
Freman Zhang
fuente