Django form field types: Widgets, options and validations

Problem

You want to create Django forms and understand what are the different field types including their widgets, options and validations.

Solution

All Django form fields are required, to allow a field value to be left empty -- None or empty string '' -- then a field must be assigned the required=False argument. The define a default value on a form field you can use the initial argument. To restrict a form field's values to a predetermined set of values you can use the choices argument.

To limit text values on form fields you can use the max_length, min_length, strip and validators options. To limit numbers on form fields you can the max_value, min_value, max_digits, decimal_places and validators options.

To override and define a custom error message on a form field you can use the error_messages option. To override a field's label output you can use the label option, to override a field's label suffix output you can use the label_suffix option and to define a field's help text you can use the help_text option.

How it works

Web forms are used to collect data from end users on the Internet. Because of the uncontrolled nature of the Internet -- with its many types of devices, browsers and user experience levels -- it creates all kinds of demands on web forms regarding the types of data they must handle, as well as the endless variations of how data must be sanitized to comply with the original purpose of a web form. When you create web forms for Django applications you rely on Django form classes, where each Django form class is composed of fields. Each Django form field definition is important because it dictates a small piece of functionality that eventually composes the bulk of a web form's overall behavior.

Django form fields define two types of functionality, a form field's HTML markup and its server-side validation facilities. For example, a Django form field translates into the actual HTML form markup (e.g. an <input>, <select> or <textarea> tag), the HTML markup attributes (e.g. length, whether a field can be left blank or if a field must be disabled), as well as the necessary hooks to easily perform server-side validation on a form field's data.

Django form fields define these two types of web form functionality out of necessity. Although browsers have progressed tremendously with technologies like HTML5 to provide out of the box form field validation through the HTML markup itself -- without JavaScript -- a browser is still in full control of an end user that with sufficient knowledge can bypass and feed whatever data he wants to a form. Therefore, it's standard practice to further inspect form field data once it's submitted by users to see if it complies with a form's rules, a process that's made very easy thanks to the use of Django form fields.

Table 1 illustrates the various Django forms fields, including: their type, the HTML they produce, their default widget and their validation behavior.

