Django: ¿Cómo agrego atributos html arbitrarios a los campos de entrada en un formulario?

101

Tengo un campo de entrada que se representa con una plantilla como esta:

<div class="field">
   {{ form.city }}
</div>

Que se representa como:

<div class="field">
    <input id="id_city" type="text" name="city" maxlength="100" />
</div>

Ahora suponga que quiero agregar un autocomplete="off"atributo al elemento de entrada que se representa, ¿cómo lo haría? ¿O onclick="xyz()"o class="my-special-css-class"?

Usuario
fuente

Respuestas:

126

Consulte esta página

city = forms.CharField(widget=forms.TextInput(attrs={'autocomplete':'off'}))
Galeno
fuente
2
Ok, gracias. En mi caso, estoy usando ModelForm, por lo que no estoy definiendo explícitamente los campos del formulario (por ejemplo, class AddressForm (forms.ModelForm): class Meta: model = models.Address) ¿Esto significa que no puedo usar ModelForm o hay algo especial? ¿necesito hacer?
Usuario
1
@InfinitelyLoopy dentro del formulario init for, puede agregar código para tomar el campo y modificar sus atributos de widgets. Aquí hay algunos que usé anteriormente para modificar 3 campos: `` `para field_name en ['image', 'image_small', 'image_mobile']: field = self.fields.get (field_name) field.widget.attrs ['data- file '] =' file '`` `
Stuart Axon
4
¿Qué pasa con los atributos que no toman argumentos como "requerido" y "enfoque automático"?
Wilhelm Klopp
1
Esta solución es mala porque no hay separación de preocupaciones. Los atributos HTML no deben escribirse en código Python en mi opinión. La solución de Mikhail Korobov es superior.
David D.
115

Perdón por los anuncios, pero recientemente lancé una aplicación ( https://github.com/kmike/django-widget-tweaks ) que hace que estas tareas sean aún menos dolorosas para que los diseñadores puedan hacerlo sin tocar el código Python:

{% load widget_tweaks %}
...
<div class="field">
   {{ form.city|attr:"autocomplete:off"|add_class:"my_css_class" }}
</div>

o alternativamente,

{% load widget_tweaks %}
...
<div class="field">
   {% render_field form.city autocomplete="off" class+="my_css_class" %}
</div>
Mikhail Korobov
fuente
3
Buena aplicación Mike, ¡justo lo que estaba buscando!
jmagnusson
la documentación no le dice que agregue "widget_tweaks" en su aplicación instalada en la configuración, podría valer la pena incluir eso en la documentación.
James Lin
Hola James, no está estresado pero en la sección 'Instalación' ya hay una nota sobre cómo agregar 'widget_tweaks' a INSTALLED_APPS.
Mikhail Korobov
@MikhailKorobov muchas gracias por esta aplicación, ¡me ayudó mucho! Esto era justo lo que estaba buscando. Necesitaba un formulario de ModelForm y no quería insertar manualmente estos atributos en cada campo (40 de ellos), así que logré lograr el mismo resultado en segundos :) ¡Esta debería ser la respuesta aceptada!
Ljubisa Livac
Estaba planeando escribir tal solicitud. Gracias por salvar mi esfuerzo.
Anuj TBE
31

Si está utilizando "ModelForm":

class YourModelForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(YourModelForm, self).__init__(*args, **kwargs)
        self.fields['city'].widget.attrs.update({
            'autocomplete': 'off'
        })
Artificioo
fuente
3
¡Bueno! No es necesario definir explícitamente todos los widgets ahora.
Mikael Lindlöf
20

Si está utilizando ModelForm, además de la posibilidad de usar __init__como @Artificioo proporcionado en su respuesta, hay un widgetsdiccionario en Meta para el caso:

class AuthorForm(ModelForm):
    class Meta:
        model = Author
        fields = ('name', 'title', 'birth_date')
        widgets = {
            'name': Textarea(attrs={'cols': 80, 'rows': 20}),
        }

Documentación relevante

Wtower
fuente
1
Tratando de averiguar por qué esto obtuvo menos votos positivos que la respuesta anterior ... a veces creo que los desarrolladores de Django / Python simplemente prefieren la forma más difícil de hacer las cosas ...
trpt4him
@ trpt4him El uso del enfoque init es útil para crear un Mixin o Clase Base que puede reutilizar en otros formularios. Esto es típico en un proyecto de mediana a gran escala. El Meta.widgets es ideal para un solo formulario. Entonces, ambas son buenas respuestas.
Akhorus
2

No quería usar una aplicación completa para esto. En su lugar, encontré el siguiente código aquí https://blog.joeymasip.com/how-to-add-attributes-to-form-widgets-in-django-templates/

# utils.py
from django.template import Library
register = Library()

@register.filter(name='add_attr')
def add_attr(field, css):
    attrs = {}
    definition = css.split(',')

    for d in definition:
        if ':' not in d:
            attrs['class'] = d
        else:
            key, val = d.split(':')
            attrs[key] = val

    return field.as_widget(attrs=attrs)

usa la etiqueta en el archivo html

{% load utils %}
{{ form.field_1|add_attr:"class:my_class1 my_class2" }}
{{ form.field_2|add_attr:"class:my_class1 my_class2,autocomplete:off" }}
ohlr
fuente
0

Aspecto y renderizado de la forma finalHe pasado bastantes días intentando crear plantillas de formulario reutilizables para crear y actualizar modelos en formularios de Django. Tenga en cuenta que estoy usando ModelForm para cambiar o crear un objeto. También estoy usando bootstrap para diseñar mis formularios. Usé django_form_tweaks para algunos formularios en el pasado, pero necesitaba algo de personalización sin mucha dependencia de la plantilla. Como ya tengo jQuery en mi proyecto, decidí aprovechar sus propiedades para diseñar mis formularios. Aquí está el código y puede funcionar con cualquier formulario.

#forms.py
from django import forms
from user.models import User, UserProfile
from .models import Task, Transaction

class AddTransactionForm(forms.ModelForm):
    class Meta:
       model = Transaction
       exclude = ['ref_number',]
       required_css_class = 'required'

Views.py

@method_decorator(login_required, name='dispatch')
class TransactionView(View):
def get(self, *args, **kwargs):
    transactions = Transaction.objects.all()
    form = AddTransactionForm
    template = 'pages/transaction.html'
    context = {
        'active': 'transaction',
        'transactions': transactions,
        'form': form
    }
    return render(self.request, template, context)

def post(self, *args, **kwargs):
    form = AddTransactionForm(self.request.POST or None)
    if form.is_valid():
        form.save()
        messages.success(self.request, 'New Transaction recorded succesfully')
        return redirect('dashboard:transaction')
    messages.error(self.request, 'Fill the form')
    return redirect('dashboard:transaction')

Nota de código HTML : estoy usando bootstrap4 modal para eliminar la molestia de crear muchas vistas. Tal vez sea mejor usar CreateView o UpdateView genérico. Enlace Bootstrap y jqQery

 <div class="modal-body">
    <form method="post" class="md-form" action="." enctype="multipart/form-data">
      {% csrf_token %}
      {% for field in form %}
      <div class="row">
        <div class="col-md-12">
          <div class="form-group row">
            <label for="" class="col-sm-4 col-form-label {% if field.field.required %}
            required font-weight-bolder text-danger{%endif %}">{{field.label}}</label>
            <div class="col-sm-8">
              {{field}}
            </div>

          </div>
        </div>
      </div>

      {% endfor %}

      <input type="submit" value="Add Transaction" class="btn btn-primary">
    </form>
  </div>

Código Javascript recuerde cargar esto en $(document).ready(function() { /* ... */});función.

var $list = $("#django_form :input[type='text']");
$list.each(function () {
    $(this).addClass('form-control')
  });
  var $select = $("#django_form select");
  $select.each(function () {
    $(this).addClass('custom-select w-90')
  });
  var $list = $("#django_form :input[type='number']");
  $list.each(function () {
    $(this).addClass('form-control')
  });
  var $list = $("form :input[type='text']");
  $list.each(function () {
    $(this).addClass('form-control')
  });
  var $select = $("form select");
  $select.each(function () {
    $(this).addClass('custom-select w-90')
  });
  var $list = $("form :input[type='number']");
  $list.each(function () {
    $(this).addClass('form-control')
  });
Fahrer Feyton
fuente