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:
Luke Plant 2013-02-21 21:56:55 +00:00
parent 1e37cb37ce
commit f026a519ae
34 changed files with 578 additions and 201 deletions

View File

@ -5,7 +5,7 @@ from django import forms
from django.conf import settings from django.conf import settings
from django.forms.formsets import all_valid, DELETION_FIELD_NAME from django.forms.formsets import all_valid, DELETION_FIELD_NAME
from django.forms.models import (modelform_factory, modelformset_factory, from django.forms.models import (modelform_factory, modelformset_factory,
inlineformset_factory, BaseInlineFormSet) inlineformset_factory, BaseInlineFormSet, modelform_defines_fields)
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.contrib.admin import widgets, helpers from django.contrib.admin import widgets, helpers
from django.contrib.admin.util import (unquote, flatten_fieldsets, get_deleted_objects, from django.contrib.admin.util import (unquote, flatten_fieldsets, get_deleted_objects,
@ -488,6 +488,10 @@ class ModelAdmin(BaseModelAdmin):
"formfield_callback": partial(self.formfield_for_dbfield, request=request), "formfield_callback": partial(self.formfield_for_dbfield, request=request),
} }
defaults.update(kwargs) defaults.update(kwargs)
if defaults['fields'] is None and not modelform_defines_fields(defaults['form']):
defaults['fields'] = forms.ALL_FIELDS
try: try:
return modelform_factory(self.model, **defaults) return modelform_factory(self.model, **defaults)
except FieldError as e: except FieldError as e:
@ -523,6 +527,10 @@ class ModelAdmin(BaseModelAdmin):
"formfield_callback": partial(self.formfield_for_dbfield, request=request), "formfield_callback": partial(self.formfield_for_dbfield, request=request),
} }
defaults.update(kwargs) defaults.update(kwargs)
if (defaults.get('fields') is None
and not modelform_defines_fields(defaults.get('form'))):
defaults['fields'] = forms.ALL_FIELDS
return modelform_factory(self.model, **defaults) return modelform_factory(self.model, **defaults)
def get_changelist_formset(self, request, **kwargs): def get_changelist_formset(self, request, **kwargs):
@ -1527,6 +1535,10 @@ class InlineModelAdmin(BaseModelAdmin):
return result return result
defaults['form'] = DeleteProtectedModelForm defaults['form'] = DeleteProtectedModelForm
if defaults['fields'] is None and not modelform_defines_fields(defaults['form']):
defaults['fields'] = forms.ALL_FIELDS
return inlineformset_factory(self.parent_model, self.model, **defaults) return inlineformset_factory(self.parent_model, self.model, **defaults)
def get_fieldsets(self, request, obj=None): def get_fieldsets(self, request, obj=None):

View File

@ -130,6 +130,7 @@ class UserChangeForm(forms.ModelForm):
class Meta: class Meta:
model = User model = User
fields = '__all__'
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(UserChangeForm, self).__init__(*args, **kwargs) super(UserChangeForm, self).__init__(*args, **kwargs)

View File

@ -13,8 +13,9 @@ from django.db.models import signals
from django.db.models.fields.related import ForeignObject, ForeignObjectRel from django.db.models.fields.related import ForeignObject, ForeignObjectRel
from django.db.models.related import PathInfo from django.db.models.related import PathInfo
from django.db.models.sql.where import Constraint from django.db.models.sql.where import Constraint
from django.forms import ModelForm from django.forms import ModelForm, ALL_FIELDS
from django.forms.models import BaseModelFormSet, modelformset_factory, save_instance from django.forms.models import (BaseModelFormSet, modelformset_factory, save_instance,
modelform_defines_fields)
from django.contrib.admin.options import InlineModelAdmin, flatten_fieldsets from django.contrib.admin.options import InlineModelAdmin, flatten_fieldsets
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.utils import six from django.utils import six
@ -480,6 +481,10 @@ class GenericInlineModelAdmin(InlineModelAdmin):
"exclude": exclude "exclude": exclude
} }
defaults.update(kwargs) defaults.update(kwargs)
if defaults['fields'] is None and not modelform_defines_fields(defaults['form']):
defaults['fields'] = ALL_FIELDS
return generic_inlineformset_factory(self.model, **defaults) return generic_inlineformset_factory(self.model, **defaults)
class GenericStackedInline(GenericInlineModelAdmin): class GenericStackedInline(GenericInlineModelAdmin):

View File

@ -12,6 +12,7 @@ class FlatpageForm(forms.ModelForm):
class Meta: class Meta:
model = FlatPage model = FlatPage
fields = '__all__'
def clean_url(self): def clean_url(self):
url = self.cleaned_data['url'] url = self.cleaned_data['url']

View File

@ -60,9 +60,11 @@ class TestModel(models.Model):
class TestModelForm(forms.ModelForm): class TestModelForm(forms.ModelForm):
class Meta: class Meta:
model = TestModel model = TestModel
fields = '__all__'
TestModelFormSet = forms.models.modelformset_factory(TestModel, form=TestModelForm, extra=2) TestModelFormSet = forms.models.modelformset_factory(TestModel, form=TestModelForm, extra=2,
fields='__all__')
class TestWizard(WizardView): class TestWizard(WizardView):

View File

@ -15,6 +15,7 @@ from django.utils._os import upath
class UserForm(forms.ModelForm): class UserForm(forms.ModelForm):
class Meta: class Meta:
model = User model = User
fields = '__all__'
UserFormSet = forms.models.modelformset_factory(User, form=UserForm, extra=2) UserFormSet = forms.models.modelformset_factory(User, form=UserForm, extra=2)

View File

