114

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"?

7 Answers 7

146

Check this page

city = forms.CharField(widget=forms.TextInput(attrs={'autocomplete':'off'}))
Sign up to request clarification or add additional context in comments.

9 Comments

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?
@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' ```
What about attributes that don't take arguments like 'required' and 'autofocus' ?
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.
|
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>

9 Comments

Nice app Mike, just what I was looking for!
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.
Hi James, it is not stressed but in the 'Installation' section there is already a note about adding 'widget_tweaks' to INSTALLED_APPS.
@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!
I was planning to write such application. Thanks to saving my effort.
|
43

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

1 Comment

Good! No need to explicitly define all widgets now.
27

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

3 Comments

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 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.
Just to clarify, this does not work for django.forms.Form?
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" }}

Comments

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')
  });

Comments

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"}))

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.