For web applications, it's natural not only to provide information but also to receive it. Developers rely on form fields when users want to submit something. Django automatically uses default widgets for each form field, but they are not optimal for every task. In this topic, we will discuss widgets in Django and learn how to use them to tweak your form fields more effectively.
How widgets work
Let me introduce you to Peter, an indie developer who wants to design his own game. This is too much for a single person, so Peter decided to hire a few colleagues. Peter is a busy fellow, so he needs your help to build a page to accept candidate applications. For that task, we are going to use Django with its form fields and widgets.
A widget is an element that allows you to modify the HTML input element through Django. A widget renders HTML and retrieves data from the GET/POST dictionary of a respective widget.
If you use form fields without specifying widgets, Django will use the default widget that's appropriate for the displayed data type. You can find out which widgets Django uses by default for each data type in the Built-in Field class documentation.
But the default widget is not always the most appropriate one. Let's assume that Peter the Developer wants to ask the following question: "Why do you want to work on my game?" For that, you need to implement a comment field. You can create CharField; Django uses TextInput as the default widget and renders a one-line field on the site:
from django import forms
class MotivationalForm(forms.Form):
motivational = forms.CharField(
label="Why do you want to work on my game?"
)The field will look like this:
This line looks quite ugly; you need something wider. You can apply the Textarea widget:
class MotivationalForm(forms.Form):
motivational = forms.CharField(
label="Why do you want to work on my game?",
widget=forms.Textarea()
)The field will appear like this:
To be clear, take a look at the diagram showing the difference between form fields and widgets:
Let's try to use widgets and help Peter find like-minded folks.
Default widgets
Let's create a job application form. We'll ask for the applicant's name first. A one-line text field is pretty convenient for this. To render it, use TextInput, the default widget used for CharField:
class NameForm(forms.Form):
name = forms.CharField(label="Your Name")The field will appear on the site that way:
The default solution works fine because Peter needs to ask the name only.
Non-default widgets
To discover the skills of potential employees, Peter decided to make a selection form. MultipleChoiceField and its CheckboxSelectMultiple widget will help us. The form field will output skills in the SKILLS list, and the widget will display them as checkboxes:
SKILLS = [
('cpp', 'C++'),
('unreal', 'Unreal Engine'),
('3d', '3D Modelling'),
]
class SkillsForm(forms.Form):
skills = forms.MultipleChoiceField(
label="What skills do you have?",
required=False,
widget=forms.CheckboxSelectMultiple(),
choices=SKILLS,
)A potential employee will see this:
After that, Peter wants to specify a preferrable role. We'll create a choice list with a ChoiceField with the RadioSelect widget:
CHOICES = [
('programmer', 'C++ Programmer'),
('modeller', '3D Modeller'),
]
class RoleForm(forms.Form):
roles = forms.ChoiceField(
label="What role are you applying for?",
required=False,
widget=forms.RadioSelect(),
choices=CHOICES,
)This is how it appears on the site:
At this point, you've discovered how to handle non-default widgets. But that's not the whole story; you can customize widgets and add some attributes.
How to add widget attributes
We also want to know the candidate's email; otherwise, Peter won't be able to contact them. Create an EmailField; Django uses the EmailInput widget by default. Let's customize our email form with some attributes. For example, the placeholder attribute will add a caption to the form that tells a user what data is required:
class EmailForm(forms.Form):
email = forms.EmailField(
label="Your Email",
widget=forms.EmailInput(attrs={'placeholder': 'Enter your email, please'}
)Instead of an empty field, the user will see this:
Peter's eager to get started as soon as possible. So we should ask applicants to specify a date when they are ready to join the team. This can be resolved with the DateInput, the default widget for DateField. This widget is a tricky one. To display the date correctly, you need to additionally provide the widget with the type key and the date value:
class DateForm(forms.Form):
start_date = forms.DateField(
label="When can you get started?",
widget=forms.DateInput(attrs={'type': 'date'}),
)The last step is to render this form on the site using a template in an HTML file:
<input type="date">
{{ date_form.start_date }}Applicants will see that form with a calendar view:
This calendar case helped us figure out exactly how the widget attributes work. To consolidate your knowledge, look at the diagram:
We've only used two attributes, but that's not the whole list. You can read more about attributes in Django in the article Specifying HTML Attributes by django-gollum.
How to decorate a widget with CSS
As a final step of the questionnaire, Peter wants to collect the applicants' CVs. To do so, we can use FileField and its default widget — ClearableFileInput.
Also, we can decorate the field using Bootstrap class or any other CSS classes:
class ResumeForm(forms.Form):
resume = forms.FileField(
label="Attach your CV",
widget=forms.ClearableFileInput(attrs={'class': 'form-control'})
)By adding this attribute, your form will match your CSS:
If you want a deeper dive into the subject of CSS for attributes, the article Specifying CSS Classes by django-gollum will help you.
The questionnaire for Peter is ready. Now he can ask the applicants all the necessary questions and choose the best candidates. We hope Peter will put together a development team and create the best indie game in his career. Let's have another look at our code:
from django import forms
SKILLS = [
('cpp', 'C++'),
('unreal', 'Unreal Engine'),
('3d', '3D Modelling'),
]
CHOICES = [
('programmer', 'C++ Programmer'),
('modeller', '3D Modeller'),
]
class EmployeeForm(forms.Form):
name = forms.CharField(
label="Your Name"
)
motivational = forms.CharField(
label="Why do you want to work on my game?",
widget=forms.Textarea()
)
skills = forms.MultipleChoiceField(
label="What skills do you have?",
required=False,
widget=forms.CheckboxSelectMultiple(),
choices=SKILLS
)
roles = forms.ChoiceField(
label="What role are you applying for?",
required=False,
widget=forms.RadioSelect(),
choices=CHOICES
)
email = forms.EmailField(
label="Your Email",
widget=forms.EmailInput(attrs={'placeholder': 'Enter your email, please'}
)
start_date = forms.DateField(
label="When can you get started?",
widget=forms.DateInput(attrs={'type': 'date'})
)
resume = forms.FileField(
label="Attach your CV",
widget=forms.ClearableFileInput(attrs={'class': 'form-control'})
)It looks like this:
Of course, that's not all that the widgets can do. We've only used a few of them in this topic. You can find more in the documentation.
Conclusion
In this topic, we've discussed default and non-default widgets, how to customize them, and decorate widgets with CSS.
As you can see, widgets are a pretty helpful addition to form fields. They help you step away from the standard solutions and tailor the forms to your own needs.