ModelForm
coreintermediateModelForm generates form fields automatically from a model's fields, declared via a Meta class naming the model and which fields to include. save() creates or updates a real model instance directly — save(commit=False) returns the unsaved instance for further changes first.
Think of it as
A plain Form describes fields you write out by hand; ModelForm reads them off a model instead — Meta.model and Meta.fields are instructions ('build a form from THIS model, using THESE fields'), not a list of fields you re-type. save() is the payoff: because the form already knows which model it's building, it can turn validated cleaned_data directly into a saved (or almost-saved, with commit=False) instance, something a plain Form has no way to do.
What we're doing: Use save(commit=False) to set a field the form itself doesn't expose — the logged-in user who created the object — before the real save.
- 5
- commit=False returns a real Article instance, built from cleaned_data, but not yet written to the database — this is exactly the seam needed to add author before it is.
- 6
- article.save() now performs the actual INSERT, with author already set — a second call is required because commit=False deliberately skipped the first one.
Why this works: author should never be a form field a client can submit — it comes from request.user, not user input — so ModelForm has no way to know about it. commit=False is the documented seam for exactly this situation: fields the form itself doesn't and shouldn't expose, but that must be set before the object is actually saved.
Using fields = "__all__" on a ModelForm
Wrong
Better
What you see: A field added to the model later (e.g. is_featured, or an internal-only flag) automatically becomes editable through this form — including by a user who should never see or set it.
Why: "__all__" ties the form's exposed fields to whatever the model happens to have, including fields added long after the form was written, by someone who may not remember this form exists. An explicit list fails safe: a new model field is simply absent from the form until someone deliberately adds it.
- Article (model)
- title, body, category
- ArticleForm
- Meta.model + Meta.fields
- is_valid()
- save()
- creates or updates the instance
ModelForm.Meta options
Together
Remember: ModelForm generates fields from Meta.model/fields and knows how to save() directly — use commit=False to set a field the form doesn't expose before the real save; always use an explicit fields list, never "__all__".
See also: forms · detail and editing views

