Fixed #19733 - deprecated ModelForms without 'fields' or 'exclude', and added '__all__' shortcut
This also updates all dependent functionality, including modelform_factory and modelformset_factory, and the generic views `ModelFormMixin`, `CreateView` and `UpdateView` which gain a new `fields` attribute.
This commit is contained in:
parent
1e37cb37ce
commit
f026a519ae
|
@ -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):
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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']
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
))
|
||||||
|
|
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -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
|
||||||
----------
|
----------
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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::
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -25,3 +25,4 @@ class Photo(models.Model):
|
||||||
class PhotoForm(ModelForm):
|
class PhotoForm(ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Photo
|
model = Photo
|
||||||
|
fields = '__all__'
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -24,3 +24,4 @@ class CompanyForm(forms.ModelForm):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Company
|
model = Company
|
||||||
|
fields = '__all__'
|
||||||
|
|
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"])
|
||||||
|
|
|
@ -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'],
|
||||||
|
|
|
@ -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 """
|
||||||
|
|
|
@ -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"},
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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__'
|
||||||
|
|
Loading…
Reference in New Issue