It can be. With fields = "all" or exclude, every model field not excluded becomes user editable, including ones added later. Django's docs say to list fields explicitly. An explicit list means a new column such as is_staff or balance stays out of the form until a developer chooses to add it.
What the Django docs warn
The ModelForm documentation says you must explicitly set which fields should be edited in the form using the fields attribute, because failing to do so can easily lead to security problems when a form unexpectedly allows a user to set certain fields, especially when new fields are added to a model. It adds that depending on how the form is rendered, the problem may not even be visible on the web page.
That last sentence is the dangerous part. A browser only submits fields the page shows, but an attacker can send any POST parameter. If the form class accepts a field, it will take the value.
The docs describe the include everything approach as known to be much less secure and to have led to serious exploits on major websites. They still offer two shortcuts, fields = "all" and exclude, for cases where you can guarantee the security concerns do not apply.
A concrete example
class Profile(models.Model):
display_name = models.CharField(max_length=80)
bio = models.TextField(blank=True)
is_verified = models.BooleanField(default=False)
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = "__all__"
The page may only render display name and bio. But a crafted request with is_verified=on is accepted, because the form includes it. The safer version:
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ["display_name", "bio"]
Why exclude is only slightly better
Exclude lists what to remove, so every new field is included by default. The next developer adds a permissions field to the model, forgets the form, and it is editable. An allow list fails closed. An exclude list fails open.
| Approach | New model field becomes editable? |
|---|---|
| Explicit fields list | No, until added |
| exclude list | Yes, until excluded |
| fields = "all" | Yes, always |
Where else this pattern appears
The same idea applies outside ModelForm. Serializers in API frameworks, dictionaries unpacked into model constructors, and code that does Model(**request.POST) or loops over request data with setattr all grant the user control over field names. Prefer an explicit mapping from known input names to known attributes.
Review checklist
- Search for
__all__andexclude =in form and serializer classes. - For each hit, list the model fields and mark any that a user should never set: roles, ownership, verification flags, prices, internal status.
- Replace with an explicit list.
- Add a test that posts an extra privileged field and confirms the saved object did not change.
- Make code review ask the question whenever a model gains a field.
Try it yourself
Click through it at your own pace, or open it full screen. Notice how code patterns are flagged for review before they reach production.
Quick check
Does hiding a field in the template protect it?
No. The server decides which parameters it accepts. If the form class includes the field, a crafted request can set it.
Is fields = "all" ever acceptable?
Django allows it where you can guarantee the security concerns do not apply, for example a model where every field is safe for any user to edit. Re-check whenever the model changes.
Safeguard's SAST and policy gates can flag risky form patterns in a pull request. A free scan is available.
Sources
Last reviewed: 19 October 2026.