Table 1 - Django form field types, generated HTML, default widget and validation behavior
Field typeDjango form field typeHTML outputDefault Django widgetValidation behavior
Booleanforms.BooleanField()<input type='checkbox' ...>forms.widgets.CheckboxInput()Generates HTML checkbox input markup to obtain a boolean True or False value; returns False when the checkbox is unchecked, True when the checkbox is checked.
Booleanforms.NullBooleanField()<select><option value="1" selected="selected">Unknown</option><option value="2">Yes</option><option value="3">No</option></select>forms.widgets.NullBooleanSelect()Works just like BooleanField but also allows "Unknown" value; returns None when the Unknown(1) value is selected, True when the Yes(2) value is selected and False when the No(3) value is selected.
Textforms.CharField()<input type="text" ...>forms.widgets.TextInput()Generates HTML text input markup.
Text (Specialized)forms.EmailField()<input type="email" ...>forms.widgets.EmailInput()Generates HTML email input markup. Note this HTML5 markup is for browser-side email validation and will only work if the browser supports HTML5, if a browser doesn't support HTML5 then it treats this markup as a regular text input. Django server-side form validation is done for email irrespective of HTML5 support.
Text (Specialized)forms.GenericIPAddressField()<input type="text" ...>forms.widgets.TextInput()Works just like CharField, but server-side Django validates the (text) value can be converted to an IPv4 or IPv6 address (e.g.192.46.3.2, 2001:0db8:85a3:0000:0000:8a2e:0370:7334).
Text (Specialized)forms.RegexField( regex='regular_expression')<input type="text" ...>forms.widgets.TextInput()Works just like CharField, but server-side Django validates the (text) value complies with the regular expression defined in regex. Note regex can be either a string that represents a regular expression (e.g. \.com$ for a string that ends in .com) or a compiled Python regular expression from Python's re package (e.g. re.compile('\.com$') )
Text (Specialized)forms.SlugField()<input type="text" ...>forms.widgets.TextInput()Works just like CharField, but server-side Django validates the (text) value can be converted to slug. In Django a 'slug' is a value that contains only lower case letters, numbers, underscores and hyphens, which is typically used to sanitize URLs and file names (e.g. the slug representation of 'What is a Slug ?! A sanitized-string' is what-is-a-slug-a-sanitized-string.
Text (Specialized)forms.URLField()<input type="url" ...>forms.widgets.URLInput()Generates HTML url input markup. Note this HTML5 markup is for browser-side url validation and will only work if the browser supports HTML5, if a browser doesn't support HTML5 then it treats this markup as a regular text input. Django server-side form validation is done for url irrespective of HTML5 support.
Text (Specialized)forms.UUIDField()<input type="text" ...>forms.widgets.TextInput()Works just like CharField, but server-side Django validates the (text) value can be converted to UUID (Universally unique identifier).
Text (Specialized)forms.ComboField(fields=[field_type1,field_type1])<input type="text" ...>forms.widgets.TextInput()Works just like CharField, but server-side Django enforces the data pass the applicable rules for a list of Django form fields in fields (e.g.ComboField(fields=[CharField(max_length=50), SlugField()]) enforces the data be a slug field with a maximum length of 50 characters)
Text (Specialized)forms.MultiValueField()Varies depending on definitionforms.widgets.TextInput()Designed to create custom form fields made up of multiple pre-exisiting form fields (e.g. Social Security form field made up of three CharField()). It requires a subclass implementation (e.g. class SocialSecurityField(MultiValueField):) to include the base form fields and validation logic of the new field.
Text (Specialized)forms.FilePathField( path='directory')<select ><option value="directory/file_1">file_1</option><option value="directory/file_2">file_2</option><option value="directory/file_2">file_2</option></select>forms.widgets.Select()Generates HTML select list from files located in server-side path directory. Note value is composed of path+filename and display is just filename
Filesforms.FileField()<input type="file" ...>forms.widgets.ClearableFileInput()Generates HTML file input markup so end user is able to select a file through his web browser. In addition, it provides various utilities to handle post-processing of files
Files (Specialized)forms.ImageField()<input type="file" ...>forms.widgets.ClearableFileInput()Generates HTML file input markup so end user is able to select an image file through his web browser. Works just like FileField but provides additional utilities to handle post-processing of images using the Pillow package. Note this field forces you to install Pillow (e.g. pip install Pillow).
Date/timeforms.DateField()<input type="text" ...>forms.widgets.DateInput()Works just like CharField, but server-side Django validates the (text) value can be converted to a datetime.date, datetime.datetime or string formatted in a particular date format (e.g. 2017-12-25, 11/25/17).
Date/timeforms.TimeField()<input type="text" ...>forms.widgets.TextInput()Works just like CharField, but server-side Django validates the (text) value can be converted to a datetime.time or string formatted in a particular time format (e.g. 15:40:33, 17:44).
Date/timeforms.DateTimeField()<input type="text" ...>forms.widgets.DateTimeInput()Works just like CharField, but server-side Django validates the (text) value can be converted to a datetime.datetime, datetime.date or string formatted in a particular datetime format (e.g. 2017-12-25 14:30:59, 11/25/17 14:30).
Date/timeforms.DurationField()<input type="text" ...>forms.widgets.TextInput()Works just like CharField, but server-side Django validates the (text) value can be converted to a timedelta. Note Django uses the django.utils.dateparse.parse_duration() method as a helper, which means the string can match the format DD HH:MM:SS.uuuuuu (e.g. 2 1:10:20 for a timedelta of 2 days, 1 hour, 10 minutes, 20 seconds).
Date/timeforms.SplitDateTimeField()<input type="text" name="_0" ...> <input type="text" name="_1" ...>forms.widgets.SplitDateTimeWidgetWorks similar to DateTimeField but generates two separate text inputs for date & time , unlike DateTimeField which expects a single string with date & time. Validation wise Django enforces the date input can be converted to a datetime.date and the time input can be converted to a datetime.time.
Numberforms.IntegerField()<input type="number" ...>forms.widgets.NumberInput()Generates HTML number input markup. Note this HTML5 markup is for browser-side number validation and will only work if the browser supports HTML5, if a browser doesn't support HTML5 then it treats this markup as a regular text input. Django server-side form validation is done for an integer number irrespective of HTML5 support.
Numberforms.DecimalField()<input type="number" ...>forms.widgets.NumberInput()Generates HTML number input markup. Note this HTML5 markup is for browser-side number validation and will only work if the browser supports HTML5, if a browser doesn't support HTML5 then it treats this markup as a regular text input. Django server-side form validation is done for a decimal number irrespective of HTML5 support.
Numberforms.FloatField()<input type="number" ...>forms.widgets.NumberInput()Generates HTML number input markup. Note this HTML5 markup is for browser-side number validation and will only work if the browser supports HTML5, if a browser doesn't support HTML5 then it treats this markup as a regular text input. Django server-side form validation is done for a float number irrespective of HTML5 support.
Predefined valuesforms.ChoiceField( choices=tuple_of_tuples)<select><option value="tuple1_1" selected="selected">tuple1_2</option><option value="tuple_2_1">tuple_2_2</option><option value="tuple_3_1">tuple_3_2</option></select>forms.widgets.Select()Generates HTML select list from choices tuple of tuples (e.g.((1,'United States'),(2,'Canada'),(3,'Mexico'))). Note with ChoiceField if no value is selected an empty string '' is passed for post-processing and if a value like '2' is selected a literal string is passed for post-processing, irrespective of the original data representation. See TypeChoiceField or clean form methods to override these last behaviors for empty values and string handling.
Predefined valuesforms.TypeChoiceField( choices=tuple_of_tuples, coerce=coerce_function, empty_value=None)<select><option value="tuple1_1" selected="selected">tuple1_2</option><option value="tuple_2_1">tuple_2_2</option><option value="tuple_3_1">tuple_3_2</option></select>forms.widgets.Select()Works just like ChoiceField but provides extra post-processing functionality with the coerce and empty_value arguments. For example, with TypeChoiceField you can define a different default value with the empty_value arguement (e.g.empty_value=None) and you can define a coercion method with the coerce argument so the selected value is converted from its string representation (e.g. with coerce=int a value like '2' gets converted to 2 (integer) through the built-in int function).
Predefined valuesforms.MultipleChoiceField( choices=tuple_of_tuples)<select multiple='multiple'><option value="tuple1_1" selected="selected">tuple1_2</option><option value="tuple_2_1">tuple_2_2</option><option value="tuple_3_1">tuple_3_2</option></select>forms.widgets.SelectMultiple()Generates HTML select list for multiple values from choices tuple of tuples (e.g.((1,'United States'),(2,'Canada'),(3,'Mexico'))). It works just like ChoiceField but allows multiple values to be selected available as a list post-processing.
Predefined valuesforms.TypedMultipleChoiceField( choices=tuple_of_tuples, coerce=coerce_function, empty_value=None)<select multiple='multiple'><option value="tuple1_1" selected="selected">tuple1_2</option><option value="tuple_2_1">tuple_2_2</option><option value="tuple_3_1">tuple_3_2</option></select>forms.widgets.Select()Works just like MultipleChoiceField but provides extra post-processing functionality with the coerce and empty_value arguments. For example, with TypedMultipleChoiceField you can define a different default value with the empty_value argument (e.g.empty_value=None) and you can define a coercion method with the coerce argument so the selected value is converted from its string representation (e.g. with coerce=int a value like '2' gets converted to 2 (integer) through the built-in int function).

As you can see in table 1, the Django form fields provided out-of-the-box support the generation of practically every HTML form field in existence, as well as provide the necessary server-side validation for a wide array of data types. For example, you can use CharField() to capture standard text or the more specialized EmailField() to ensure the captured value is a valid email. Just as you can use ChoiceField() to generate a form list with predefined values or DateField() to enforce a form value is a valid date.

What are Django widgets ? And what is their relationship with Django form fields ?

In table 1 you can see that besides the actual Django form field syntax (e.g. forms.CharField(), forms.ImageField()) each form field is associated with a default widget. Django widgets for the most part go unnoticed conceptually and are often mixed together with the functionality of a form field itself (e.g. if you want an HTML text input like <input type="text"..> use forms.CharField()). However, when you require changes to the HTML produced by a form field or the way a form field's data is initially processed, then you'll inevitably need to work with Django widgets.

To make matters a little more confusing, there are many options you specify on form fields that end up being used as part of a widget. For example, the form field forms.CharField(max_length=25) tells Django to limit a value to a maximum of 25 characters upon processing, but this same max_length option is passed to the forms.widgets.TextInput() widget to generate the HTML <input type="text" maxlength="25"...> to enforce the same rule on the browser via the maxlength="25" attribute. So in this case, you can actually change the HTML output through a form field option, without even knowing about widgets.

So do you really need to work with widgets to change the HTML produced by a form field ? The answer is it depends. A lot of form fields options are automatically passed to a widget behind the scenes in effect altering the generated HTML, but make no mistake it's a widget that's tasked with generating the HTML and not the form field. For cases when a form field's options can't achieve a desired HTML output, then it becomes necessary to change a form field's widget to achieve a custom HTML output.

Now that you know about the existence of Django widgets and their relationship with Django form fields, in the next sections I'll explain the various Django form fields options and their validation behavior. In the next recipe Set up the layout for Django forms in templates I'll describe everything related to form layout issues in templates and how to override Django widgets.

Empty, default and predetermined values: required, initial and choices.

By default, all Django form fields are marked as required which means every field must contain a value to be considered valid. The required argument is valid across all Django form fields described in table 1 and in addition to enforcing that a value is not empty on the server-side, the HTML 5 required attribute is also assigned to a form field so a user's browser also performs a requirement check.

If you want to allow a field value to be left empty -- None or empty string '' -- then a field must be assigned the required=False argument.

In certain cases you may not want to allow a field to be left empty, but rather assign a default value to a field, in which case you can use the initial argument. The initial argument is equally valid across all Django form fields described in table 1 and is described in detail in the past recipe on form processing in the section Initialize forms.

For cases in which you don't want to allow a user the ability to introduce open-ended values for a field you can restrict a field's values to a predetermined set of values through the choices argument. If you want to use the choices attribute you must use a form field data type designed to generate an HTML <select> list such as forms.ChoiceField(), forms.MultipleChoiceField or forms.FielPathField(). The choices argument cannot be used on data types like forms.CharField() designed for open-ended input.

Limiting text values: max_length, min_length, strip and validators

Form field data types that accept text such as CharField(), EmailField() and others described in table 1, can accept both the max_length and min_length arguments to restrict a field's value to a maximum and minimum character length, respectively.

The strip argument is used to apply Python's strip() method -- which strips all trailing and leading whitespace -- to a field's value. The strip argument applies to two Django field data types, CharField() which defaults to strip=True and RegexField() which defaults to strip=False.

To apply more advanced limitation rules on fields that accept text values, see the previous recipe's Validating form values section which describes validators and other techniques to limit field values.

Limiting number values: max_value, min_value, max_digits, decimal_places and validators.

Form field data types that accept numbers such as IntegerField(), DecimalField() and FloatField() can accept both the max_value and min_value arguments to restrict the upper and lower bounds of field's number value, respectively.

In addition, the DecimalField() data type accepting a more elaborate type of number can use the max_digits argument to restrict the maximum number of digits in a value or the decimal_places argument to specify the maximum number of decimal places permitted in a value.

To apply more advanced limitation rules on fields that accept number values, see the previous recipe's Validating form values section which describes validators and other techniques to limit field values.

Error messages: error_messages

Every Django field data type has built-in error messages. For example, when a field data type is required and no value is added by a user, Django associates the error message This field is required to the field. Similarly, if a field data type uses the max_length argument and the value provided by a user exceeds this threshold, Django associates the error message Ensure this value has at most X characters (it has X). to the field.

While these built-in error messages are often descriptive enough, it's possible to customize these messages through the error_messages argument. The error_messages argument expects a dictionary where each key is the message code and the value represents the actual error message. For example, to provide a custom message for the required code you would use the syntax:CharField(errors_messages={"required":"Please provide a value, it's required"}).

Table 2 illustrates the various error message codes, their default message, as well as the form field data types that apply to each code.

Table 2 - Django form field error message codes, default error messages and application ford field data types
Error codeDefault error messageApplicable form field data types
required()BooleanField, CharField, ChoiceField, TypedChoiceField, DateField, DateTimeField, DecimalField, DurationField, EmailField, FileField, FilePathField, FloatField, ImageField, IntegerField, GenericIPAddressField, MultipleChoiceField, TypedMultipleChoiceField, RegexField, SlugField, TimeField, URLField, UUIDField, ComboField, MultiValueField, SplitDateTimeField
max_length()CharField, FileField
min_length()CharField
invalid_choice()ChoiceField, TypedChoiceField, FileField, FilePathField, ImageField, MultipleChoiceField, TypedMultipleChoiceField
invalid()DateField, DateTimeField, DecimalField, DurationField, EmailField, FloatField, IntegerField, GenericIPAddressField, RegexField, SlugField, TimeField, URLField, UUIDField, ComboField, MultiValueField, SplitDateTimeField
max_value()DecimalField, FloatField, IntegerField
min_value()DecimalField, FloatField, IntegerField
max_digits()DecimalField
max_decimal_places()DecimalField
max_whole_digits()DecimalField
missing'No file was submitted.'FileField, ImageField
empty'The submitted file is empty.'FileField, ImageField
invalid_image'Upload a valid image. The file you uploaded was either not an image or a corrupted image'ImageField
invalid_list'Enter a list of values'MultipleChoiceField
incomplete'Enter a complete value'MultiValueField
invalid_date'Enter a valid date'SplitDateTimeField
invalid_time'Enter a valid time'SplitDateTimeField

If you want to define more sophisticated error messages, see the previous recipe's Error form values section which describes how to create custom error message and other techniques related to error handling.

Field layout values: label, label_suffix, help_text

When you output a form field in a template besides the essential HTML form field markup (e.g. <input type="text">) it's almost always accompanied by a human-friendly descriptor to indicate what a field is for (e.g. Email: <input type="text">). This human-friendly descriptor is called a label and by default in Django it's assigned the same value as the field name.

To customize a form field's label you can use the label argument. For example, to provide a more descriptive label for a field named email you can use the syntax email = EmailField(label="Please provide your email"). By default, all labels on a form are accompanied by the : symbol which functions as a suffix. You can further customize the output of field labels with the label_suffix argument on individual form fields or the form instance itself. For example, the syntax email = EmailField(label_suffix='-->') overrides the default suffix label on the email field for the --> symbol. In addition, it's also possible to use the syntax form = ContactForm(label_suffix='-->') so all form fields use the same suffix label --> symbol, the previous recipe on form processing in the section Initialize forms describes this last technique in detail.

In certain circumstances it can be helpful to add more explicit instructions to a form field, for such cases you can use the help_text argument. Depending on the template layout you use, the help_text value is added right next to the HTML form field markup (e.g. <input type="text">). For example, the syntax comment = CharField(help_text="Please be as specific as possible to receive a quick response") generates the given html_text value right next to the comment field. The next recipe Set up the layout for Django forms in templates goes into further detail about this and other form layout properties.