summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorLuke Plant <L.Plant.98@cantab.net>2013-02-21 21:56:55 +0000
committerLuke Plant <L.Plant.98@cantab.net>2013-05-09 16:44:36 +0100
commitf026a519aea8f3ea7ca339bfbbb007e1ee0068b0 (patch)
tree882e594178a78d507ede36c2c9b0b8d5a9305561 /docs
parent1e37cb37cec330a6b78925e2ef5589817428d09a (diff)
downloaddjango-f026a519aea8f3ea7ca339bfbbb007e1ee0068b0.tar.gz
Fixed #19733 - deprecated ModelForms without 'fields' or 'exclude', and added '__all__' shortcut
This also updates all dependent functionality, including modelform_factory and modelformset_factory, and the generic views `ModelFormMixin`, `CreateView` and `UpdateView` which gain a new `fields` attribute.
Diffstat (limited to 'docs')
-rw-r--r--docs/ref/class-based-views/generic-editing.txt2
-rw-r--r--docs/ref/class-based-views/mixins-editing.txt12
-rw-r--r--docs/ref/contrib/admin/index.txt34
-rw-r--r--docs/ref/forms/models.txt8
-rw-r--r--docs/releases/1.6.txt52
-rw-r--r--docs/topics/auth/customizing.txt1
-rw-r--r--docs/topics/class-based-views/generic-editing.txt37
-rw-r--r--docs/topics/forms/modelforms.txt163
8 files changed, 204 insertions, 105 deletions
diff --git a/docs/ref/class-based-views/generic-editing.txt b/docs/ref/class-based-views/generic-editing.txt
index 1dbb427036..555ba40cfb 100644
--- a/docs/ref/class-based-views/generic-editing.txt
+++ b/docs/ref/class-based-views/generic-editing.txt
@@ -110,6 +110,7 @@ CreateView
class AuthorCreate(CreateView):
model = Author
+ fields = ['name']
UpdateView
----------
@@ -152,6 +153,7 @@ UpdateView
class AuthorUpdate(UpdateView):
model = Author
+ fields = ['name']
DeleteView
----------
diff --git a/docs/ref/class-based-views/mixins-editing.txt b/docs/ref/class-based-views/mixins-editing.txt
index 3f32269742..51d8628818 100644
--- a/docs/ref/class-based-views/mixins-editing.txt
+++ b/docs/ref/class-based-views/mixins-editing.txt
@@ -116,6 +116,18 @@ ModelFormMixin
by examining ``self.object`` or
:attr:`~django.views.generic.detail.SingleObjectMixin.queryset`.
+ .. attribute:: fields
+
+ .. versionadded:: 1.6
+
+ A list of names of fields. This is interpreted the same way as the
+ ``Meta.fields`` attribute of :class:`~django.forms.ModelForm`.
+
+ This is a required attribute if you are generating the form class
+ automatically (e.g. using ``model``). Omitting this attribute will
+ result in all fields being used, but this behaviour is deprecated
+ and will be removed in Django 1.8.
+
.. attribute:: success_url
The URL to redirect to when the form is successfully processed.
diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt
index 43f0398566..67e498ee91 100644
--- a/docs/ref/contrib/admin/index.txt
+++ b/docs/ref/contrib/admin/index.txt
@@ -337,6 +337,22 @@ subclass::
.. admonition:: Note
+ .. versionchanged:: 1.6
+
+ If you define the ``Meta.model`` attribute on a
+ :class:`~django.forms.ModelForm`, you must also define the
+ ``Meta.fields`` attribute (or the ``Meta.exclude`` attribute). However,
+ since the admin has its own way of defining fields, the ``Meta.fields``
+ attribute will be ignored.
+
+ If the ``ModelForm`` is only going to be used for the admin, the easiest
+ solution is to omit the ``Meta.model`` attribute, since ``ModelAdmin``
+ will provide the correct model to use. Alternatively, you can set
+ ``fields = []`` in the ``Meta`` class to satisfy the validation on the
+ ``ModelForm``.
+
+ .. admonition:: Note
+
If your ``ModelForm`` and ``ModelAdmin`` both define an ``exclude``
option then ``ModelAdmin`` takes precedence::
@@ -1283,13 +1299,24 @@ templates used by the :class:`ModelAdmin` views:
on the changelist page. To use a custom form, for example::
class MyForm(forms.ModelForm):
- class Meta:
- model = MyModel
+ pass
class MyModelAdmin(admin.ModelAdmin):
def get_changelist_form(self, request, **kwargs):
return MyForm
+ .. admonition:: Note
+
+ .. versionchanged:: 1.6
+
+ If you define the ``Meta.model`` attribute on a
+ :class:`~django.forms.ModelForm`, you must also define the
+ ``Meta.fields`` attribute (or the ``Meta.exclude`` attribute). However,
+ ``ModelAdmin`` ignores this value, overriding it with the
+ :attr:`ModelAdmin.list_editable` attribute. The easiest solution is to
+ omit the ``Meta.model`` attribute, since ``ModelAdmin`` will provide the
+ correct model to use.
+
.. method:: ModelAdmin.get_changelist_formset(self, request, **kwargs)
Returns a :ref:`ModelFormSet <model-formsets>` class for use on the
@@ -1490,9 +1517,6 @@ needed. Now within your form you can add your own custom validation for
any field::
class MyArticleAdminForm(forms.ModelForm):
- class Meta:
- model = Article
-
def clean_name(self):
# do something that validates your data
return self.cleaned_data["name"]
diff --git a/docs/ref/forms/models.txt b/docs/ref/forms/models.txt
index 7e3a1470b6..9b3480758a 100644
--- a/docs/ref/forms/models.txt
+++ b/docs/ref/forms/models.txt
@@ -25,6 +25,14 @@ Model Form Functions
See :ref:`modelforms-factory` for example usage.
+ .. versionchanged:: 1.6
+
+ You must provide the list of fields explicitly, either via keyword arguments
+ ``fields`` or ``exclude``, or the corresponding attributes on the form's
+ inner ``Meta`` class. See :ref:`modelforms-selecting-fields` for more
+ information. Omitting any definition of the fields to use will result in all
+ fields being used, but this behaviour is deprecated.
+
.. function:: modelformset_factory(model, form=ModelForm, formfield_callback=None, formset=BaseModelFormSet, extra=1, can_delete=False, can_order=False, max_num=None, fields=None, exclude=None, widgets=None, validate_max=False)
Returns a ``FormSet`` class for the given ``model`` class.
diff --git a/docs/releases/1.6.txt b/docs/releases/1.6.txt
index 611b661415..9cce36aac3 100644
--- a/docs/releases/1.6.txt
+++ b/docs/releases/1.6.txt
@@ -532,3 +532,55 @@ including it in an URLconf, simply replace::
with::
(r'^prefix/(?P<content_type_id>\d+)/(?P<object_id>.*)/$', 'django.contrib.contenttypes.views.shortcut'),
+
+``ModelForm`` without ``fields`` or ``exclude``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Previously, if you wanted a :class:`~django.forms.ModelForm` to use all fields on
+the model, you could simply omit the ``Meta.fields`` attribute, and all fields
+would be used.
+
+This can lead to security problems where fields are added to the model and,
+unintentionally, automatically become editable by end users. In some cases,
+particular with boolean fields, it is possible for this problem to be completely
+invisible. This is a form of `Mass assignment vulnerability
+<http://en.wikipedia.org/wiki/Mass_assignment_vulnerability>`_.
+
+For this reason, this behaviour is deprecated, and using the ``Meta.exclude``
+option is strongly discouraged. Instead, all fields that are intended for
+inclusion in the form should be listed explicitly in the ``fields`` attribute.
+
+If this security concern really does not apply in your case, there is a shortcut
+to explicitly indicate that all fields should be used - use the special value
+``"__all__"`` for the fields attribute::
+
+ class MyModelForm(ModelForm):
+ class Meta:
+ fields = "__all__"
+ model = MyModel
+
+If you have custom ``ModelForms`` that only need to be used in the admin, there
+is another option. The admin has its own methods for defining fields
+(``fieldsets`` etc.), and so adding a list of fields to the ``ModelForm`` is
+redundant. Instead, simply omit the ``Meta`` inner class of the ``ModelForm``,
+or omit the ``Meta.model`` attribute. Since the ``ModelAdmin`` subclass knows
+which model it is for, it can add the necessary attributes to derive a
+functioning ``ModelForm``. This behaviour also works for earlier Django
+versions.
+
+``UpdateView`` and ``CreateView`` without explicit fields
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The generic views :class:`~django.views.generic.edit.CreateView` and
+:class:`~django.views.generic.edit.UpdateView`, and anything else derived from
+:class:`~django.views.generic.edit.ModelFormMixin`, are vulnerable to the
+security problem described in the section above, because they can automatically
+create a ``ModelForm`` that uses all fields for a model.
+
+For this reason, if you use these views for editing models, you must also supply
+the ``fields`` attribute, which is a list of model fields and works in the same
+way as the :class:`~django.forms.ModelForm` ``Meta.fields`` attribute. Alternatively,
+you can set set the ``form_class`` attribute to a ``ModelForm`` that explicitly
+defines the fields to be used. Defining an ``UpdateView`` or ``CreateView``
+subclass to be used with a model but without an explicit list of fields is
+deprecated.
diff --git a/docs/topics/auth/customizing.txt b/docs/topics/auth/customizing.txt
index a5d7d3f9a1..b53bbe8211 100644
--- a/docs/topics/auth/customizing.txt
+++ b/docs/topics/auth/customizing.txt
@@ -1051,6 +1051,7 @@ code would be required in the app's ``admin.py`` file::
class Meta:
model = MyUser
+ fields = ['email', 'password', 'date_of_birth', 'is_active', 'is_admin']
def clean_password(self):
# Regardless of what the user provides, return the initial value.
diff --git a/docs/topics/class-based-views/generic-editing.txt b/docs/topics/class-based-views/generic-editing.txt
index 66ba36fd87..86c5280159 100644
--- a/docs/topics/class-based-views/generic-editing.txt
+++ b/docs/topics/class-based-views/generic-editing.txt
@@ -114,9 +114,11 @@ here; we don't have to write any logic ourselves::
class AuthorCreate(CreateView):
model = Author
+ fields = ['name']
class AuthorUpdate(UpdateView):
model = Author
+ fields = ['name']
class AuthorDelete(DeleteView):
model = Author
@@ -126,6 +128,17 @@ here; we don't have to write any logic ourselves::
We have to use :func:`~django.core.urlresolvers.reverse_lazy` here, not
just ``reverse`` as the urls are not loaded when the file is imported.
+.. versionchanged:: 1.6
+
+In Django 1.6, the ``fields`` attribute was added, which works the same way as
+the ``fields`` attribute on the inner ``Meta`` class on
+:class:`~django.forms.ModelForm`.
+
+Omitting the fields attribute will work as previously, but is deprecated and
+this attribute will be required from 1.8 (unless you define the form class in
+another way).
+
+
Finally, we hook these new views into the URLconf::
# urls.py
@@ -177,33 +190,17 @@ the foreign key relation to the model::
# ...
-Create a custom :class:`~django.forms.ModelForm` in order to exclude the
-``created_by`` field and prevent the user from editing it:
-
-.. code-block:: python
-
- # forms.py
- from django import forms
- from myapp.models import Author
-
- class AuthorForm(forms.ModelForm):
- class Meta:
- model = Author
- exclude = ('created_by',)
-
-In the view, use the custom
-:attr:`~django.views.generic.edit.FormMixin.form_class` and override
-:meth:`~django.views.generic.edit.ModelFormMixin.form_valid()` to add the
-user::
+In the view, ensure that you exclude ``created_by`` in the list of fields to
+edit, and override
+:meth:`~django.views.generic.edit.ModelFormMixin.form_valid()` to add the user::
# views.py
from django.views.generic.edit import CreateView
from myapp.models import Author
- from myapp.forms import AuthorForm
class AuthorCreate(CreateView):
- form_class = AuthorForm
model = Author
+ fields = ['name']
def form_valid(self, form):
form.instance.created_by = self.request.user
diff --git a/docs/topics/forms/modelforms.txt b/docs/topics/forms/modelforms.txt
index eaf2bbbaf2..e58dade736 100644
--- a/docs/topics/forms/modelforms.txt
+++ b/docs/topics/forms/modelforms.txt
@@ -28,6 +28,7 @@ For example::
>>> class ArticleForm(ModelForm):
... class Meta:
... model = Article
+ ... fields = ['pub_date', 'headline', 'content', 'reporter']
# Creating a form to add an article.
>>> form = ArticleForm()
@@ -39,11 +40,13 @@ For example::
Field types
-----------
-The generated ``Form`` class will have a form field for every model field. Each
-model field has a corresponding default form field. For example, a
-``CharField`` on a model is represented as a ``CharField`` on a form. A
-model ``ManyToManyField`` is represented as a ``MultipleChoiceField``. Here is
-the full list of conversions:
+The generated ``Form`` class will have a form field for every model field
+specified, in the order specified in the ``fields`` attribute.
+
+Each model field has a corresponding default form field. For example, a
+``CharField`` on a model is represented as a ``CharField`` on a form. A model
+``ManyToManyField`` is represented as a ``MultipleChoiceField``. Here is the
+full list of conversions:
=============================== ========================================
Model field Form field
@@ -168,10 +171,13 @@ Consider this set of models::
class AuthorForm(ModelForm):
class Meta:
model = Author
+ fields = ['name', 'title', 'birth_date']
class BookForm(ModelForm):
class Meta:
model = Book
+ fields = ['name', 'authors']
+
With these models, the ``ModelForm`` subclasses above would be roughly
equivalent to this (the only difference being the ``save()`` method, which
@@ -288,47 +294,66 @@ method is used to determine whether a form requires multipart file upload (and
hence whether ``request.FILES`` must be passed to the form), etc. See
:ref:`binding-uploaded-files` for more information.
-Using a subset of fields on the form
-------------------------------------
+.. _modelforms-selecting-fields:
-In some cases, you may not want all the model fields to appear on the generated
-form. There are three ways of telling ``ModelForm`` to use only a subset of the
-model fields:
+Selecting the fields to use
+---------------------------
-1. Set ``editable=False`` on the model field. As a result, *any* form
- created from the model via ``ModelForm`` will not include that
- field.
+It is strongly recommended that you explicitly set all fields that should be
+edited in the form using the ``fields`` attribute. Failure 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. Depending on how the
+form is rendered, the problem may not even be visible on the web page.
-2. Use the ``fields`` attribute of the ``ModelForm``'s inner ``Meta``
- class. This attribute, if given, should be a list of field names
- to include in the form. The order in which the fields names are specified
- in that list is respected when the form renders them.
+The alternative approach would be to include all fields automatically, or
+blacklist only some. This fundamental approach is known to be much less secure
+and has led to serious exploits on major websites (e.g. `GitHub
+<https://github.com/blog/1068-public-key-security-vulnerability-and-mitigation>`_).
-3. Use the ``exclude`` attribute of the ``ModelForm``'s inner ``Meta``
- class. This attribute, if given, should be a list of field names
- to exclude from the form.
+There are, however, two shortcuts available for cases where you can guarantee
+these security concerns do not apply to you:
-For example, if you want a form for the ``Author`` model (defined
-above) that includes only the ``name`` and ``birth_date`` fields, you would
-specify ``fields`` or ``exclude`` like this::
+1. Set the ``fields`` attribute to the special value ``'__all__'`` to indicate
+ that all fields in the model should be used. For example::
- class PartialAuthorForm(ModelForm):
- class Meta:
- model = Author
- fields = ('name', 'birth_date')
+ class AuthorForm(ModelForm):
+ class Meta:
+ model = Author
+ fields = '__all__'
- class PartialAuthorForm(ModelForm):
- class Meta:
- model = Author
- exclude = ('title',)
+2. Set the ``exclude`` attribute of the ``ModelForm``'s inner ``Meta`` class to
+ a list of fields to be excluded from the form.
+
+ For example::
+
+ class PartialAuthorForm(ModelForm):
+ class Meta:
+ model = Author
+ exclude = ['title']
+
+ Since the ``Author`` model has the 3 fields ``name``, ``title`` and
+ ``birth_date``, this will result in the fields ``name`` and ``birth_date``
+ being present on the form.
+
+If either of these are used, the order the fields appear in the form will be the
+order the fields are defined in the model, with ``ManyToManyField`` instances
+appearing last.
+
+In addition, Django applies the following rule: if you set ``editable=False`` on
+the model field, *any* form created from the model via ``ModelForm`` will not
+include that field.
+
+.. versionchanged:: 1.6
+
+ Before version 1.6, the ``'__all__'`` shortcut did not exist, but omitting
+ the ``fields`` attribute had the same effect. Omitting both ``fields`` and
+ ``exclude`` is now deprecated, but will continue to work as before until
+ version 1.8
-Since the Author model has only 3 fields, 'name', 'title', and
-'birth_date', the forms above will contain exactly the same fields.
.. note::
- If you specify ``fields`` or ``exclude`` when creating a form with
- ``ModelForm``, then the fields that are not in the resulting form
+ Any fields not included in a form by the above logic
will not be set by the form's ``save()`` method. Also, if you
manually add the excluded fields back to the form, they will not
be initialized from the model instance.
@@ -401,15 +426,19 @@ field, you could do the following::
class Meta:
model = Article
+ fields = ['pub_date', 'headline', 'content', 'reporter']
+
If you want to override a field's default label, then specify the ``label``
parameter when declaring the form field::
- >>> class ArticleForm(ModelForm):
- ... pub_date = DateField(label='Publication date')
- ...
- ... class Meta:
- ... model = Article
+ class ArticleForm(ModelForm):
+ pub_date = DateField(label='Publication date')
+
+ class Meta:
+ model = Article
+ fields = ['pub_date', 'headline', 'content', 'reporter']
+
.. note::
@@ -436,6 +465,7 @@ parameter when declaring the form field::
class Meta:
model = Article
+ fields = ['headline', 'content']
You must ensure that the type of the form field can be used to set the
contents of the corresponding model field. When they are not compatible,
@@ -444,30 +474,6 @@ parameter when declaring the form field::
See the :doc:`form field documentation </ref/forms/fields>` for more information
on fields and their arguments.
-Changing the order of fields
-----------------------------
-
-By default, a ``ModelForm`` will render fields in the same order that they are
-defined on the model, with ``ManyToManyField`` instances appearing last. If
-you want to change the order in which fields are rendered, you can use the
-``fields`` attribute on the ``Meta`` class.
-
-The ``fields`` attribute defines the subset of model fields that will be
-rendered, and the order in which they will be rendered. For example given this
-model::
-
- class Book(models.Model):
- author = models.ForeignKey(Author)
- title = models.CharField(max_length=100)
-
-the ``author`` field would be rendered first. If we wanted the title field
-to be rendered first, we could specify the following ``ModelForm``::
-
- >>> class BookForm(ModelForm):
- ... class Meta:
- ... model = Book
- ... fields = ('title', 'author')
-
.. _overriding-modelform-clean-method:
Overriding the clean() method
@@ -550,21 +556,19 @@ definition. This may be more convenient if you do not have many customizations
to make::
>>> from django.forms.models import modelform_factory
- >>> BookForm = modelform_factory(Book)
+ >>> BookForm = modelform_factory(Book, fields=("author", "title"))
This can also be used to make simple modifications to existing forms, for
-example by specifying which fields should be displayed::
-
- >>> Form = modelform_factory(Book, form=BookForm, fields=("author",))
-
-... or which fields should be excluded::
-
- >>> Form = modelform_factory(Book, form=BookForm, exclude=("title",))
-
-You can also specify the widgets to be used for a given field::
+example by specifying the widgets to be used for a given field::
>>> from django.forms import Textarea
- >>> Form = modelform_factory(Book, form=BookForm, widgets={"title": Textarea()})
+ >>> Form = modelform_factory(Book, form=BookForm,
+ widgets={"title": Textarea()})
+
+The fields to include can be specified using the ``fields`` and ``exclude``
+keyword arguments, or the corresponding attributes on the ``ModelForm`` inner
+``Meta`` class. Please see the ``ModelForm`` :ref:`modelforms-selecting-fields`
+documentation.
.. _model-formsets:
@@ -688,11 +692,10 @@ database. If a given instance's data didn't change in the bound data, the
instance won't be saved to the database and won't be included in the return
value (``instances``, in the above example).
-When fields are missing from the form (for example because they have
-been excluded), these fields will not be set by the ``save()``
-method. You can find more information about this restriction, which
-also holds for regular ``ModelForms``, in `Using a subset of fields on
-the form`_.
+When fields are missing from the form (for example because they have been
+excluded), these fields will not be set by the ``save()`` method. You can find
+more information about this restriction, which also holds for regular
+``ModelForms``, in `Selecting the fields to use`_.
Pass ``commit=False`` to return the unsaved model instances::