mirror of
https://github.com/django/django.git
synced 2025-08-03 10:34:04 +00:00
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.
This commit is contained in:
parent
1e37cb37ce
commit
f026a519ae
34 changed files with 578 additions and 201 deletions
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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::
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue