Django: Como adiciono atributos de html arbitrários aos campos de entrada de um formulário?

101

Eu tenho um campo de entrada que é renderizado com um modelo assim:

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

Que é processado como:

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

Agora, suponha que eu queira adicionar um autocomplete="off"atributo ao elemento de entrada que é renderizado, como eu faria isso? Ou onclick="xyz()"ou class="my-special-css-class"?

Do utilizador
fonte

Respostas:

126

Verifique esta página

city = forms.CharField(widget=forms.TextInput(attrs={'autocomplete':'off'}))
Galen
fonte
2
Ok, obrigado. No meu caso, estou usando ModelForm, então não estou definindo explicitamente os campos do formulário (por exemplo, classe AddressForm (forms.ModelForm): class Meta: model = models.Address) Isso significa que não posso usar ModelForm ou há algo especial? necessário fazer?
Usuário
1
ok deixa pra lá, rtfm
Usuário
1
@InfinitelyLoopy dentro do formulário de inicialização , você pode adicionar algum código para capturar o campo e modificar seus atributos de widgets. Aqui estão alguns que usei anteriormente para modificar 3 campos: `` `para field_name em ['image', 'image_small', 'image_mobile']: field = self.fields.get (field_name) field.widget.attrs ['data- arquivo '] =' arquivo '`` `
Stuart Axon
4
E os atributos que não aceitam argumentos como 'obrigatório' e 'foco automático'?
Wilhelm Klopp
1
Essa solução é ruim porque não há separação de interesses. Os atributos HTML não devem ser escritos no código Python IMO. A solução de Mikhail Korobov é superior.
David D.
115

Desculpe pelo anúncio, mas recentemente lancei um aplicativo ( https://github.com/kmike/django-widget-tweaks ) que torna essas tarefas ainda menos dolorosas para que os designers possam fazer isso sem mexer no código python:

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

ou alternativamente,

{% load widget_tweaks %}
...
<div class="field">
   {% render_field form.city autocomplete="off" class+="my_css_class" %}
</div>
Mikhail Korobov
fonte
3
Bom app Mike, exatamente o que eu estava procurando!
jmagnusson
a documentação não diz para você adicionar "widget_tweaks" em seu aplicativo instalado nas configurações, pode valer a pena colocá-lo na documentação.
James Lin
Olá James, não é enfatizado, mas na seção 'Instalação' já há uma nota sobre como adicionar 'widget_tweaks' ao INSTALLED_APPS.
Mikhail Korobov
@MikhailKorobov muito obrigado por este app, me ajudou muito! Essa era a coisa certa que eu estava procurando. Eu precisava de um formulário do ModelForm e não queria inserir manualmente esses atributos em todos os campos (40 deles), então elegantemente consegui obter o mesmo resultado em segundos :) Esta deve ser a resposta aceita!
Ljubisa Livac
Eu estava planejando escrever esse aplicativo. Obrigado por economizar meu esforço.
Anuj TBE
31

Se você estiver usando "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
fonte
3
Boa! Não há necessidade de definir explicitamente todos os widgets agora.
Mikael Lindlöf
20

Se estiver usando ModelForm, além da possibilidade de usar __init__como @Artificioo fornecida em sua resposta, existe um widgetsdicionário em Meta para esse assunto:

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

Documentação relevante

Wtower
fonte
1
Tentando descobrir por que isso teve menos votos positivos do que a resposta acima ... às vezes acho que os desenvolvedores Django / Python apenas preferem a maneira mais difícil de fazer as coisas ...
trpt4him
@ trpt4him Usar a abordagem init é útil para criar um Mixin ou Classe Base que você pode reutilizar em outros Formulários. Isso é típico de um projeto de médio a grande escala. O Meta.widgets é ótimo para um único Form. Portanto, ambas são boas respostas.
Akhorus,
2

Eu não queria usar um aplicativo inteiro para isso. Em vez disso, encontrei o seguinte código aqui 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)

use a tag no arquivo 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
fonte
0

Aparência e renderização da forma finalPassei alguns dias tentando criar modelos de formulário reutilizáveis ​​para criar e atualizar modelos em formulários Django. Observe que estou usando ModelForm para alterar ou criar um objeto. Também estou usando bootstrap para estilizar meus formulários. Eu usei django_form_tweaks para alguns formulários no passado, mas precisava de alguma personalização sem muita dependência de template. Como já tenho jQuery no meu projeto, decidi aproveitar suas propriedades para estilizar meus formulários. Aqui está o código e pode funcionar com qualquer formulário.

#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')

Código HTML Nota: Estou usando o modal bootstrap4 para eliminar o incômodo de criar muitas visualizações. Talvez seja melhor usar CreateView ou UpdateView genérico. Link Bootstrap e 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, lembre-se de carregar isso na $(document).ready(function() { /* ... */});função.

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
fonte