Why does a Django form's FileField report 'This field is required.' even though the user chose a file to upload?
answer
- two dictionaries, not one
- where FileInput reads from
- the form tag's encoding
- is_multipart() in templates
- optional fields fail silently
basics
~20 sA Django FileField's widget reads only from the form's files dict, so the form must be built with files=request.FILES, and the HTML form must use enctype="multipart/form-data"; otherwise request.FILES is empty and the field sees no file.
solid answer
~30 sDjango keeps uploads separate from ordinary fields: the form takes `data` and `files`, and `FileInput.value_from_datadict()` looks only in `files`. Two independent mistakes leave it empty. First, the view builds `ImportForm(request.POST)` and never passes `request.FILES`. Second, the template's `<form>` lacks `enctype="multipart/form-data"` (or uses GET), so Django never populates `request.FILES` at all. Either way the field sees `None`, and a required `FileField` raises "This field is required."; an optional one quietly cleans to `None`, which is worse because nothing tells you the upload was lost. The fix is `ImportForm(request.POST, request.FILES)` plus the multipart form tag, which templates can choose with `form.is_multipart`.
code
python · 15 linesfrom django import forms
from django.shortcuts import render
class SubscriberImportForm(forms.Form):
csv_file = forms.FileField()
def import_subscribers(request):
if request.method == 'POST':
form = SubscriberImportForm(request.POST, request.FILES) # files= is essential
if form.is_valid():
upload = form.cleaned_data['csv_file']
...
else:
form = SubscriberImportForm()
return render(request, 'import.html', {'form': form})go deeper
Recall the two requirements: pass request.FILES as the second argument and give the form tag enctype multipart/form-data.
Explain that file widgets read from files, not data, why each mistake yields the required error, and what is_multipart() is for.
Point out that optional file fields lose uploads silently, and insist on tests that post a real file and assert it reached cleaned_data.
Discuss standardising upload forms and templates so encoding and file handling are solved once, with content validation owned explicitly.
## Where a file value comes from A Django form holds two input dictionaries: `data` for ordinary fields and `files` for uploads. They are passed separately, `ImportForm(request.POST, request.FILES)`, and the form treats itself as bound if **either** is not `None`. Each widget decides which dictionary it reads. Most use `data`. `FileInput`, and `ClearableFileInput` (the default widget of `FileField`), read from `files` only, because uploaded content never appears in `request.POST`. If the form was built without `files`, it gets an empty dictionary and the file widget finds nothing. ## The two independent causes A newsletter team adds a subscriber-import page: upload a CSV of addresses. The form looks right, the user picks a file, and the page answers "This field is required." There are two usual causes, and either one alone is enough: 1. **The view forgot `files=`.** `ImportForm(request.POST)` leaves the form's `files` empty, so the widget returns `None` even though `request.FILES` holds the upload. 2. **The template forgot the encoding.** A `<form method="post">` without `enctype="multipart/form-data"` is sent URL-encoded. Django fills `request.FILES` only for a POST sent as multipart form data, so it stays empty however correct the view is. Templates that render forms generically can let the form choose: `form.is_multipart()` returns True when any field's widget needs multipart encoding, which every file input does. ## Required versus optional fields - A **required** `FileField` that sees `None` raises the standard `required` error. That is the visible symptom. - An **optional** `FileField` (`required=False`) cleans `None` without complaint. The form is valid, the upload is simply gone, and the bug can ship unnoticed. Tests for upload forms should post a real file and assert it arrived in `cleaned_data`. ## What FileField checks once a file arrives | Error code | Default message | When | |---|---|---| | `required` | This field is required. | required field, no file | | `invalid` | No file was submitted. Check the encoding type on the form. | the value has no file name or size, for example a plain string | | `empty` | The submitted file is empty. | zero-byte file while `allow_empty_file=False` (the default) | | `max_length` | Ensure this filename has at most ... characters | `max_length` set and the file name is longer | | `contradiction` | Please either submit a file or check the clear checkbox, not both. | `ClearableFileInput` got a new file and a clear request | Note that the `invalid` message mentions the encoding type, but the common missing-enctype case produces `required`, because the file widget never looks at `request.POST` where the browser put the file name. ## Keeping or clearing an existing file On an edit page the field may already hold a file, passed to the form as its initial value (for example a stored file with a URL): - If no new file is posted, `FileField.clean()` returns the initial file, so an edit that changes other fields keeps the stored one. - `ClearableFileInput` renders a **clear** checkbox for an optional field whose initial value has a URL. Ticking it makes the widget return `False`, which the optional field cleans to `False`, the signal to remove the file. - Ticking clear while also uploading a new file raises the `contradiction` error. - Because the kept value comes from the initial data, forgetting to pass it when binding the POST reproduces the missing-file symptom on edit pages. ## What cleaned_data gives you After validation, `cleaned_data['csv_file']` is an uploaded-file object with a `name`, a `size` and methods to read the content. `FileField` checks only that something file-like arrived; it does not inspect the content, so parsing the CSV or limiting its size is your code's job. Saving it to storage and tuning upload handlers belong to upload handling in views. `ImageField` extends `FileField` with an image-type check. ## What interviewers listen for - Uploads live in `request.FILES`, passed as the form's second argument. - The form tag needs multipart encoding, and `is_multipart()` can pick it. - A required field fails loudly, an optional one fails silently.
- In Django, is a form bound if it is constructed with only files=request.FILES and no data?Yes. Django marks a form bound when either `data` or `files` is not `None`, so `ImportForm(files=request.FILES)` validates. Ordinary fields then see an empty data dictionary and required ones fail.
- What does Django's forms.FileField do with a zero-byte upload by default?It rejects it with the `empty` error, "The submitted file is empty.", because `allow_empty_file` defaults to False. Pass `allow_empty_file=True` when an empty file is a legitimate submission.
saying these in an interview costs you the question
- Uploaded files arrive in request.POST alongside the other fields.
- Passing request.POST alone is enough for a form with a FileField.
- A missing enctype produces the 'invalid' error on the file field.
- An optional FileField warns you when the upload was lost.
- FileField validates the file's content type and structure.