@ -5,6 +5,8 @@ and database field objects.
from __future__ import absolute_import, unicode_literals from __future__ import absolute_import, unicode_literals
import warnings
from django.core.exceptions import ValidationError, NON_FIELD_ERRORS, FieldError from django.core.exceptions import ValidationError, NON_FIELD_ERRORS, FieldError
from django.forms.fields import Field, ChoiceField from django.forms.fields import Field, ChoiceField
from django.forms.forms import BaseForm, get_declared_fields from django.forms.forms import BaseForm, get_declared_fields
@ -22,8 +24,12 @@ from django.utils.translation import ugettext_lazy as _, ugettext
__all__ = ( __all__ = (
'ModelForm', 'BaseModelForm', 'model_to_dict', 'fields_for_model', 'ModelForm', 'BaseModelForm', 'model_to_dict', 'fields_for_model',
'save_instance', 'ModelChoiceField', 'ModelMultipleChoiceField', 'save_instance', 'ModelChoiceField', 'ModelMultipleChoiceField',
'ALL_FIELDS',
) )
ALL_FIELDS = '__all__'
def construct_instance(form, instance, fields=None, exclude=None): def construct_instance(form, instance, fields=None, exclude=None):
""" """
Constructs and returns a model instance from the bound ``form``'s Constructs and returns a model instance from the bound ``form``'s
@ -211,7 +217,7 @@ class ModelFormMetaclass(type):
# of ('foo',) # of ('foo',)
for opt in ['fields', 'exclude']: for opt in ['fields', 'exclude']:
value = getattr(opts, opt) value = getattr(opts, opt)
if isinstance(value, six.string_types): if isinstance(value, six.string_types) and value != ALL_FIELDS:
msg = ("%(model)s.Meta.%(opt)s cannot be a string. " msg = ("%(model)s.Meta.%(opt)s cannot be a string. "
"Did you mean to type: ('%(value)s',)?" % { "Did you mean to type: ('%(value)s',)?" % {
'model': new_class.__name__, 'model': new_class.__name__,
@ -222,6 +228,20 @@ class ModelFormMetaclass(type):
if opts.model: if opts.model:
# If a model is defined, extract form fields from it. # If a model is defined, extract form fields from it.
if opts.fields is None and opts.exclude is None:
# This should be some kind of assertion error once deprecation
# cycle is complete.
warnings.warn("Creating a ModelForm without either the 'fields' attribute "
"or the 'exclude' attribute is deprecated - form %s "
"needs updating" % name,
PendingDeprecationWarning)
if opts.fields == ALL_FIELDS:
# sentinel for fields_for_model to indicate "get the list of
# fields from the model"
opts.fields = None
fields = fields_for_model(opts.model, opts.fields, fields = fields_for_model(opts.model, opts.fields,
opts.exclude, opts.widgets, formfield_callback) opts.exclude, opts.widgets, formfield_callback)
# make sure opts.fields doesn't specify an invalid field # make sure opts.fields doesn't specify an invalid field
@ -394,7 +414,8 @@ def modelform_factory(model, form=ModelForm, fields=None, exclude=None,
Returns a ModelForm containing form fields for the given model. Returns a ModelForm containing form fields for the given model.
``fields`` is an optional list of field names. If provided, only the named ``fields`` is an optional list of field names. If provided, only the named
fields will be included in the returned fields. fields will be included in the returned fields. If omitted or '__all__',
all fields will be used.
``exclude`` is an optional list of field names. If provided, the named ``exclude`` is an optional list of field names. If provided, the named
fields will be excluded from the returned fields, even if they are listed fields will be excluded from the returned fields, even if they are listed
@ -434,6 +455,15 @@ def modelform_factory(model, form=ModelForm, fields=None, exclude=None,
'formfield_callback': formfield_callback 'formfield_callback': formfield_callback
} }
# The ModelFormMetaclass will trigger a similar warning/error, but this will
# be difficult to debug for code that needs updating, so we produce the
# warning here too.
if (getattr(Meta, 'fields', None) is None and
getattr(Meta, 'exclude', None) is None):
warnings.warn("Calling modelform_factory without defining 'fields' or "
"'exclude' explicitly is deprecated",
PendingDeprecationWarning, stacklevel=2)
# Instatiate type(form) in order to use the same metaclass as form. # Instatiate type(form) in order to use the same metaclass as form.
return type(form)(class_name, (form,), form_class_attrs) return type(form)(class_name, (form,), form_class_attrs)
@ -701,6 +731,21 @@ def modelformset_factory(model, form=ModelForm, formfield_callback=None,
""" """
Returns a FormSet class for the given Django model class. Returns a FormSet class for the given Django model class.
""" """
# modelform_factory will produce the same warning/error, but that will be
# difficult to debug for code that needs upgrading, so we produce the
# warning here too. This logic is reproducing logic inside
# modelform_factory, but it can be removed once the deprecation cycle is
# complete, since the validation exception will produce a helpful
# stacktrace.
meta = getattr(form, 'Meta', None)
if meta is None:
meta = type(str('Meta'), (object,), {})
if (getattr(meta, 'fields', fields) is None and
getattr(meta, 'exclude', exclude) is None):
warnings.warn("Calling modelformset_factory without defining 'fields' or "
"'exclude' explicitly is deprecated",
PendingDeprecationWarning, stacklevel=2)
form = modelform_factory(model, form=form, fields=fields, exclude=exclude, form = modelform_factory(model, form=form, fields=fields, exclude=exclude,
formfield_callback=formfield_callback, formfield_callback=formfield_callback,
widgets=widgets) widgets=widgets)
@ -1091,3 +1136,11 @@ class ModelMultipleChoiceField(ModelChoiceField):
initial_set = set([force_text(value) for value in self.prepare_value(initial)]) initial_set = set([force_text(value) for value in self.prepare_value(initial)])
data_set = set([force_text(value) for value in data]) data_set = set([force_text(value) for value in data])
return data_set != initial_set return data_set != initial_set
def modelform_defines_fields(form_class):
return (form_class is not None and (
hasattr(form_class, '_meta') and
(form_class._meta.fields is not None or
form_class._meta.exclude is not None)
))

View File

@ -1,3 +1,5 @@
import warnings
from django.forms import models as model_forms from django.forms import models as model_forms
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
@ -95,7 +97,14 @@ class ModelFormMixin(FormMixin, SingleObjectMixin):
# Try to get a queryset and extract the model class # Try to get a queryset and extract the model class
# from that # from that
model = self.get_queryset().model model = self.get_queryset().model
return model_forms.modelform_factory(model)
fields = getattr(self, 'fields', None)
if fields is None:
warnings.warn("Using ModelFormMixin (base class of %s) without "
"the 'fields' attribute is deprecated." % self.__class__.__name__,
PendingDeprecationWarning)
return model_forms.modelform_factory(model, fields=fields)
def get_form_kwargs(self): def get_form_kwargs(self):
""" """

View File

@ -110,6 +110,7 @@ CreateView
class AuthorCreate(CreateView): class AuthorCreate(CreateView):
model = Author model = Author
fields = ['name']
UpdateView UpdateView
---------- ----------
@ -152,6 +153,7 @@ UpdateView
class AuthorUpdate(UpdateView): class AuthorUpdate(UpdateView):
model = Author model = Author
fields = ['name']
DeleteView DeleteView
---------- ----------

View File

@ -116,6 +116,18 @@ ModelFormMixin
by examining ``self.object`` or by examining ``self.object`` or
:attr:`~django.views.generic.detail.SingleObjectMixin.queryset`. :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 .. attribute:: success_url
The URL to redirect to when the form is successfully processed. The URL to redirect to when the form is successfully processed.

View File

@ -335,6 +335,22 @@ subclass::
For an example see the section `Adding custom validation to the admin`_. For an example see the section `Adding custom validation to the admin`_.
.. 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 .. admonition:: Note
If your ``ModelForm`` and ``ModelAdmin`` both define an ``exclude`` If your ``ModelForm`` and ``ModelAdmin`` both define an ``exclude``
@ -1283,13 +1299,24 @@ templates used by the :class:`ModelAdmin` views:
on the changelist page. To use a custom form, for example:: on the changelist page. To use a custom form, for example::
class MyForm(forms.ModelForm): class MyForm(forms.ModelForm):
class Meta: pass
model = MyModel
class MyModelAdmin(admin.ModelAdmin): class MyModelAdmin(admin.ModelAdmin):
def get_changelist_form(self, request, **kwargs): def get_changelist_form(self, request, **kwargs):
return MyForm 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) .. method:: ModelAdmin.get_changelist_formset(self, request, **kwargs)
Returns a :ref:`ModelFormSet <model-formsets>` class for use on the 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:: any field::
class MyArticleAdminForm(forms.ModelForm): class MyArticleAdminForm(forms.ModelForm):
class Meta:
model = Article
def clean_name(self): def clean_name(self):
# do something that validates your data # do something that validates your data
return self.cleaned_data["name"] return self.cleaned_data["name"]

View File

@ -25,6 +25,14 @@ Model Form Functions
See :ref:`modelforms-factory` for example usage. 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) .. 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. Returns a ``FormSet`` class for the given ``model`` class.

View File

@ -532,3 +532,55 @@ including it in an URLconf, simply replace::
with:: with::
(r'^prefix/(?P<content_type_id>\d+)/(?P<object_id>.*)/$', 'django.contrib.contenttypes.views.shortcut'), (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.

View File

@ -1051,6 +1051,7 @@ code would be required in the app's ``admin.py`` file::
class Meta: class Meta:
model = MyUser model = MyUser
fields = ['email', 'password', 'date_of_birth', 'is_active', 'is_admin']
def clean_password(self): def clean_password(self):
# Regardless of what the user provides, return the initial value. # Regardless of what the user provides, return the initial value.

View File

@ -114,9 +114,11 @@ here; we don't have to write any logic ourselves::
class AuthorCreate(CreateView): class AuthorCreate(CreateView):
model = Author model = Author
fields = ['name']
class AuthorUpdate(UpdateView): class AuthorUpdate(UpdateView):
model = Author model = Author
fields = ['name']
class AuthorDelete(DeleteView): class AuthorDelete(DeleteView):
model = Author 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 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. 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:: Finally, we hook these new views into the URLconf::
# urls.py # 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 In the view, ensure that you exclude ``created_by`` in the list of fields to
``created_by`` field and prevent the user from editing it: edit, and override
:meth:`~django.views.generic.edit.ModelFormMixin.form_valid()` to add the user::
.. 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::
# views.py # views.py
from django.views.generic.edit import CreateView from django.views.generic.edit import CreateView
from myapp.models import Author from myapp.models import Author
from myapp.forms import AuthorForm
class AuthorCreate(CreateView): class AuthorCreate(CreateView):
form_class = AuthorForm
model = Author model = Author
fields = ['name']
def form_valid(self, form): def form_valid(self, form):
form.instance.created_by = self.request.user form.instance.created_by = self.request.user

View File

@ -28,6 +28,7 @@ For example::
>>> class ArticleForm(ModelForm): >>> class ArticleForm(ModelForm):
... class Meta: ... class Meta:
... model = Article ... model = Article
... fields = ['pub_date', 'headline', 'content', 'reporter']
# Creating a form to add an article. # Creating a form to add an article.
>>> form = ArticleForm() >>> form = ArticleForm()
@ -39,11 +40,13 @@ For example::
Field types Field types
----------- -----------
The generated ``Form`` class will have a form field for every model field. Each The generated ``Form`` class will have a form field for every model field
model field has a corresponding default form field. For example, a specified, in the order specified in the ``fields`` attribute.
``CharField`` on a model is represented as a ``CharField`` on a form. A
model ``ManyToManyField`` is represented as a ``MultipleChoiceField``. Here is Each model field has a corresponding default form field. For example, a
the full list of conversions: ``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 Model field Form field
@ -168,10 +171,13 @@ Consider this set of models::
class AuthorForm(ModelForm): class AuthorForm(ModelForm):
class Meta: class Meta:
model = Author model = Author
fields = ['name', 'title', 'birth_date']
class BookForm(ModelForm): class BookForm(ModelForm):
class Meta: class Meta:
model = Book model = Book
fields = ['name', 'authors']
With these models, the ``ModelForm`` subclasses above would be roughly With these models, the ``ModelForm`` subclasses above would be roughly
equivalent to this (the only difference being the ``save()`` method, which 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 hence whether ``request.FILES`` must be passed to the form), etc. See
:ref:`binding-uploaded-files` for more information. :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 Selecting the fields to use
form. There are three ways of telling ``ModelForm`` to use only a subset of the ---------------------------
model fields:
1. Set ``editable=False`` on the model field. As a result, *any* form It is strongly recommended that you explicitly set all fields that should be
created from the model via ``ModelForm`` will not include that edited in the form using the ``fields`` attribute. Failure to do so can easily
field. 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`` The alternative approach would be to include all fields automatically, or
class. This attribute, if given, should be a list of field names blacklist only some. This fundamental approach is known to be much less secure
to include in the form. The order in which the fields names are specified and has led to serious exploits on major websites (e.g. `GitHub
in that list is respected when the form renders them. <https://github.com/blog/1068-public-key-security-vulnerability-and-mitigation>`_).
3. Use the ``exclude`` attribute of the ``ModelForm``'s inner ``Meta`` There are, however, two shortcuts available for cases where you can guarantee
class. This attribute, if given, should be a list of field names these security concerns do not apply to you:
to exclude from the form.
For example, if you want a form for the ``Author`` model (defined 1. Set the ``fields`` attribute to the special value ``'__all__'`` to indicate
above) that includes only the ``name`` and ``birth_date`` fields, you would that all fields in the model should be used. For example::
specify ``fields`` or ``exclude`` like this::
class PartialAuthorForm(ModelForm): class AuthorForm(ModelForm):
class Meta: class Meta:
model = Author model = Author
fields = ('name', 'birth_date') fields = '__all__'
class PartialAuthorForm(ModelForm): 2. Set the ``exclude`` attribute of the ``ModelForm``'s inner ``Meta`` class to
class Meta: a list of fields to be excluded from the form.
model = Author
exclude = ('title',) 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:: .. note::
If you specify ``fields`` or ``exclude`` when creating a form with Any fields not included in a form by the above logic
``ModelForm``, then the fields that are not in the resulting form
will not be set by the form's ``save()`` method. Also, if you 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 manually add the excluded fields back to the form, they will not
be initialized from the model instance. be initialized from the model instance.
@ -401,15 +426,19 @@ field, you could do the following::
class Meta: class Meta:
model = Article model = Article
fields = ['pub_date', 'headline', 'content', 'reporter']
If you want to override a field's default label, then specify the ``label`` If you want to override a field's default label, then specify the ``label``
parameter when declaring the form field:: parameter when declaring the form field::
>>> class ArticleForm(ModelForm): class ArticleForm(ModelForm):
... pub_date = DateField(label='Publication date') pub_date = DateField(label='Publication date')
...
... class Meta: class Meta:
... model = Article model = Article
fields = ['pub_date', 'headline', 'content', 'reporter']
.. note:: .. note::
@ -436,6 +465,7 @@ parameter when declaring the form field::
class Meta: class Meta:
model = Article model = Article
fields = ['headline', 'content']
You must ensure that the type of the form field can be used to set the 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, 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 See the :doc:`form field documentation </ref/forms/fields>` for more information
on fields and their arguments. 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-modelform-clean-method:
Overriding the 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:: to make::
>>> from django.forms.models import modelform_factory >>> 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 This can also be used to make simple modifications to existing forms, for
example by specifying which fields should be displayed:: example by specifying the widgets to be used for a given field::
>>> 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::
>>> from django.forms import Textarea >>> 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: .. _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 instance won't be saved to the database and won't be included in the return
value (``instances``, in the above example). value (``instances``, in the above example).
When fields are missing from the form (for example because they have When fields are missing from the form (for example because they have been
been excluded), these fields will not be set by the ``save()`` excluded), these fields will not be set by the ``save()`` method. You can find
method. You can find more information about this restriction, which more information about this restriction, which also holds for regular
also holds for regular ``ModelForms``, in `Using a subset of fields on ``ModelForms``, in `Selecting the fields to use`_.
the form`_.
Pass ``commit=False`` to return the unsaved model instances:: Pass ``commit=False`` to return the unsaved model instances::

View File

@ -285,6 +285,8 @@ class ValidationTestCase(TestCase):
extra_data = forms.CharField() extra_data = forms.CharField()
class Meta: class Meta:
model = Song model = Song
fields = '__all__'
class FieldsOnFormOnlyAdmin(admin.ModelAdmin): class FieldsOnFormOnlyAdmin(admin.ModelAdmin):
form = SongForm form = SongForm

View File

@ -25,3 +25,4 @@ class Photo(models.Model):
class PhotoForm(ModelForm): class PhotoForm(ModelForm):
class Meta: class Meta:
model = Photo model = Photo
fields = '__all__'

View File

@ -322,6 +322,7 @@ class FormsTests(TestCase):
class ArticleForm(forms.ModelForm): class ArticleForm(forms.ModelForm):
class Meta: class Meta:
model = Article model = Article
fields = '__all__'
def test_foreign_object_form(self): def test_foreign_object_form(self):
# A very crude test checking that the non-concrete fields do not get form fields. # A very crude test checking that the non-concrete fields do not get form fields.

View File

@ -139,6 +139,7 @@ class FormsRegressionsTestCase(TestCase):
class CheeseForm(ModelForm): class CheeseForm(ModelForm):
class Meta: class Meta:
model = Cheese model = Cheese
fields = '__all__'
form = CheeseForm({ form = CheeseForm({
'name': 'Brie', 'name': 'Brie',

View File

@ -17,11 +17,13 @@ from ..models import (ChoiceOptionModel, ChoiceFieldModel, FileModel, Group,
class ChoiceFieldForm(ModelForm): class ChoiceFieldForm(ModelForm):
class Meta: class Meta:
model = ChoiceFieldModel model = ChoiceFieldModel
fields = '__all__'
class OptionalMultiChoiceModelForm(ModelForm): class OptionalMultiChoiceModelForm(ModelForm):
class Meta: class Meta:
model = OptionalMultiChoiceModel model = OptionalMultiChoiceModel
fields = '__all__'
class FileForm(Form): class FileForm(Form):
@ -139,6 +141,7 @@ class FormsModelTestCase(TestCase):
class BoundaryForm(ModelForm): class BoundaryForm(ModelForm):
class Meta: class Meta:
model = BoundaryModel model = BoundaryModel
fields = '__all__'
f = BoundaryForm({'positive_integer': 100}) f = BoundaryForm({'positive_integer': 100})
self.assertTrue(f.is_valid()) self.assertTrue(f.is_valid())
@ -154,6 +157,7 @@ class FormsModelTestCase(TestCase):
class DefaultsForm(ModelForm): class DefaultsForm(ModelForm):
class Meta: class Meta:
model = Defaults model = Defaults
fields = '__all__'
self.assertEqual(DefaultsForm().fields['name'].initial, 'class default value') self.assertEqual(DefaultsForm().fields['name'].initial, 'class default value')
self.assertEqual(DefaultsForm().fields['def_date'].initial, datetime.date(1980, 1, 1)) self.assertEqual(DefaultsForm().fields['def_date'].initial, datetime.date(1980, 1, 1))

View File

@ -247,6 +247,7 @@ class CustomWidget(forms.TextInput):
class TaggedItemForm(forms.ModelForm): class TaggedItemForm(forms.ModelForm):
class Meta: class Meta:
model = TaggedItem model = TaggedItem
fields = '__all__'
widgets = {'tag': CustomWidget} widgets = {'tag': CustomWidget}
class GenericInlineFormsetTest(TestCase): class GenericInlineFormsetTest(TestCase):

View File

@ -1,12 +1,14 @@
from __future__ import absolute_import from __future__ import absolute_import
import warnings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django import forms from django import forms
from django.test import TestCase from django.test import TestCase
from django.utils.unittest import expectedFailure from django.utils.unittest import expectedFailure
from django.views.generic.base import View from django.views.generic.base import View
from django.views.generic.edit import FormMixin from django.views.generic.edit import FormMixin, CreateView, UpdateView
from . import views from . import views
from .models import Artist, Author from .models import Artist, Author
@ -34,6 +36,7 @@ class ModelFormMixinTests(TestCase):
form_class = views.AuthorGetQuerySetFormView().get_form_class() form_class = views.AuthorGetQuerySetFormView().get_form_class()
self.assertEqual(form_class._meta.model, Author) self.assertEqual(form_class._meta.model, Author)
class CreateViewTests(TestCase): class CreateViewTests(TestCase):
urls = 'generic_views.urls' urls = 'generic_views.urls'
@ -112,6 +115,45 @@ class CreateViewTests(TestCase):
self.assertEqual(res.status_code, 302) self.assertEqual(res.status_code, 302)
self.assertRedirects(res, 'http://testserver/accounts/login/?next=/edit/authors/create/restricted/') self.assertRedirects(res, 'http://testserver/accounts/login/?next=/edit/authors/create/restricted/')
def test_create_view_with_restricted_fields(self):
class MyCreateView(CreateView):
model = Author
fields = ['name']
self.assertEqual(list(MyCreateView().get_form_class().base_fields),
['name'])
def test_create_view_all_fields(self):
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always", PendingDeprecationWarning)
class MyCreateView(CreateView):
model = Author
fields = '__all__'
self.assertEqual(list(MyCreateView().get_form_class().base_fields),
['name', 'slug'])
self.assertEqual(len(w), 0)
def test_create_view_without_explicit_fields(self):
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always", PendingDeprecationWarning)
class MyCreateView(CreateView):
model = Author
# Until end of the deprecation cycle, should still create the form
# as before:
self.assertEqual(list(MyCreateView().get_form_class().base_fields),
['name', 'slug'])
# but with a warning:
self.assertEqual(w[0].category, PendingDeprecationWarning)
class UpdateViewTests(TestCase): class UpdateViewTests(TestCase):
urls = 'generic_views.urls' urls = 'generic_views.urls'

View File

@ -11,6 +11,7 @@ class AuthorForm(forms.ModelForm):
class Meta: class Meta:
model = Author model = Author
fields = ['name', 'slug']
class ContactForm(forms.Form): class ContactForm(forms.Form):

View File

@ -85,15 +85,18 @@ class ContactView(generic.FormView):
class ArtistCreate(generic.CreateView): class ArtistCreate(generic.CreateView):
model = Artist model = Artist
fields = '__all__'
class NaiveAuthorCreate(generic.CreateView): class NaiveAuthorCreate(generic.CreateView):
queryset = Author.objects.all() queryset = Author.objects.all()
fields = '__all__'
class AuthorCreate(generic.CreateView): class AuthorCreate(generic.CreateView):
model = Author model = Author
success_url = '/list/authors/' success_url = '/list/authors/'
fields = '__all__'
class SpecializedAuthorCreate(generic.CreateView): class SpecializedAuthorCreate(generic.CreateView):
@ -112,19 +115,23 @@ class AuthorCreateRestricted(AuthorCreate):
class ArtistUpdate(generic.UpdateView): class ArtistUpdate(generic.UpdateView):
model = Artist model = Artist
fields = '__all__'
class NaiveAuthorUpdate(generic.UpdateView): class NaiveAuthorUpdate(generic.UpdateView):
queryset = Author.objects.all() queryset = Author.objects.all()
fields = '__all__'
class AuthorUpdate(generic.UpdateView): class AuthorUpdate(generic.UpdateView):
model = Author model = Author
success_url = '/list/authors/' success_url = '/list/authors/'
fields = '__all__'
class OneAuthorUpdate(generic.UpdateView): class OneAuthorUpdate(generic.UpdateView):
success_url = '/list/authors/' success_url = '/list/authors/'
fields = '__all__'
def get_object(self): def get_object(self):
return Author.objects.get(pk=1) return Author.objects.get(pk=1)
@ -184,6 +191,8 @@ class BookDetail(BookConfig, generic.DateDetailView):
pass pass
class AuthorGetQuerySetFormView(generic.edit.ModelFormMixin): class AuthorGetQuerySetFormView(generic.edit.ModelFormMixin):
fields = '__all__'
def get_queryset(self): def get_queryset(self):
return Author.objects.all() return Author.objects.all()

View File

@ -24,3 +24,4 @@ class CompanyForm(forms.ModelForm):
class Meta: class Meta:
model = Company model = Company
fields = '__all__'

View File

@ -10,7 +10,7 @@ from .models import Poet, Poem, School, Parent, Child
class DeletionTests(TestCase): class DeletionTests(TestCase):
def test_deletion(self): def test_deletion(self):
PoemFormSet = inlineformset_factory(Poet, Poem, can_delete=True) PoemFormSet = inlineformset_factory(Poet, Poem, can_delete=True, fields="__all__")
poet = Poet.objects.create(name='test') poet = Poet.objects.create(name='test')
poem = poet.poem_set.create(name='test poem') poem = poet.poem_set.create(name='test poem')
data = { data = {
@ -32,7 +32,7 @@ class DeletionTests(TestCase):
Make sure that an add form that is filled out, but marked for deletion Make sure that an add form that is filled out, but marked for deletion
doesn't cause validation errors. doesn't cause validation errors.
""" """
PoemFormSet = inlineformset_factory(Poet, Poem, can_delete=True) PoemFormSet = inlineformset_factory(Poet, Poem, can_delete=True, fields="__all__")
poet = Poet.objects.create(name='test') poet = Poet.objects.create(name='test')
data = { data = {
'poem_set-TOTAL_FORMS': '1', 'poem_set-TOTAL_FORMS': '1',
@ -60,7 +60,7 @@ class DeletionTests(TestCase):
Make sure that a change form that is filled out, but marked for deletion Make sure that a change form that is filled out, but marked for deletion
doesn't cause validation errors. doesn't cause validation errors.
""" """
PoemFormSet = inlineformset_factory(Poet, Poem, can_delete=True) PoemFormSet = inlineformset_factory(Poet, Poem, can_delete=True, fields="__all__")
poet = Poet.objects.create(name='test') poet = Poet.objects.create(name='test')
poem = poet.poem_set.create(name='test poem') poem = poet.poem_set.create(name='test poem')
data = { data = {
@ -115,8 +115,8 @@ class InlineFormsetFactoryTest(TestCase):
""" """
These should both work without a problem. These should both work without a problem.
""" """
inlineformset_factory(Parent, Child, fk_name='mother') inlineformset_factory(Parent, Child, fk_name='mother', fields="__all__")
inlineformset_factory(Parent, Child, fk_name='father') inlineformset_factory(Parent, Child, fk_name='father', fields="__all__")
def test_exception_on_unspecified_foreign_key(self): def test_exception_on_unspecified_foreign_key(self):
""" """

View File

@ -3,6 +3,7 @@ from __future__ import absolute_import, unicode_literals
import datetime import datetime
import os import os
from decimal import Decimal from decimal import Decimal
import warnings
from django import forms from django import forms
from django.core.exceptions import FieldError from django.core.exceptions import FieldError
@ -30,19 +31,25 @@ if test_images:
class ImageFileForm(forms.ModelForm): class ImageFileForm(forms.ModelForm):
class Meta: class Meta:
model = ImageFile model = ImageFile
fields = '__all__'
class OptionalImageFileForm(forms.ModelForm): class OptionalImageFileForm(forms.ModelForm):
class Meta: class Meta:
model = OptionalImageFile model = OptionalImageFile
fields = '__all__'
class ProductForm(forms.ModelForm): class ProductForm(forms.ModelForm):
class Meta: class Meta:
model = Product model = Product
fields = '__all__'
class PriceForm(forms.ModelForm): class PriceForm(forms.ModelForm):
class Meta: class Meta:
model = Price model = Price
fields = '__all__'
class BookForm(forms.ModelForm): class BookForm(forms.ModelForm):
@ -66,11 +73,13 @@ class ExplicitPKForm(forms.ModelForm):
class PostForm(forms.ModelForm): class PostForm(forms.ModelForm):
class Meta: class Meta:
model = Post model = Post
fields = '__all__'
class DerivedPostForm(forms.ModelForm): class DerivedPostForm(forms.ModelForm):
class Meta: class Meta:
model = DerivedPost model = DerivedPost
fields = '__all__'
class CustomAuthorForm(forms.ModelForm): class CustomAuthorForm(forms.ModelForm):
@ -78,61 +87,79 @@ class CustomAuthorForm(forms.ModelForm):
class Meta: class Meta:
model = Author model = Author
fields = '__all__'
class FlexDatePostForm(forms.ModelForm): class FlexDatePostForm(forms.ModelForm):
class Meta: class Meta:
model = FlexibleDatePost model = FlexibleDatePost
fields = '__all__'
class BaseCategoryForm(forms.ModelForm): class BaseCategoryForm(forms.ModelForm):
class Meta: class Meta:
model = Category model = Category
fields = '__all__'
class ArticleForm(forms.ModelForm): class ArticleForm(forms.ModelForm):
class Meta: class Meta:
model = Article model = Article
fields = '__all__'
class ArticleForm(forms.ModelForm): class ArticleForm(forms.ModelForm):
class Meta: class Meta:
model = Article model = Article
fields = '__all__'
class PartialArticleForm(forms.ModelForm): class PartialArticleForm(forms.ModelForm):
class Meta: class Meta:
model = Article model = Article
fields = ('headline','pub_date') fields = ('headline','pub_date')
class RoykoForm(forms.ModelForm): class RoykoForm(forms.ModelForm):
class Meta: class Meta:
model = Author model = Author
fields = '__all__'
class TestArticleForm(forms.ModelForm): class TestArticleForm(forms.ModelForm):
class Meta: class Meta:
model = Article model = Article
fields = '__all__'
class PartialArticleFormWithSlug(forms.ModelForm): class PartialArticleFormWithSlug(forms.ModelForm):
class Meta: class Meta:
model = Article model = Article
fields=('headline', 'slug', 'pub_date') fields = ('headline', 'slug', 'pub_date')
class ArticleStatusForm(forms.ModelForm): class ArticleStatusForm(forms.ModelForm):
class Meta: class Meta:
model = ArticleStatus model = ArticleStatus
fields = '__all__'
class InventoryForm(forms.ModelForm): class InventoryForm(forms.ModelForm):
class Meta: class Meta:
model = Inventory model = Inventory
fields = '__all__'
class SelectInventoryForm(forms.Form): class SelectInventoryForm(forms.Form):
items = forms.ModelMultipleChoiceField(Inventory.objects.all(), to_field_name='barcode') items = forms.ModelMultipleChoiceField(Inventory.objects.all(), to_field_name='barcode')
class CustomFieldForExclusionForm(forms.ModelForm): class CustomFieldForExclusionForm(forms.ModelForm):
class Meta: class Meta:
model = CustomFieldForExclusionModel model = CustomFieldForExclusionModel
fields = ['name', 'markup'] fields = ['name', 'markup']
class ShortCategory(forms.ModelForm): class ShortCategory(forms.ModelForm):
name = forms.CharField(max_length=5) name = forms.CharField(max_length=5)
slug = forms.CharField(max_length=5) slug = forms.CharField(max_length=5)
@ -140,30 +167,44 @@ class ShortCategory(forms.ModelForm):
class Meta: class Meta:
model = Category model = Category
fields = '__all__'
class ImprovedArticleForm(forms.ModelForm): class ImprovedArticleForm(forms.ModelForm):
class Meta: class Meta:
model = ImprovedArticle model = ImprovedArticle
fields = '__all__'
class ImprovedArticleWithParentLinkForm(forms.ModelForm): class ImprovedArticleWithParentLinkForm(forms.ModelForm):
class Meta: class Meta:
model = ImprovedArticleWithParentLink model = ImprovedArticleWithParentLink
fields = '__all__'
class BetterAuthorForm(forms.ModelForm): class BetterAuthorForm(forms.ModelForm):
class Meta: class Meta:
model = BetterAuthor model = BetterAuthor
fields = '__all__'
class AuthorProfileForm(forms.ModelForm): class AuthorProfileForm(forms.ModelForm):
class Meta: class Meta:
model = AuthorProfile model = AuthorProfile
fields = '__all__'
class TextFileForm(forms.ModelForm): class TextFileForm(forms.ModelForm):
class Meta: class Meta:
model = TextFile model = TextFile
fields = '__all__'
class BigIntForm(forms.ModelForm): class BigIntForm(forms.ModelForm):
class Meta: class Meta:
model = BigInt model = BigInt
fields = '__all__'
class ModelFormWithMedia(forms.ModelForm): class ModelFormWithMedia(forms.ModelForm):
class Media: class Media:
@ -173,19 +214,25 @@ class ModelFormWithMedia(forms.ModelForm):
} }
class Meta: class Meta:
model = TextFile model = TextFile
fields = '__all__'
class CommaSeparatedIntegerForm(forms.ModelForm): class CommaSeparatedIntegerForm(forms.ModelForm):
class Meta: class Meta:
model = CommaSeparatedInteger model = CommaSeparatedInteger
fields = '__all__'
class PriceFormWithoutQuantity(forms.ModelForm): class PriceFormWithoutQuantity(forms.ModelForm):
class Meta: class Meta:
model = Price model = Price
exclude = ('quantity',) exclude = ('quantity',)
class ColourfulItemForm(forms.ModelForm): class ColourfulItemForm(forms.ModelForm):
class Meta: class Meta:
model = ColourfulItem model = ColourfulItem
fields = '__all__'
class ModelFormBaseTest(TestCase): class ModelFormBaseTest(TestCase):
@ -193,6 +240,25 @@ class ModelFormBaseTest(TestCase):
self.assertEqual(list(BaseCategoryForm.base_fields), self.assertEqual(list(BaseCategoryForm.base_fields),
['name', 'slug', 'url']) ['name', 'slug', 'url'])
def test_missing_fields_attribute(self):
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always", PendingDeprecationWarning)
class MissingFieldsForm(forms.ModelForm):
class Meta:
model = Category
# There is some internal state in warnings module which means that
# if a warning has been seen already, the catch_warnings won't
# have recorded it. The following line therefore will not work reliably:
# self.assertEqual(w[0].category, PendingDeprecationWarning)
# Until end of the deprecation cycle, should still create the
# form as before:
self.assertEqual(list(MissingFieldsForm.base_fields),
['name', 'slug', 'url'])
def test_extra_fields(self): def test_extra_fields(self):
class ExtraFields(BaseCategoryForm): class ExtraFields(BaseCategoryForm):
some_extra_field = forms.BooleanField() some_extra_field = forms.BooleanField()
@ -206,6 +272,33 @@ class ModelFormBaseTest(TestCase):
class Meta: class Meta:
model = Category model = Category
fields = '__all__'
self.assertTrue(isinstance(ReplaceField.base_fields['url'],
forms.fields.BooleanField))
def test_replace_field_variant_2(self):
# Should have the same result as before,
# but 'fields' attribute specified differently
class ReplaceField(forms.ModelForm):
url = forms.BooleanField()
class Meta:
model = Category
fields = ['url']
self.assertTrue(isinstance(ReplaceField.base_fields['url'],
forms.fields.BooleanField))
def test_replace_field_variant_3(self):
# Should have the same result as before,
# but 'fields' attribute specified differently
class ReplaceField(forms.ModelForm):
url = forms.BooleanField()
class Meta:
model = Category
fields = [] # url will still appear, since it is explicit above
self.assertTrue(isinstance(ReplaceField.base_fields['url'], self.assertTrue(isinstance(ReplaceField.base_fields['url'],
forms.fields.BooleanField)) forms.fields.BooleanField))
@ -216,19 +309,11 @@ class ModelFormBaseTest(TestCase):
class Meta: class Meta:
model = Author model = Author
fields = '__all__'
wf = AuthorForm({'name': 'Richard Lockridge'}) wf = AuthorForm({'name': 'Richard Lockridge'})
self.assertTrue(wf.is_valid()) self.assertTrue(wf.is_valid())
def test_limit_fields(self):
class LimitFields(forms.ModelForm):
class Meta:
model = Category
fields = ['url']
self.assertEqual(list(LimitFields.base_fields),
['url'])
def test_limit_nonexistent_field(self): def test_limit_nonexistent_field(self):
expected_msg = 'Unknown field(s) (nonexistent) specified for Category' expected_msg = 'Unknown field(s) (nonexistent) specified for Category'
with self.assertRaisesMessage(FieldError, expected_msg): with self.assertRaisesMessage(FieldError, expected_msg):
@ -294,6 +379,7 @@ class ModelFormBaseTest(TestCase):
""" """
class Meta: class Meta:
model = Article model = Article
fields = '__all__'
# MixModelForm is now an Article-related thing, because MixModelForm.Meta # MixModelForm is now an Article-related thing, because MixModelForm.Meta
# overrides BaseCategoryForm.Meta. # overrides BaseCategoryForm.Meta.
@ -348,6 +434,7 @@ class ModelFormBaseTest(TestCase):
class Meta: class Meta:
model = Category model = Category
fields = '__all__'
class SubclassMeta(SomeCategoryForm): class SubclassMeta(SomeCategoryForm):
""" We can also subclass the Meta inner class to change the fields """ We can also subclass the Meta inner class to change the fields

View File

@ -1,6 +1,7 @@
from __future__ import absolute_import, unicode_literals from __future__ import absolute_import, unicode_literals
from datetime import date from datetime import date
import warnings
from django import forms from django import forms
from django.core.exceptions import FieldError, ValidationError from django.core.exceptions import FieldError, ValidationError
@ -43,9 +44,12 @@ class ModelMultipleChoiceFieldTests(TestCase):
f.clean([p.pk for p in Person.objects.all()[8:9]]) f.clean([p.pk for p in Person.objects.all()[8:9]])
self.assertTrue(self._validator_run) self.assertTrue(self._validator_run)
class TripleForm(forms.ModelForm): class TripleForm(forms.ModelForm):
class Meta: class Meta:
model = Triple model = Triple
fields = '__all__'
class UniqueTogetherTests(TestCase): class UniqueTogetherTests(TestCase):
def test_multiple_field_unique_together(self): def test_multiple_field_unique_together(self):
@ -63,15 +67,18 @@ class UniqueTogetherTests(TestCase):
form = TripleForm({'left': '1', 'middle': '3', 'right': '1'}) form = TripleForm({'left': '1', 'middle': '3', 'right': '1'})
self.assertTrue(form.is_valid()) self.assertTrue(form.is_valid())
class TripleFormWithCleanOverride(forms.ModelForm): class TripleFormWithCleanOverride(forms.ModelForm):
class Meta: class Meta:
model = Triple model = Triple
fields = '__all__'
def clean(self): def clean(self):
if not self.cleaned_data['left'] == self.cleaned_data['right']: if not self.cleaned_data['left'] == self.cleaned_data['right']:
raise forms.ValidationError('Left and right should be equal') raise forms.ValidationError('Left and right should be equal')
return self.cleaned_data return self.cleaned_data
class OverrideCleanTests(TestCase): class OverrideCleanTests(TestCase):
def test_override_clean(self): def test_override_clean(self):
""" """
@ -84,6 +91,7 @@ class OverrideCleanTests(TestCase):
# by form.full_clean(). # by form.full_clean().
self.assertEqual(form.instance.left, 1) self.assertEqual(form.instance.left, 1)
# Regression test for #12960. # Regression test for #12960.
# Make sure the cleaned_data returned from ModelForm.clean() is applied to the # Make sure the cleaned_data returned from ModelForm.clean() is applied to the
# model instance. # model instance.
@ -95,6 +103,8 @@ class PublicationForm(forms.ModelForm):
class Meta: class Meta:
model = Publication model = Publication
fields = '__all__'
class ModelFormCleanTest(TestCase): class ModelFormCleanTest(TestCase):
def test_model_form_clean_applies_to_model(self): def test_model_form_clean_applies_to_model(self):
@ -103,9 +113,12 @@ class ModelFormCleanTest(TestCase):
publication = form.save() publication = form.save()
self.assertEqual(publication.title, 'TEST') self.assertEqual(publication.title, 'TEST')
class FPForm(forms.ModelForm): class FPForm(forms.ModelForm):
class Meta: class Meta:
model = FilePathModel model = FilePathModel
fields = '__all__'
class FilePathFieldTests(TestCase): class FilePathFieldTests(TestCase):
def test_file_path_field_blank(self): def test_file_path_field_blank(self):
@ -133,7 +146,8 @@ class ManyToManyCallableInitialTests(TestCase):
book3 = Publication.objects.create(title="Third Book", date_published=date(2009,1,1)) book3 = Publication.objects.create(title="Third Book", date_published=date(2009,1,1))
# Create a ModelForm, instantiate it, and check that the output is as expected # Create a ModelForm, instantiate it, and check that the output is as expected
ModelForm = modelform_factory(Article, formfield_callback=formfield_for_dbfield) ModelForm = modelform_factory(Article, fields="__all__",
formfield_callback=formfield_for_dbfield)
form = ModelForm() form = ModelForm()
self.assertHTMLEqual(form.as_ul(), """<li><label for="id_headline">Headline:</label> <input id="id_headline" type="text" name="headline" maxlength="100" /></li> self.assertHTMLEqual(form.as_ul(), """<li><label for="id_headline">Headline:</label> <input id="id_headline" type="text" name="headline" maxlength="100" /></li>
<li><label for="id_publications">Publications:</label> <select multiple="multiple" name="publications" id="id_publications"> <li><label for="id_publications">Publications:</label> <select multiple="multiple" name="publications" id="id_publications">
@ -143,9 +157,12 @@ class ManyToManyCallableInitialTests(TestCase):
</select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></li>""" </select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></li>"""
% (book1.pk, book2.pk, book3.pk)) % (book1.pk, book2.pk, book3.pk))
class CFFForm(forms.ModelForm): class CFFForm(forms.ModelForm):
class Meta: class Meta:
model = CustomFF model = CustomFF
fields = '__all__'
class CustomFieldSaveTests(TestCase): class CustomFieldSaveTests(TestCase):
def test_save(self): def test_save(self):
@ -168,9 +185,12 @@ class ModelChoiceIteratorTests(TestCase):
f = Form() f = Form()
self.assertEqual(len(f.fields["publications"].choices), 1) self.assertEqual(len(f.fields["publications"].choices), 1)
class RealPersonForm(forms.ModelForm): class RealPersonForm(forms.ModelForm):
class Meta: class Meta:
model = RealPerson model = RealPerson
fields = '__all__'
class CustomModelFormSaveMethod(TestCase): class CustomModelFormSaveMethod(TestCase):
def test_string_message(self): def test_string_message(self):
@ -230,9 +250,12 @@ class TestTicket11183(TestCase):
self.assertTrue(field1 is not ModelChoiceForm.base_fields['person']) self.assertTrue(field1 is not ModelChoiceForm.base_fields['person'])
self.assertTrue(field1.widget.choices.field is field1) self.assertTrue(field1.widget.choices.field is field1)
class HomepageForm(forms.ModelForm): class HomepageForm(forms.ModelForm):
class Meta: class Meta:
model = Homepage model = Homepage
fields = '__all__'
class URLFieldTests(TestCase): class URLFieldTests(TestCase):
def test_url_on_modelform(self): def test_url_on_modelform(self):
@ -274,6 +297,7 @@ class FormFieldCallbackTests(TestCase):
class Meta: class Meta:
model = Person model = Person
widgets = {'name': widget} widgets = {'name': widget}
fields = "__all__"
Form = modelform_factory(Person, form=BaseForm) Form = modelform_factory(Person, form=BaseForm)
self.assertTrue(Form.base_fields['name'].widget is widget) self.assertTrue(Form.base_fields['name'].widget is widget)
@ -285,11 +309,11 @@ class FormFieldCallbackTests(TestCase):
widget = forms.Textarea() widget = forms.Textarea()
# Without a widget should not set the widget to textarea # Without a widget should not set the widget to textarea
Form = modelform_factory(Person) Form = modelform_factory(Person, fields="__all__")
self.assertNotEqual(Form.base_fields['name'].widget.__class__, forms.Textarea) self.assertNotEqual(Form.base_fields['name'].widget.__class__, forms.Textarea)
# With a widget should not set the widget to textarea # With a widget should not set the widget to textarea
Form = modelform_factory(Person, widgets={'name':widget}) Form = modelform_factory(Person, fields="__all__", widgets={'name':widget})
self.assertEqual(Form.base_fields['name'].widget.__class__, forms.Textarea) self.assertEqual(Form.base_fields['name'].widget.__class__, forms.Textarea)
def test_custom_callback(self): def test_custom_callback(self):
@ -307,6 +331,7 @@ class FormFieldCallbackTests(TestCase):
class Meta: class Meta:
model = Person model = Person
widgets = {'name': widget} widgets = {'name': widget}
fields = "__all__"
_ = modelform_factory(Person, form=BaseForm, _ = modelform_factory(Person, form=BaseForm,
formfield_callback=callback) formfield_callback=callback)
@ -317,7 +342,7 @@ class FormFieldCallbackTests(TestCase):
def test_bad_callback(self): def test_bad_callback(self):
# A bad callback provided by user still gives an error # A bad callback provided by user still gives an error
self.assertRaises(TypeError, modelform_factory, Person, self.assertRaises(TypeError, modelform_factory, Person, fields="__all__",
formfield_callback='not a function or callable') formfield_callback='not a function or callable')
@ -362,6 +387,8 @@ class InvalidFieldAndFactory(TestCase):
class DocumentForm(forms.ModelForm): class DocumentForm(forms.ModelForm):
class Meta: class Meta:
model = Document model = Document
fields = '__all__'
class FileFieldTests(unittest.TestCase): class FileFieldTests(unittest.TestCase):
def test_clean_false(self): def test_clean_false(self):
@ -425,6 +452,7 @@ class FileFieldTests(unittest.TestCase):
self.assertTrue('something.txt' in rendered) self.assertTrue('something.txt' in rendered)
self.assertTrue('myfile-clear' in rendered) self.assertTrue('myfile-clear' in rendered)
class EditionForm(forms.ModelForm): class EditionForm(forms.ModelForm):
author = forms.ModelChoiceField(queryset=Person.objects.all()) author = forms.ModelChoiceField(queryset=Person.objects.all())
publication = forms.ModelChoiceField(queryset=Publication.objects.all()) publication = forms.ModelChoiceField(queryset=Publication.objects.all())
@ -433,6 +461,8 @@ class EditionForm(forms.ModelForm):
class Meta: class Meta:
model = Edition model = Edition
fields = '__all__'
class UniqueErrorsTests(TestCase): class UniqueErrorsTests(TestCase):
def setUp(self): def setUp(self):
@ -473,7 +503,7 @@ class EmptyFieldsTestCase(TestCase):
def test_empty_fields_to_construct_instance(self): def test_empty_fields_to_construct_instance(self):
"No fields should be set on a model instance if construct_instance receives fields=()" "No fields should be set on a model instance if construct_instance receives fields=()"
form = modelform_factory(Person)({'name': 'John Doe'}) form = modelform_factory(Person, fields="__all__")({'name': 'John Doe'})
self.assertTrue(form.is_valid()) self.assertTrue(form.is_valid())
instance = construct_instance(form, Person(), fields=()) instance = construct_instance(form, Person(), fields=())
self.assertEqual(instance.name, '') self.assertEqual(instance.name, '')
@ -485,10 +515,25 @@ class CustomMetaclass(ModelFormMetaclass):
new.base_fields = {} new.base_fields = {}
return new return new
class CustomMetaclassForm(six.with_metaclass(CustomMetaclass, forms.ModelForm)): class CustomMetaclassForm(six.with_metaclass(CustomMetaclass, forms.ModelForm)):
pass pass
class CustomMetaclassTestCase(TestCase): class CustomMetaclassTestCase(TestCase):
def test_modelform_factory_metaclass(self): def test_modelform_factory_metaclass(self):
new_cls = modelform_factory(Person, form=CustomMetaclassForm) new_cls = modelform_factory(Person, fields="__all__", form=CustomMetaclassForm)
self.assertEqual(new_cls.base_fields, {}) self.assertEqual(new_cls.base_fields, {})
class TestTicket19733(TestCase):
def test_modelform_factory_without_fields(self):
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always", PendingDeprecationWarning)
# This should become an error once deprecation cycle is complete.
form = modelform_factory(Person)
self.assertEqual(w[0].category, PendingDeprecationWarning)
def test_modelform_factory_with_all_fields(self):
form = modelform_factory(Person, fields="__all__")
self.assertEqual(form.base_fields.keys(), ["name"])

View File

@ -21,7 +21,7 @@ from .models import (Author, BetterAuthor, Book, BookWithCustomPK,
class DeletionTests(TestCase): class DeletionTests(TestCase):
def test_deletion(self): def test_deletion(self):
PoetFormSet = modelformset_factory(Poet, can_delete=True) PoetFormSet = modelformset_factory(Poet, fields="__all__", can_delete=True)
poet = Poet.objects.create(name='test') poet = Poet.objects.create(name='test')
data = { data = {
'form-TOTAL_FORMS': '1', 'form-TOTAL_FORMS': '1',
@ -41,7 +41,7 @@ class DeletionTests(TestCase):
Make sure that an add form that is filled out, but marked for deletion Make sure that an add form that is filled out, but marked for deletion
doesn't cause validation errors. doesn't cause validation errors.
""" """
PoetFormSet = modelformset_factory(Poet, can_delete=True) PoetFormSet = modelformset_factory(Poet, fields="__all__", can_delete=True)
poet = Poet.objects.create(name='test') poet = Poet.objects.create(name='test')
# One existing untouched and two new unvalid forms # One existing untouched and two new unvalid forms
data = { data = {
@ -75,7 +75,7 @@ class DeletionTests(TestCase):
Make sure that a change form that is filled out, but marked for deletion Make sure that a change form that is filled out, but marked for deletion
doesn't cause validation errors. doesn't cause validation errors.
""" """
PoetFormSet = modelformset_factory(Poet, can_delete=True) PoetFormSet = modelformset_factory(Poet, fields="__all__", can_delete=True)
poet = Poet.objects.create(name='test') poet = Poet.objects.create(name='test')
data = { data = {
'form-TOTAL_FORMS': '1', 'form-TOTAL_FORMS': '1',
@ -100,7 +100,7 @@ class DeletionTests(TestCase):
class ModelFormsetTest(TestCase): class ModelFormsetTest(TestCase):
def test_simple_save(self): def test_simple_save(self):
qs = Author.objects.all() qs = Author.objects.all()
AuthorFormSet = modelformset_factory(Author, extra=3) AuthorFormSet = modelformset_factory(Author, fields="__all__", extra=3)
formset = AuthorFormSet(queryset=qs) formset = AuthorFormSet(queryset=qs)
self.assertEqual(len(formset.forms), 3) self.assertEqual(len(formset.forms), 3)
@ -138,7 +138,7 @@ class ModelFormsetTest(TestCase):
# we'll use it to display them in alphabetical order by name. # we'll use it to display them in alphabetical order by name.
qs = Author.objects.order_by('name') qs = Author.objects.order_by('name')
AuthorFormSet = modelformset_factory(Author, extra=1, can_delete=False) AuthorFormSet = modelformset_factory(Author, fields="__all__", extra=1, can_delete=False)
formset = AuthorFormSet(queryset=qs) formset = AuthorFormSet(queryset=qs)
self.assertEqual(len(formset.forms), 3) self.assertEqual(len(formset.forms), 3)
@ -176,7 +176,7 @@ class ModelFormsetTest(TestCase):
# marked for deletion, make sure we don't save that form. # marked for deletion, make sure we don't save that form.
qs = Author.objects.order_by('name') qs = Author.objects.order_by('name')
AuthorFormSet = modelformset_factory(Author, extra=1, can_delete=True) AuthorFormSet = modelformset_factory(Author, fields="__all__", extra=1, can_delete=True)
formset = AuthorFormSet(queryset=qs) formset = AuthorFormSet(queryset=qs)
self.assertEqual(len(formset.forms), 4) self.assertEqual(len(formset.forms), 4)
@ -256,7 +256,7 @@ class ModelFormsetTest(TestCase):
author4 = Author.objects.create(name='John Steinbeck') author4 = Author.objects.create(name='John Steinbeck')
AuthorMeetingFormSet = modelformset_factory(AuthorMeeting, extra=1, can_delete=True) AuthorMeetingFormSet = modelformset_factory(AuthorMeeting, fields="__all__", extra=1, can_delete=True)
data = { data = {
'form-TOTAL_FORMS': '2', # the number of forms rendered 'form-TOTAL_FORMS': '2', # the number of forms rendered
'form-INITIAL_FORMS': '1', # the number of forms with initial data 'form-INITIAL_FORMS': '1', # the number of forms with initial data
@ -294,22 +294,22 @@ class ModelFormsetTest(TestCase):
qs = Author.objects.order_by('name') qs = Author.objects.order_by('name')
AuthorFormSet = modelformset_factory(Author, max_num=None, extra=3) AuthorFormSet = modelformset_factory(Author, fields="__all__", max_num=None, extra=3)
formset = AuthorFormSet(queryset=qs) formset = AuthorFormSet(queryset=qs)
self.assertEqual(len(formset.forms), 6) self.assertEqual(len(formset.forms), 6)
self.assertEqual(len(formset.extra_forms), 3) self.assertEqual(len(formset.extra_forms), 3)
AuthorFormSet = modelformset_factory(Author, max_num=4, extra=3) AuthorFormSet = modelformset_factory(Author, fields="__all__", max_num=4, extra=3)
formset = AuthorFormSet(queryset=qs) formset = AuthorFormSet(queryset=qs)
self.assertEqual(len(formset.forms), 4) self.assertEqual(len(formset.forms), 4)
self.assertEqual(len(formset.extra_forms), 1) self.assertEqual(len(formset.extra_forms), 1)
AuthorFormSet = modelformset_factory(Author, max_num=0, extra=3) AuthorFormSet = modelformset_factory(Author, fields="__all__", max_num=0, extra=3)
formset = AuthorFormSet(queryset=qs) formset = AuthorFormSet(queryset=qs)
self.assertEqual(len(formset.forms), 3) self.assertEqual(len(formset.forms), 3)
self.assertEqual(len(formset.extra_forms), 0) self.assertEqual(len(formset.extra_forms), 0)
AuthorFormSet = modelformset_factory(Author, max_num=None) AuthorFormSet = modelformset_factory(Author, fields="__all__", max_num=None)
formset = AuthorFormSet(queryset=qs) formset = AuthorFormSet(queryset=qs)
self.assertQuerysetEqual(formset.get_queryset(), [ self.assertQuerysetEqual(formset.get_queryset(), [
'<Author: Charles Baudelaire>', '<Author: Charles Baudelaire>',
@ -317,7 +317,7 @@ class ModelFormsetTest(TestCase):
'<Author: Walt Whitman>', '<Author: Walt Whitman>',
]) ])
AuthorFormSet = modelformset_factory(Author, max_num=0) AuthorFormSet = modelformset_factory(Author, fields="__all__", max_num=0)
formset = AuthorFormSet(queryset=qs) formset = AuthorFormSet(queryset=qs)
self.assertQuerysetEqual(formset.get_queryset(), [ self.assertQuerysetEqual(formset.get_queryset(), [
'<Author: Charles Baudelaire>', '<Author: Charles Baudelaire>',
@ -325,7 +325,7 @@ class ModelFormsetTest(TestCase):
'<Author: Walt Whitman>', '<Author: Walt Whitman>',
]) ])
AuthorFormSet = modelformset_factory(Author, max_num=4) AuthorFormSet = modelformset_factory(Author, fields="__all__", max_num=4)
formset = AuthorFormSet(queryset=qs) formset = AuthorFormSet(queryset=qs)
self.assertQuerysetEqual(formset.get_queryset(), [ self.assertQuerysetEqual(formset.get_queryset(), [
'<Author: Charles Baudelaire>', '<Author: Charles Baudelaire>',
@ -343,7 +343,7 @@ class ModelFormsetTest(TestCase):
author.save() author.save()
return author return author
PoetFormSet = modelformset_factory(Poet, form=PoetForm) PoetFormSet = modelformset_factory(Poet, fields="__all__", form=PoetForm)
data = { data = {
'form-TOTAL_FORMS': '3', # the number of forms rendered 'form-TOTAL_FORMS': '3', # the number of forms rendered
@ -387,7 +387,7 @@ class ModelFormsetTest(TestCase):
self.assertFalse("subtitle" in formset.forms[0].fields) self.assertFalse("subtitle" in formset.forms[0].fields)
def test_model_inheritance(self): def test_model_inheritance(self):
BetterAuthorFormSet = modelformset_factory(BetterAuthor) BetterAuthorFormSet = modelformset_factory(BetterAuthor, fields="__all__")
formset = BetterAuthorFormSet() formset = BetterAuthorFormSet()
self.assertEqual(len(formset.forms), 1) self.assertEqual(len(formset.forms), 1)
self.assertHTMLEqual(formset.forms[0].as_p(), self.assertHTMLEqual(formset.forms[0].as_p(),
@ -440,7 +440,7 @@ class ModelFormsetTest(TestCase):
# We can also create a formset that is tied to a parent model. This is # We can also create a formset that is tied to a parent model. This is
# how the admin system's edit inline functionality works. # how the admin system's edit inline functionality works.
AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=3) AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=3, fields="__all__")
author = Author.objects.create(name='Charles Baudelaire') author = Author.objects.create(name='Charles Baudelaire')
formset = AuthorBooksFormSet(instance=author) formset = AuthorBooksFormSet(instance=author)
@ -474,7 +474,7 @@ class ModelFormsetTest(TestCase):
# another one. This time though, an edit form will be available for # another one. This time though, an edit form will be available for
# every existing book. # every existing book.
AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=2) AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=2, fields="__all__")
author = Author.objects.get(name='Charles Baudelaire') author = Author.objects.get(name='Charles Baudelaire')
formset = AuthorBooksFormSet(instance=author) formset = AuthorBooksFormSet(instance=author)
@ -514,7 +514,7 @@ class ModelFormsetTest(TestCase):
def test_inline_formsets_save_as_new(self): def test_inline_formsets_save_as_new(self):
# The save_as_new parameter lets you re-associate the data to a new # The save_as_new parameter lets you re-associate the data to a new
# instance. This is used in the admin for save_as functionality. # instance. This is used in the admin for save_as functionality.
AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=2) AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=2, fields="__all__")
author = Author.objects.create(name='Charles Baudelaire') author = Author.objects.create(name='Charles Baudelaire')
data = { data = {
@ -553,7 +553,7 @@ class ModelFormsetTest(TestCase):
# primary key that is not the fk to the parent object. # primary key that is not the fk to the parent object.
self.maxDiff = 1024 self.maxDiff = 1024
AuthorBooksFormSet2 = inlineformset_factory(Author, BookWithCustomPK, can_delete=False, extra=1) AuthorBooksFormSet2 = inlineformset_factory(Author, BookWithCustomPK, can_delete=False, extra=1, fields="__all__")
author = Author.objects.create(pk=1, name='Charles Baudelaire') author = Author.objects.create(pk=1, name='Charles Baudelaire')
formset = AuthorBooksFormSet2(instance=author) formset = AuthorBooksFormSet2(instance=author)
@ -585,7 +585,7 @@ class ModelFormsetTest(TestCase):
# Test inline formsets where the inline-edited object uses multi-table # Test inline formsets where the inline-edited object uses multi-table
# inheritance, thus has a non AutoField yet auto-created primary key. # inheritance, thus has a non AutoField yet auto-created primary key.
AuthorBooksFormSet3 = inlineformset_factory(Author, AlternateBook, can_delete=False, extra=1) AuthorBooksFormSet3 = inlineformset_factory(Author, AlternateBook, can_delete=False, extra=1, fields="__all__")
author = Author.objects.create(pk=1, name='Charles Baudelaire') author = Author.objects.create(pk=1, name='Charles Baudelaire')
formset = AuthorBooksFormSet3(instance=author) formset = AuthorBooksFormSet3(instance=author)
@ -616,7 +616,7 @@ class ModelFormsetTest(TestCase):
# Test inline formsets where the inline-edited object has a # Test inline formsets where the inline-edited object has a
# unique_together constraint with a nullable member # unique_together constraint with a nullable member
AuthorBooksFormSet4 = inlineformset_factory(Author, BookWithOptionalAltEditor, can_delete=False, extra=2) AuthorBooksFormSet4 = inlineformset_factory(Author, BookWithOptionalAltEditor, can_delete=False, extra=2, fields="__all__")
author = Author.objects.create(pk=1, name='Charles Baudelaire') author = Author.objects.create(pk=1, name='Charles Baudelaire')
data = { data = {
@ -640,7 +640,7 @@ class ModelFormsetTest(TestCase):
self.assertEqual(book2.title, 'Les Fleurs du Mal') self.assertEqual(book2.title, 'Les Fleurs du Mal')
def test_inline_formsets_with_custom_save_method(self): def test_inline_formsets_with_custom_save_method(self):
AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=2) AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=2, fields="__all__")
author = Author.objects.create(pk=1, name='Charles Baudelaire') author = Author.objects.create(pk=1, name='Charles Baudelaire')
book1 = Book.objects.create(pk=1, author=author, title='Les Paradis Artificiels') book1 = Book.objects.create(pk=1, author=author, title='Les Paradis Artificiels')
book2 = Book.objects.create(pk=2, author=author, title='Les Fleurs du Mal') book2 = Book.objects.create(pk=2, author=author, title='Les Fleurs du Mal')
@ -655,7 +655,7 @@ class ModelFormsetTest(TestCase):
poem.save() poem.save()
return poem return poem
PoemFormSet = inlineformset_factory(Poet, Poem, form=PoemForm) PoemFormSet = inlineformset_factory(Poet, Poem, form=PoemForm, fields="__all__")
data = { data = {
'poem_set-TOTAL_FORMS': '3', # the number of forms rendered 'poem_set-TOTAL_FORMS': '3', # the number of forms rendered
@ -732,7 +732,7 @@ class ModelFormsetTest(TestCase):
def test_custom_pk(self): def test_custom_pk(self):
# We need to ensure that it is displayed # We need to ensure that it is displayed
CustomPrimaryKeyFormSet = modelformset_factory(CustomPrimaryKey) CustomPrimaryKeyFormSet = modelformset_factory(CustomPrimaryKey, fields="__all__")
formset = CustomPrimaryKeyFormSet() formset = CustomPrimaryKeyFormSet()
self.assertEqual(len(formset.forms), 1) self.assertEqual(len(formset.forms), 1)
self.assertHTMLEqual(formset.forms[0].as_p(), self.assertHTMLEqual(formset.forms[0].as_p(),
@ -743,7 +743,7 @@ class ModelFormsetTest(TestCase):
place = Place.objects.create(pk=1, name='Giordanos', city='Chicago') place = Place.objects.create(pk=1, name='Giordanos', city='Chicago')
FormSet = inlineformset_factory(Place, Owner, extra=2, can_delete=False) FormSet = inlineformset_factory(Place, Owner, extra=2, can_delete=False, fields="__all__")
formset = FormSet(instance=place) formset = FormSet(instance=place)
self.assertEqual(len(formset.forms), 2) self.assertEqual(len(formset.forms), 2)
self.assertHTMLEqual(formset.forms[0].as_p(), self.assertHTMLEqual(formset.forms[0].as_p(),
@ -799,7 +799,7 @@ class ModelFormsetTest(TestCase):
# Ensure a custom primary key that is a ForeignKey or OneToOneField get rendered for the user to choose. # Ensure a custom primary key that is a ForeignKey or OneToOneField get rendered for the user to choose.
FormSet = modelformset_factory(OwnerProfile) FormSet = modelformset_factory(OwnerProfile, fields="__all__")
formset = FormSet() formset = FormSet()
self.assertHTMLEqual(formset.forms[0].as_p(), self.assertHTMLEqual(formset.forms[0].as_p(),
'<p><label for="id_form-0-owner">Owner:</label> <select name="form-0-owner" id="id_form-0-owner">\n' '<p><label for="id_form-0-owner">Owner:</label> <select name="form-0-owner" id="id_form-0-owner">\n'
@ -811,7 +811,7 @@ class ModelFormsetTest(TestCase):
% (owner1.auto_id, owner2.auto_id)) % (owner1.auto_id, owner2.auto_id))
owner1 = Owner.objects.get(name='Joe Perry') owner1 = Owner.objects.get(name='Joe Perry')
FormSet = inlineformset_factory(Owner, OwnerProfile, max_num=1, can_delete=False) FormSet = inlineformset_factory(Owner, OwnerProfile, max_num=1, can_delete=False, fields="__all__")
self.assertEqual(FormSet.max_num, 1) self.assertEqual(FormSet.max_num, 1)
formset = FormSet(instance=owner1) formset = FormSet(instance=owner1)
@ -861,7 +861,7 @@ class ModelFormsetTest(TestCase):
place = Place.objects.create(pk=1, name='Giordanos', city='Chicago') place = Place.objects.create(pk=1, name='Giordanos', city='Chicago')
FormSet = inlineformset_factory(Place, Location, can_delete=False) FormSet = inlineformset_factory(Place, Location, can_delete=False, fields="__all__")
self.assertEqual(FormSet.max_num, 1) self.assertEqual(FormSet.max_num, 1)
formset = FormSet(instance=place) formset = FormSet(instance=place)
@ -875,7 +875,7 @@ class ModelFormsetTest(TestCase):
self.assertEqual(type(_get_foreign_key(MexicanRestaurant, Owner)), models.ForeignKey) self.assertEqual(type(_get_foreign_key(MexicanRestaurant, Owner)), models.ForeignKey)
def test_unique_validation(self): def test_unique_validation(self):
FormSet = modelformset_factory(Product, extra=1) FormSet = modelformset_factory(Product, fields="__all__", extra=1)
data = { data = {
'form-TOTAL_FORMS': '1', 'form-TOTAL_FORMS': '1',
'form-INITIAL_FORMS': '0', 'form-INITIAL_FORMS': '0',
@ -915,19 +915,19 @@ class ModelFormsetTest(TestCase):
'form-1-quantity': '2', 'form-1-quantity': '2',
} }
FormSet = modelformset_factory(Price, extra=1, max_num=1, validate_max=True) FormSet = modelformset_factory(Price, fields="__all__", extra=1, max_num=1, validate_max=True)
formset = FormSet(data) formset = FormSet(data)
self.assertFalse(formset.is_valid()) self.assertFalse(formset.is_valid())
self.assertEqual(formset.non_form_errors(), ['Please submit 1 or fewer forms.']) self.assertEqual(formset.non_form_errors(), ['Please submit 1 or fewer forms.'])
# Now test the same thing without the validate_max flag to ensure # Now test the same thing without the validate_max flag to ensure
# default behavior is unchanged # default behavior is unchanged
FormSet = modelformset_factory(Price, extra=1, max_num=1) FormSet = modelformset_factory(Price, fields="__all__", extra=1, max_num=1)
formset = FormSet(data) formset = FormSet(data)
self.assertTrue(formset.is_valid()) self.assertTrue(formset.is_valid())
def test_unique_together_validation(self): def test_unique_together_validation(self):
FormSet = modelformset_factory(Price, extra=1) FormSet = modelformset_factory(Price, fields="__all__", extra=1)
data = { data = {
'form-TOTAL_FORMS': '1', 'form-TOTAL_FORMS': '1',
'form-INITIAL_FORMS': '0', 'form-INITIAL_FORMS': '0',
@ -958,7 +958,7 @@ class ModelFormsetTest(TestCase):
# Also see bug #8882. # Also see bug #8882.
repository = Repository.objects.create(name='Test Repo') repository = Repository.objects.create(name='Test Repo')
FormSet = inlineformset_factory(Repository, Revision, extra=1) FormSet = inlineformset_factory(Repository, Revision, extra=1, fields="__all__")
data = { data = {
'revision_set-TOTAL_FORMS': '1', 'revision_set-TOTAL_FORMS': '1',
'revision_set-INITIAL_FORMS': '0', 'revision_set-INITIAL_FORMS': '0',
@ -1007,7 +1007,7 @@ class ModelFormsetTest(TestCase):
# Use of callable defaults (see bug #7975). # Use of callable defaults (see bug #7975).
person = Person.objects.create(name='Ringo') person = Person.objects.create(name='Ringo')
FormSet = inlineformset_factory(Person, Membership, can_delete=False, extra=1) FormSet = inlineformset_factory(Person, Membership, can_delete=False, extra=1, fields="__all__")
formset = FormSet(instance=person) formset = FormSet(instance=person)
# Django will render a hidden field for model fields that have a callable # Django will render a hidden field for model fields that have a callable
@ -1057,11 +1057,12 @@ class ModelFormsetTest(TestCase):
date_joined = forms.SplitDateTimeField(initial=now) date_joined = forms.SplitDateTimeField(initial=now)
class Meta: class Meta:
model = Membership model = Membership
fields = "__all__"
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(MembershipForm, self).__init__(**kwargs) super(MembershipForm, self).__init__(**kwargs)
self.fields['date_joined'].widget = forms.SplitDateTimeWidget() self.fields['date_joined'].widget = forms.SplitDateTimeWidget()
FormSet = inlineformset_factory(Person, Membership, form=MembershipForm, can_delete=False, extra=1) FormSet = inlineformset_factory(Person, Membership, form=MembershipForm, can_delete=False, extra=1, fields="__all__")
data = { data = {
'membership_set-TOTAL_FORMS': '1', 'membership_set-TOTAL_FORMS': '1',
'membership_set-INITIAL_FORMS': '0', 'membership_set-INITIAL_FORMS': '0',
@ -1081,7 +1082,7 @@ class ModelFormsetTest(TestCase):
Player(name="Timmy").save() Player(name="Timmy").save()
Player(name="Bobby", team=team).save() Player(name="Bobby", team=team).save()
PlayerInlineFormSet = inlineformset_factory(Team, Player) PlayerInlineFormSet = inlineformset_factory(Team, Player, fields="__all__")
formset = PlayerInlineFormSet() formset = PlayerInlineFormSet()
self.assertQuerysetEqual(formset.get_queryset(), []) self.assertQuerysetEqual(formset.get_queryset(), [])
@ -1101,7 +1102,7 @@ class ModelFormsetTest(TestCase):
def test_model_formset_with_initial_model_instance(self): def test_model_formset_with_initial_model_instance(self):
# has_changed should compare model instance and primary key # has_changed should compare model instance and primary key
# see #18898 # see #18898
FormSet = modelformset_factory(Poem) FormSet = modelformset_factory(Poem, fields='__all__')
john_milton = Poet(name="John Milton") john_milton = Poet(name="John Milton")
john_milton.save() john_milton.save()
data = { data = {
@ -1117,7 +1118,7 @@ class ModelFormsetTest(TestCase):
def test_model_formset_with_initial_queryset(self): def test_model_formset_with_initial_queryset(self):
# has_changed should work with queryset and list of pk's # has_changed should work with queryset and list of pk's
# see #18898 # see #18898
FormSet = modelformset_factory(AuthorMeeting) FormSet = modelformset_factory(AuthorMeeting, fields='__all__')
author = Author.objects.create(pk=1, name='Charles Baudelaire') author = Author.objects.create(pk=1, name='Charles Baudelaire')
data = { data = {
'form-TOTAL_FORMS': 1, 'form-TOTAL_FORMS': 1,
@ -1131,7 +1132,7 @@ class ModelFormsetTest(TestCase):
self.assertFalse(formset.extra_forms[0].has_changed()) self.assertFalse(formset.extra_forms[0].has_changed())
def test_prevent_duplicates_from_with_the_same_formset(self): def test_prevent_duplicates_from_with_the_same_formset(self):
FormSet = modelformset_factory(Product, extra=2) FormSet = modelformset_factory(Product, fields="__all__", extra=2)
data = { data = {
'form-TOTAL_FORMS': 2, 'form-TOTAL_FORMS': 2,
'form-INITIAL_FORMS': 0, 'form-INITIAL_FORMS': 0,
@ -1144,7 +1145,7 @@ class ModelFormsetTest(TestCase):
self.assertEqual(formset._non_form_errors, self.assertEqual(formset._non_form_errors,
['Please correct the duplicate data for slug.']) ['Please correct the duplicate data for slug.'])
FormSet = modelformset_factory(Price, extra=2) FormSet = modelformset_factory(Price, fields="__all__", extra=2)
data = { data = {
'form-TOTAL_FORMS': 2, 'form-TOTAL_FORMS': 2,
'form-INITIAL_FORMS': 0, 'form-INITIAL_FORMS': 0,
@ -1172,7 +1173,7 @@ class ModelFormsetTest(TestCase):
formset = FormSet(data) formset = FormSet(data)
self.assertTrue(formset.is_valid()) self.assertTrue(formset.is_valid())
FormSet = inlineformset_factory(Author, Book, extra=0) FormSet = inlineformset_factory(Author, Book, extra=0, fields="__all__")
author = Author.objects.create(pk=1, name='Charles Baudelaire') author = Author.objects.create(pk=1, name='Charles Baudelaire')
book1 = Book.objects.create(pk=1, author=author, title='Les Paradis Artificiels') book1 = Book.objects.create(pk=1, author=author, title='Les Paradis Artificiels')
book2 = Book.objects.create(pk=2, author=author, title='Les Fleurs du Mal') book2 = Book.objects.create(pk=2, author=author, title='Les Fleurs du Mal')
@ -1199,7 +1200,7 @@ class ModelFormsetTest(TestCase):
self.assertEqual(formset.errors, self.assertEqual(formset.errors,
[{}, {'__all__': ['Please correct the duplicate values below.']}]) [{}, {'__all__': ['Please correct the duplicate values below.']}])
FormSet = modelformset_factory(Post, extra=2) FormSet = modelformset_factory(Post, fields="__all__", extra=2)
data = { data = {
'form-TOTAL_FORMS': '2', 'form-TOTAL_FORMS': '2',
'form-INITIAL_FORMS': '0', 'form-INITIAL_FORMS': '0',
@ -1265,7 +1266,7 @@ class TestModelFormsetWidgets(TestCase):
widgets = { widgets = {
'name': forms.TextInput(attrs={'class': 'poet'}) 'name': forms.TextInput(attrs={'class': 'poet'})
} }
PoetFormSet = modelformset_factory(Poet, widgets=widgets) PoetFormSet = modelformset_factory(Poet, fields="__all__", widgets=widgets)
form = PoetFormSet.form() form = PoetFormSet.form()
self.assertHTMLEqual( self.assertHTMLEqual(
"%s" % form['name'], "%s" % form['name'],
@ -1276,7 +1277,7 @@ class TestModelFormsetWidgets(TestCase):
widgets = { widgets = {
'title': forms.TextInput(attrs={'class': 'book'}) 'title': forms.TextInput(attrs={'class': 'book'})
} }
BookFormSet = inlineformset_factory(Author, Book, widgets=widgets) BookFormSet = inlineformset_factory(Author, Book, widgets=widgets, fields="__all__")
form = BookFormSet.form() form = BookFormSet.form()
self.assertHTMLEqual( self.assertHTMLEqual(
"%s" % form['title'], "%s" % form['title'],

View File

@ -13,8 +13,8 @@ from .models import User, UserSite, Restaurant, Manager, Network, Host
class InlineFormsetTests(TestCase): class InlineFormsetTests(TestCase):
def test_formset_over_to_field(self): def test_formset_over_to_field(self):
"A formset over a ForeignKey with a to_field can be saved. Regression for #10243" "A formset over a ForeignKey with a to_field can be saved. Regression for #10243"
Form = modelform_factory(User) Form = modelform_factory(User, fields="__all__")
FormSet = inlineformset_factory(User, UserSite) FormSet = inlineformset_factory(User, UserSite, fields="__all__")
# Instantiate the Form and FormSet to prove # Instantiate the Form and FormSet to prove
# you can create a form with no data # you can create a form with no data
@ -89,8 +89,8 @@ class InlineFormsetTests(TestCase):
def test_formset_over_inherited_model(self): def test_formset_over_inherited_model(self):
"A formset over a ForeignKey with a to_field can be saved. Regression for #11120" "A formset over a ForeignKey with a to_field can be saved. Regression for #11120"
Form = modelform_factory(Restaurant) Form = modelform_factory(Restaurant, fields="__all__")
FormSet = inlineformset_factory(Restaurant, Manager) FormSet = inlineformset_factory(Restaurant, Manager, fields="__all__")
# Instantiate the Form and FormSet to prove # Instantiate the Form and FormSet to prove
# you can create a form with no data # you can create a form with no data
@ -156,8 +156,8 @@ class InlineFormsetTests(TestCase):
def test_formset_with_none_instance(self): def test_formset_with_none_instance(self):
"A formset with instance=None can be created. Regression for #11872" "A formset with instance=None can be created. Regression for #11872"
Form = modelform_factory(User) Form = modelform_factory(User, fields="__all__")
FormSet = inlineformset_factory(User, UserSite) FormSet = inlineformset_factory(User, UserSite, fields="__all__")
# Instantiate the Form and FormSet to prove # Instantiate the Form and FormSet to prove
# you can create a formset with an instance of None # you can create a formset with an instance of None
@ -182,7 +182,7 @@ class InlineFormsetTests(TestCase):
efnet = Network.objects.create(name="EFNet") efnet = Network.objects.create(name="EFNet")
host1 = Host.objects.create(hostname="irc.he.net", network=efnet) host1 = Host.objects.create(hostname="irc.he.net", network=efnet)
HostFormSet = inlineformset_factory(Network, Host) HostFormSet = inlineformset_factory(Network, Host, fields="__all__")
# Add a new host, modify previous host, and save-as-new # Add a new host, modify previous host, and save-as-new
data = { data = {
@ -208,7 +208,7 @@ class InlineFormsetTests(TestCase):
def test_initial_data(self): def test_initial_data(self):
user = User.objects.create(username="bibi", serial=1) user = User.objects.create(username="bibi", serial=1)
UserSite.objects.create(user=user, data=7) UserSite.objects.create(user=user, data=7)
FormSet = inlineformset_factory(User, UserSite, extra=2) FormSet = inlineformset_factory(User, UserSite, extra=2, fields="__all__")
formset = FormSet(instance=user, initial=[{'data': 41}, {'data': 42}]) formset = FormSet(instance=user, initial=[{'data': 41}, {'data': 42}])
self.assertEqual(formset.forms[0].initial['data'], 7) self.assertEqual(formset.forms[0].initial['data'], 7)
@ -221,7 +221,7 @@ class FormsetTests(TestCase):
''' '''
Test the type of Formset and Form error attributes Test the type of Formset and Form error attributes
''' '''
Formset = modelformset_factory(User) Formset = modelformset_factory(User, fields="__all__")
data = { data = {
'form-TOTAL_FORMS': '2', 'form-TOTAL_FORMS': '2',
'form-INITIAL_FORMS': '0', 'form-INITIAL_FORMS': '0',
@ -244,14 +244,14 @@ class FormsetTests(TestCase):
def test_initial_data(self): def test_initial_data(self):
User.objects.create(username="bibi", serial=1) User.objects.create(username="bibi", serial=1)
Formset = modelformset_factory(User, extra=2) Formset = modelformset_factory(User, fields="__all__", extra=2)
formset = Formset(initial=[{'username': 'apollo11'}, {'username': 'apollo12'}]) formset = Formset(initial=[{'username': 'apollo11'}, {'username': 'apollo12'}])
self.assertEqual(formset.forms[0].initial['username'], "bibi") self.assertEqual(formset.forms[0].initial['username'], "bibi")
self.assertEqual(formset.extra_forms[0].initial['username'], "apollo11") self.assertEqual(formset.extra_forms[0].initial['username'], "apollo11")
self.assertTrue('value="apollo12"' in formset.extra_forms[1].as_p()) self.assertTrue('value="apollo12"' in formset.extra_forms[1].as_p())
def test_extraneous_query_is_not_run(self): def test_extraneous_query_is_not_run(self):
Formset = modelformset_factory(Network) Formset = modelformset_factory(Network, fields="__all__")
data = {'test-TOTAL_FORMS': '1', data = {'test-TOTAL_FORMS': '1',
'test-INITIAL_FORMS': '0', 'test-INITIAL_FORMS': '0',
'test-MAX_NUM_FORMS': '', 'test-MAX_NUM_FORMS': '',
@ -268,6 +268,7 @@ class CustomWidget(forms.widgets.TextInput):
class UserSiteForm(forms.ModelForm): class UserSiteForm(forms.ModelForm):
class Meta: class Meta:
model = UserSite model = UserSite
fields = "__all__"
widgets = { widgets = {
'id': CustomWidget, 'id': CustomWidget,
'data': CustomWidget, 'data': CustomWidget,
@ -292,7 +293,7 @@ class FormfieldCallbackTests(TestCase):
""" """
def test_inlineformset_factory_default(self): def test_inlineformset_factory_default(self):
Formset = inlineformset_factory(User, UserSite, form=UserSiteForm) Formset = inlineformset_factory(User, UserSite, form=UserSiteForm, fields="__all__")
form = Formset().forms[0] form = Formset().forms[0]
self.assertTrue(isinstance(form['id'].field.widget, CustomWidget)) self.assertTrue(isinstance(form['id'].field.widget, CustomWidget))
self.assertTrue(isinstance(form['data'].field.widget, CustomWidget)) self.assertTrue(isinstance(form['data'].field.widget, CustomWidget))
@ -315,7 +316,7 @@ class FormfieldCallbackTests(TestCase):
def test_inlineformset_custom_callback(self): def test_inlineformset_custom_callback(self):
callback = Callback() callback = Callback()
inlineformset_factory(User, UserSite, form=UserSiteForm, inlineformset_factory(User, UserSite, form=UserSiteForm,
formfield_callback=callback) formfield_callback=callback, fields="__all__")
self.assertCallbackCalled(callback) self.assertCallbackCalled(callback)
def test_modelformset_custom_callback(self): def test_modelformset_custom_callback(self):
@ -353,6 +354,7 @@ class FormfieldShouldDeleteFormTests(TestCase):
""" A model form with a 'should_delete' method """ """ A model form with a 'should_delete' method """
class Meta: class Meta:
model = User model = User
fields = "__all__"
def should_delete(self): def should_delete(self):
""" delete form if odd PK """ """ delete form if odd PK """

View File

@ -418,6 +418,8 @@ class ModelInheritanceTest(TestCase):
class ProfileForm(forms.ModelForm): class ProfileForm(forms.ModelForm):
class Meta: class Meta:
model = Profile model = Profile
fields = '__all__'
User.objects.create(username="user_only") User.objects.create(username="user_only")
p = Profile.objects.create(username="user_with_profile") p = Profile.objects.create(username="user_with_profile")
form = ProfileForm({'username': "user_with_profile", 'extra': "hello"}, form = ProfileForm({'username': "user_with_profile", 'extra': "hello"},

View File

@ -229,9 +229,6 @@ class ModelAdminTests(TestCase):
class AdminBandForm(forms.ModelForm): class AdminBandForm(forms.ModelForm):
delete = forms.BooleanField() delete = forms.BooleanField()
class Meta:
model = Band
class BandAdmin(ModelAdmin): class BandAdmin(ModelAdmin):
form = AdminBandForm form = AdminBandForm
@ -319,8 +316,7 @@ class ModelAdminTests(TestCase):
'</select>' % (band2.id, self.band.id)) '</select>' % (band2.id, self.band.id))
class AdminConcertForm(forms.ModelForm): class AdminConcertForm(forms.ModelForm):
class Meta: pass
model = Concert
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(AdminConcertForm, self).__init__(*args, **kwargs) super(AdminConcertForm, self).__init__(*args, **kwargs)
@ -685,9 +681,6 @@ class ValidationTests(unittest.TestCase):
class AdminBandForm(forms.ModelForm): class AdminBandForm(forms.ModelForm):
delete = forms.BooleanField() delete = forms.BooleanField()
class Meta:
model = Band
class BandAdmin(ModelAdmin): class BandAdmin(ModelAdmin):
form = AdminBandForm form = AdminBandForm

View File

@ -11,3 +11,4 @@ class EventSplitForm(forms.Form):
class EventModelForm(forms.ModelForm): class EventModelForm(forms.ModelForm):
class Meta: class Meta:
model = Event model = Event
fields = '__all__'