112

I have an input field that is rendered with a template like so:

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

Which is rendered as:

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

Now suppose I want to add an autocomplete="off" attribute to the input element that is rendered, how would I do that? Or onclick="xyz()" or class="my-special-css-class"?

Steve
  • 11,596
  • 7
  • 39
  • 53
User
  • 62,498
  • 72
  • 186
  • 247

7 Answers7

143

Check this page

city = forms.CharField(widget=forms.TextInput(attrs={'autocomplete':'off'}))
Galen
  • 29,976
  • 9
  • 71
  • 89
  • 2
    Ok thank you. In my case I am using ModelForm so I am not explicitly defining the form fields (e.g. class AddressForm(forms.ModelForm): class Meta: model = models.Address ) Does this mean I can't use ModelForm or is there something special I need to do? – User May 25 '10 at 05:02
  • 1
    ok nevermind, rtfm: http://docs.djangoproject.com/en/dev/topics/forms/modelforms/ – User May 25 '10 at 05:05
  • Hmmm, this seems to overwrite the element descending from the original model if inheriting as modelform. Anyone know how to keep from doing this? I want to keep access to the model help_text attribute – asgaines May 08 '14 at 22:26
  • 1
    @InfinitelyLoopy inside the __init__ for form, you can add some code to grab the field and modify its widgets attributes. Here is some I used earlier to modify 3 fields: ``` for field_name in ['image', 'image_small', 'image_mobile']: field = self.fields.get(field_name) field.widget.attrs['data-file'] = 'file' ``` – Stuart Axon Jun 06 '14 at 11:59
  • Sorry for the bored formatting, I can never remember how to format code on S/O after being on github :/ – Stuart Axon Jun 06 '14 at 12:03
  • 5
    What about attributes that don't take arguments like 'required' and 'autofocus' ? – Wilhelm Klopp Jan 07 '15 at 17:13
  • 2
    This solution is bad because there is no separation of concerns. HTML attributes should not be written in python code IMO. Mikhail Korobov solution is superior. – David Dahan Dec 16 '17 at 15:14
  • What if there I am using the same form for create and edit and I want to disable the field only in edit? – utkarsh2k2 Jan 11 '19 at 11:01
  • @WilhelmKlopp: Apparently use a boolean `True` for the value: `name = forms.TextInput(attrs={"required": True})` ---> `` – User Aug 30 '23 at 21:06
122

Sorry for advertisment, but I've recently released an app (https://github.com/kmike/django-widget-tweaks) that makes such tasks even less painful so designers can do that without touching python code:

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

or, alternatively,

{% load widget_tweaks %}
...
<div class="field">
   {% render_field form.city autocomplete="off" class+="my_css_class" %}
</div>
Mikhail Korobov
  • 21,908
  • 8
  • 73
  • 65
  • 3
    Nice app Mike, just what I was looking for! – jmagnusson Mar 04 '11 at 13:17
  • the documentation does not tell you to add "widget_tweaks" into your installed app in settings, might be worth to put that in to the documentation. – James Lin Nov 08 '11 at 19:01
  • Hi James, it is not stressed but in the 'Installation' section there is already a note about adding 'widget_tweaks' to INSTALLED_APPS. – Mikhail Korobov Nov 08 '11 at 23:17
  • @MikhailKorobov thank you so much for this app, it helped me a lot! This was just the right thing i was looking for. I needed a form from ModelForm and didn't want to manually insert this attributes to every single field (40 of them), so i elegantly managed to achieve same result in seconds :) This should be the accepted answer! – Ljubisa Livac Jan 28 '16 at 09:04
  • I was planning to write such application. Thanks to saving my effort. – Anuj TBE Sep 21 '17 at 07:57
  • This should be the accepted answer, even if it's not the official solution. It's way better that writing HTML attributes in python code. – David Dahan Dec 16 '17 at 15:15
  • This is not useful if you need to extract info from the model that isn't accessible in the template to put into say, a data attribute. – Katharine Osborne Apr 19 '18 at 18:32
  • I like this solution because we can manage HTML attributes in the V of the MVC: the template. @Katharine Osborne, you can pass to the template any info you need. – Gregorio Feb 19 '20 at 20:06
  • How would you use it to set properties w/o values e.g "novalidate"? – CutePoison Apr 09 '21 at 06:58
37

If you are using "ModelForm":

class YourModelForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(YourModelForm, self).__init__(*args, **kwargs)
        self.fields['city'].widget.attrs.update({
            'autocomplete': 'off'
        })
Lidor
  • 394
  • 9
  • 16
Artificioo
  • 704
  • 1
  • 9
  • 19
25

If you are using ModelForm, apart from the possibility of using __init__ as @Artificioo provided in his answer, there is a widgets dictionary in Meta for that matter:

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

Relevant documentation

mehmet
  • 7,720
  • 5
  • 42
  • 48
Wtower
  • 18,848
  • 11
  • 103
  • 80
  • 1
    Trying to figure out why this got less upvotes than the answer above... sometimes I think Django/Python developers just prefer the harder way of doing things... – trpt4him Jul 17 '15 at 01:53
  • @trpt4him Using the __init__ approach is useful to create a Mixin or Base Class that you can re-use in other Forms. This is typicall in a medium to big-scale project. The Meta.widgets is great for a single Form. So, both are good answers. – Akhorus Sep 04 '15 at 13:36
  • Just to clarify, this does not work for `django.forms.Form`? – run_the_race Mar 24 '22 at 19:11
4

I did't want to use an entire app for this thing. Instead I found the following code here 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 the tag in the html file

{% 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
  • 1,839
  • 1
  • 13
  • 29
0

Final form look and renderingI have spent quite a few days trying to create re-usable form templates to create and update models in Django forms. Note that am using ModelForm to change or create object. Am using also bootstrap to style my forms. I used django_form_tweaks for some forms in past, but I needed some customization without a lot of template dependency. Since I already have jQuery in my Project I decided to leverage its properties to style my forms. Here is the code, and can work with any form.

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

HTML Code Note: Am using bootstrap4 modal to remove the hassle of creating many views. Maybe it is better to use generic CreateView or UpdateView. Link Bootstrap and 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>

Javascript Code remember to load this in $(document).ready(function() { /* ... */}); function.

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')
  });
Alessio
  • 3,404
  • 19
  • 35
  • 48
0

Example:

class LoginForm(forms.ModelForm):
        username = forms.CharField(max_length=50, label='Username', widget=forms.TextInput(attrs={"class":"login-input", "autocomplete": "username"}))
        password = forms.CharField(label='Пароль', widget=forms.PasswordInput(attrs={"class":"login-input", "autocomplete": "current-password"}))