mirror of
https://github.com/django/django.git
synced 2025-03-31 03:30:46 +00:00
Fixed #27039 -- Fixed empty data fallback to model field default in model forms.
This commit is contained in:
parent
426bca002c
commit
4bc6b93994
@ -52,6 +52,11 @@ def construct_instance(form, instance, fields=None, exclude=None):
|
|||||||
continue
|
continue
|
||||||
if exclude and f.name in exclude:
|
if exclude and f.name in exclude:
|
||||||
continue
|
continue
|
||||||
|
# Leave defaults for fields that aren't in POST data, except for
|
||||||
|
# checkbox inputs because they don't appear in POST data if not checked.
|
||||||
|
if (f.has_default() and f.name not in form.data and
|
||||||
|
not getattr(form[f.name].field.widget, 'dont_use_model_field_default_for_empty_data', False)):
|
||||||
|
continue
|
||||||
# Defer saving file-type fields until after the other fields, so a
|
# Defer saving file-type fields until after the other fields, so a
|
||||||
# callable upload_to can use the values from other fields.
|
# callable upload_to can use the values from other fields.
|
||||||
if isinstance(f, models.FileField):
|
if isinstance(f, models.FileField):
|
||||||
|
@ -481,6 +481,10 @@ def boolean_check(v):
|
|||||||
|
|
||||||
|
|
||||||
class CheckboxInput(Widget):
|
class CheckboxInput(Widget):
|
||||||
|
# Don't use model field defaults for fields that aren't in POST data,
|
||||||
|
# because checkboxes don't appear in POST data if not checked.
|
||||||
|
dont_use_model_field_default_for_empty_data = True
|
||||||
|
|
||||||
def __init__(self, attrs=None, check_test=None):
|
def __init__(self, attrs=None, check_test=None):
|
||||||
super(CheckboxInput, self).__init__(attrs)
|
super(CheckboxInput, self).__init__(attrs)
|
||||||
# check_test is a callable that takes a value and returns True
|
# check_test is a callable that takes a value and returns True
|
||||||
|
@ -79,3 +79,6 @@ Bugfixes
|
|||||||
|
|
||||||
* Reallowed subclassing ``UserCreationForm`` without ``USERNAME_FIELD`` in
|
* Reallowed subclassing ``UserCreationForm`` without ``USERNAME_FIELD`` in
|
||||||
``Meta.fields`` (:ticket:`27111`).
|
``Meta.fields`` (:ticket:`27111`).
|
||||||
|
|
||||||
|
* Fixed a regression in model forms where model fields with a ``default`` that
|
||||||
|
didn't appear in POST data no longer used the ``default`` (:ticket:`27039`).
|
||||||
|
@ -332,6 +332,21 @@ Note that if the form :ref:`hasn't been validated
|
|||||||
``form.errors``. A ``ValueError`` will be raised if the data in the form
|
``form.errors``. A ``ValueError`` will be raised if the data in the form
|
||||||
doesn't validate -- i.e., if ``form.errors`` evaluates to ``True``.
|
doesn't validate -- i.e., if ``form.errors`` evaluates to ``True``.
|
||||||
|
|
||||||
|
If an optional field doesn't appear in the form's data, the resulting model
|
||||||
|
instance uses the model field :attr:`~django.db.models.Field.default`, if
|
||||||
|
there is one, for that field. This behavior doesn't apply to fields that use
|
||||||
|
:class:`~django.forms.CheckboxInput` (or any custom widget with
|
||||||
|
``dont_use_model_field_default_for_empty_data=True``) since an unchecked
|
||||||
|
checkbox doesn't appear in the data of an HTML form submission. Use a custom
|
||||||
|
form field or widget if you're designing an API and want the default fallback
|
||||||
|
for a ``BooleanField``.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.10.1
|
||||||
|
|
||||||
|
Older versions don't have the exception for
|
||||||
|
:class:`~django.forms.CheckboxInput` which means that unchecked checkboxes
|
||||||
|
receive a value of ``True`` if that's the model field default.
|
||||||
|
|
||||||
This ``save()`` method accepts an optional ``commit`` keyword argument, which
|
This ``save()`` method accepts an optional ``commit`` keyword argument, which
|
||||||
accepts either ``True`` or ``False``. If you call ``save()`` with
|
accepts either ``True`` or ``False``. If you call ``save()`` with
|
||||||
``commit=False``, then it will return an object that hasn't yet been saved to
|
``commit=False``, then it will return an object that hasn't yet been saved to
|
||||||
|
@ -122,6 +122,7 @@ class PublicationDefaults(models.Model):
|
|||||||
date_published = models.DateField(default=datetime.date.today)
|
date_published = models.DateField(default=datetime.date.today)
|
||||||
mode = models.CharField(max_length=2, choices=MODE_CHOICES, default=default_mode)
|
mode = models.CharField(max_length=2, choices=MODE_CHOICES, default=default_mode)
|
||||||
category = models.IntegerField(choices=CATEGORY_CHOICES, default=default_category)
|
category = models.IntegerField(choices=CATEGORY_CHOICES, default=default_category)
|
||||||
|
active = models.BooleanField(default=True)
|
||||||
|
|
||||||
|
|
||||||
class Author(models.Model):
|
class Author(models.Model):
|
||||||
|
@ -565,6 +565,42 @@ class ModelFormBaseTest(TestCase):
|
|||||||
self.assertEqual(list(OrderFields2.base_fields),
|
self.assertEqual(list(OrderFields2.base_fields),
|
||||||
['slug', 'name'])
|
['slug', 'name'])
|
||||||
|
|
||||||
|
def test_default_populated_on_optional_field(self):
|
||||||
|
class PubForm(forms.ModelForm):
|
||||||
|
mode = forms.CharField(max_length=255, required=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = PublicationDefaults
|
||||||
|
fields = ('mode',)
|
||||||
|
|
||||||
|
# Empty data uses the model field default.
|
||||||
|
mf1 = PubForm({})
|
||||||
|
self.assertEqual(mf1.errors, {})
|
||||||
|
m1 = mf1.save(commit=False)
|
||||||
|
self.assertEqual(m1.mode, 'di')
|
||||||
|
self.assertEqual(m1._meta.get_field('mode').get_default(), 'di')
|
||||||
|
|
||||||
|
# Blank data doesn't use the model field default.
|
||||||
|
mf2 = PubForm({'mode': ''})
|
||||||
|
self.assertEqual(mf2.errors, {})
|
||||||
|
m2 = mf2.save(commit=False)
|
||||||
|
self.assertEqual(m2.mode, '')
|
||||||
|
|
||||||
|
def test_default_not_populated_on_optional_checkbox_input(self):
|
||||||
|
class PubForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = PublicationDefaults
|
||||||
|
fields = ('active',)
|
||||||
|
|
||||||
|
# Empty data doesn't use the model default because CheckboxInput
|
||||||
|
# doesn't have a value in HTML form submission.
|
||||||
|
mf1 = PubForm({})
|
||||||
|
self.assertEqual(mf1.errors, {})
|
||||||
|
m1 = mf1.save(commit=False)
|
||||||
|
self.assertIs(m1.active, False)
|
||||||
|
self.assertIsInstance(mf1.fields['active'].widget, forms.CheckboxInput)
|
||||||
|
self.assertIs(m1._meta.get_field('active').get_default(), True)
|
||||||
|
|
||||||
|
|
||||||
class FieldOverridesByFormMetaForm(forms.ModelForm):
|
class FieldOverridesByFormMetaForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -774,7 +810,10 @@ class UniqueTest(TestCase):
|
|||||||
title = 'Boss'
|
title = 'Boss'
|
||||||
isbn = '12345'
|
isbn = '12345'
|
||||||
DerivedBook.objects.create(title=title, author=self.writer, isbn=isbn)
|
DerivedBook.objects.create(title=title, author=self.writer, isbn=isbn)
|
||||||
form = DerivedBookForm({'title': 'Other', 'author': self.writer.pk, 'isbn': isbn})
|
form = DerivedBookForm({
|
||||||
|
'title': 'Other', 'author': self.writer.pk, 'isbn': isbn,
|
||||||
|
'suffix1': '1', 'suffix2': '2',
|
||||||
|
})
|
||||||
self.assertFalse(form.is_valid())
|
self.assertFalse(form.is_valid())
|
||||||
self.assertEqual(len(form.errors), 1)
|
self.assertEqual(len(form.errors), 1)
|
||||||
self.assertEqual(form.errors['isbn'], ['Derived book with this Isbn already exists.'])
|
self.assertEqual(form.errors['isbn'], ['Derived book with this Isbn already exists.'])
|
||||||
@ -2491,7 +2530,7 @@ class OtherModelFormTests(TestCase):
|
|||||||
class PublicationDefaultsForm(forms.ModelForm):
|
class PublicationDefaultsForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PublicationDefaults
|
model = PublicationDefaults
|
||||||
fields = '__all__'
|
fields = ('title', 'date_published', 'mode', 'category')
|
||||||
|
|
||||||
self.maxDiff = 2000
|
self.maxDiff = 2000
|
||||||
form = PublicationDefaultsForm()
|
form = PublicationDefaultsForm()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user