skip to content

In Django forms, what does a field's required flag actually check, and why does a BooleanField reject an unticked checkbox?

level: juniorimportance: should knowfreq 46%

answer

  1. checked after coercion
  2. the field's empty_values
  3. browsers omit unticked boxes
  4. required means must be ticked
  5. empty_value per field type

basics

~20 s

A Django field with required=True (the default) rejects a cleaned value that is empty: None, '', or an empty list, tuple or dict. An unticked checkbox cleans to False, and a required BooleanField treats False as missing.

solid answer

~40 s

`required` defaults to True on every Django form field. After `to_python()` coerces the input, `validate()` raises the `required` error ("This field is required.") if the value is one of the field's empty values: `None`, `''`, `[]`, `()` or `{}`. Because `CharField` strips whitespace first, three spaces count as empty. With `required=False` the field instead cleans empty input to its own empty value: `''` for `CharField` (configurable with `empty_value`), `None` for `DateField` and `IntegerField`, `[]` for `MultipleChoiceField`. Browsers do not submit unticked checkboxes, so `CheckboxInput` reports False, and `BooleanField.validate()` raises `required` for any falsy value. A required `BooleanField` therefore means "must be ticked", which suits a consent box and breaks an optional "partner offers" box unless you set `required=False`.

code

python · 13 lines
python
from django import forms

class NewsletterSignupForm(forms.Form):
    email = forms.EmailField()
    first_name = forms.CharField(required=False, empty_value=None)
    accept_privacy = forms.BooleanField()                  # must be ticked
    partner_offers = forms.BooleanField(required=False)    # may stay unticked

f = NewsletterSignupForm(data={'email': '[email protected]', 'first_name': '   '})
f.is_valid()                     # False
f.errors['accept_privacy']       # ['This field is required.']
f.cleaned_data['first_name']     # None
f.cleaned_data['partner_offers'] # False

go deeper

for a junior

Recall that fields are required by default, that an unticked checkbox cleans to False, and that a required BooleanField therefore means the box must be ticked.

for a middle

Explain the order: widget extraction, to_python with stripping, then the empty-values check, and list each field type's empty value, including CharField's empty_value.

for a senior

Show judgment about data meaning: empty string versus None, the forms.NullBooleanField form field for tri-state answers, and why browser required attributes never replace server checks.

for a principal

Discuss consistent conventions for optional data across forms and APIs so that blank, absent and false are not conflated in stored records.

## What required checks Every Django form field accepts `required`, and it defaults to **True**. The check happens during field cleaning, after conversion: 1. The widget pulls the raw value out of the submitted data. 2. `to_python()` coerces it. `CharField` with the default `strip=True` trims whitespace here, so `' '` becomes `''`. 3. `validate()` raises a `ValidationError` with code `required` and the message "This field is required." if the value is in the field's **empty values**: `None`, `''`, `[]`, `()` or `{}`. So `required` is not "the key is present in POST"; it is "the coerced value is not empty". A field missing from the submission and a field submitted blank fail the same way. ## What required=False gives you instead An optional field that receives nothing cleans to that field's **empty value** rather than failing: | Field | Empty input cleans to | |---|---| | `CharField`, `EmailField` | `''` (set `empty_value` to change it) | | `IntegerField`, `DateField` | `None` | | `BooleanField` | `False` | | `ChoiceField` | `''` | | `TypedChoiceField` | its `empty_value`, `''` unless set | | `MultipleChoiceField` | `[]` | | `FileField` | `None` | `CharField(required=False, empty_value=None)` is the usual way to tell "left blank" apart in later code when an empty string is ambiguous. The value you get is typed and predictable, so views should read `cleaned_data['first_name']` rather than testing whether a key exists in `request.POST`. ## The checkbox case HTML forms do not send anything for an unticked checkbox. Django's `CheckboxInput.value_from_datadict()` handles that by returning `False` when the name is absent, and it maps the strings `'true'` and `'false'` to booleans. `BooleanField.validate()` then applies its own rule: if the value is falsy **and** the field is required, raise `required`. - For a newsletter's "I agree to the privacy policy" box that rule is exactly right: `forms.BooleanField()` forces the tick. - For "also send me partner offers" it is wrong: the unticked box fails validation. Declare `forms.BooleanField(required=False)` and read `True` or `False` from `cleaned_data`. - For a genuine three-state answer (yes, no, not said) use the form field `forms.NullBooleanField`, whose default widget is a select and whose empty value is `None`. (It is current; only the model field of that name was removed, in Django 4.0, in favour of `BooleanField(null=True)`.) ## What counts as empty, and what does not The generic check compares the coerced value against the empty values only, which has consequences worth knowing: - `0` is **not** empty. A required `IntegerField` accepts `0`; if zero is not a sensible answer, add `min_value=1`. - `False` is not empty for the generic check either. It is `BooleanField`'s own `validate()` that treats a falsy value as missing, which is why the checkbox rule is specific to that field. - A `ChoiceField` whose first option is a placeholder such as `('', 'Choose a frequency')` enforces a real choice through `required`: the placeholder submits `''`, which is empty, so the form reports the required error rather than an invalid choice. - A required `MultipleChoiceField` with nothing selected receives an empty list and fails the same way. ## Browser side versus server side - Because the form's `use_required_attribute` is True by default, required fields also render the HTML `required` attribute. That is a convenience for the user; a crafted POST skips it and the server check still runs. - Error text can be changed per field with `error_messages={'required': '...'}` while keeping the same `required` code. - `required` is a per-field rule. Cross-field rules such as "birthday is required if the subscriber picked the birthday offer" belong in form-level cleaning. ## What interviewers listen for That `required` is checked on the coerced value, that each field type has its own empty value, and that a required `BooleanField` means "must be True". Candidates who have shipped a sign-up form usually remember the partner-offers checkbox that nobody could leave unticked.

  • In Django forms, how would you model a subscriber's answer that can be yes, no or not stated?
    Use `forms.NullBooleanField`. Its default `NullBooleanSelect` widget offers unknown, yes and no, it cleans unrecognised or empty input to `None`, and it never raises the `required` error, so a missing answer is stored as `None` rather than `False`.
  • In Django, does removing the HTML required attribute from a rendered field make that field optional?
    No. The attribute only affects the browser. Server-side cleaning still raises the `required` error while the field's `required` flag is True; to make the field optional you set `required=False` on the field.

saying these in an interview costs you the question

  • required=True only checks that the key is present in request.POST.
  • An unticked checkbox is submitted as the string 'off'.
  • A required BooleanField accepts False as a valid answer.
  • Whitespace-only input passes a required CharField.
  • Every optional Django form field cleans empty input to None.