summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnubhav Joshi <anubhav9042@gmail.com>2014-06-03 09:06:59 +0530
committerTim Graham <timograham@gmail.com>2014-06-04 07:57:19 -0400
commit45e049937d2564d11035827ca9a9221b86215e45 (patch)
treed1a9cc51459536d014b47cf616ea7f9030494e94
parent7e2c87809ca601b7290203dd54ba3e9f90a702bd (diff)
Fixed #13776 -- Fixed ModelForm.is_valid() exception with non-nullable FK and blank=True.
Thanks peterbe for the report.
-rw-r--r--django/forms/models.py11
-rw-r--r--tests/model_forms/models.py6
-rw-r--r--tests/model_forms/tests.py30
3 files changed, 44 insertions, 3 deletions
diff --git a/django/forms/models.py b/django/forms/models.py
index 263d3d4340..055af3cf77 100644
--- a/django/forms/models.py
+++ b/django/forms/models.py
@@ -401,10 +401,12 @@ class BaseModelForm(BaseForm):
def _post_clean(self):
opts = self._meta
- # Update the model instance with self.cleaned_data.
- self.instance = construct_instance(self, self.instance, opts.fields, opts.exclude)
exclude = self._get_validation_exclusions()
+ # a subset of `exclude` which won't have the InlineForeignKeyField
+ # if we're adding a new object since that value doesn't exist
+ # until after the new instance is saved to the database.
+ construct_instance_exclude = list(exclude)
# Foreign Keys being used to represent inline relationships
# are excluded from basic field value validation. This is for two
@@ -415,8 +417,13 @@ class BaseModelForm(BaseForm):
# so this can't be part of _get_validation_exclusions().
for name, field in self.fields.items():
if isinstance(field, InlineForeignKeyField):
+ if self.cleaned_data.get(name) is not None and self.cleaned_data[name]._state.adding:
+ construct_instance_exclude.append(name)
exclude.append(name)
+ # Update the model instance with self.cleaned_data.
+ self.instance = construct_instance(self, self.instance, opts.fields, construct_instance_exclude)
+
try:
self.instance.full_clean(exclude=exclude, validate_unique=False)
except ValidationError as e:
diff --git a/tests/model_forms/models.py b/tests/model_forms/models.py
index e71c482eb5..d327a5f074 100644
--- a/tests/model_forms/models.py
+++ b/tests/model_forms/models.py
@@ -401,3 +401,9 @@ class Character(models.Model):
class StumpJoke(models.Model):
most_recently_fooled = models.ForeignKey(Character, limit_choices_to=today_callable_dict, related_name="+")
has_fooled_today = models.ManyToManyField(Character, limit_choices_to=today_callable_q, related_name="+")
+
+
+# Model for #13776
+class Student(models.Model):
+ character = models.ForeignKey(Character)
+ study = models.CharField(max_length=30)
diff --git a/tests/model_forms/tests.py b/tests/model_forms/tests.py
index de96246c6f..783a0316f7 100644
--- a/tests/model_forms/tests.py
+++ b/tests/model_forms/tests.py
@@ -23,7 +23,7 @@ from .models import (Article, ArticleStatus, Author, Author1, BetterWriter, BigI
ImprovedArticle, ImprovedArticleWithParentLink, Inventory, Person, Post, Price,
Product, Publication, TextFile, Triple, Writer, WriterProfile,
Colour, ColourfulItem, DateTimePost, CustomErrorMessage,
- test_images, StumpJoke, Character)
+ test_images, StumpJoke, Character, Student)
if test_images:
from .models import ImageFile, OptionalImageFile
@@ -199,6 +199,34 @@ class ModelFormBaseTest(TestCase):
instance = construct_instance(form, Person(), fields=())
self.assertEqual(instance.name, '')
+ def test_blank_with_null_foreign_key_field(self):
+ """
+ #13776 -- ModelForm's with models having a FK set to null=False and
+ required=False should be valid.
+ """
+ class FormForTestingIsValid(forms.ModelForm):
+ class Meta:
+ model = Student
+ fields = '__all__'
+
+ def __init__(self, *args, **kwargs):
+ super(FormForTestingIsValid, self).__init__(*args, **kwargs)
+ self.fields['character'].required = False
+
+ char = Character.objects.create(username='user',
+ last_action=datetime.datetime.today())
+ data = {'study': 'Engineering'}
+ data2 = {'study': 'Engineering', 'character': char.pk}
+
+ # form is valid because required=False for field 'character'
+ f1 = FormForTestingIsValid(data)
+ self.assertTrue(f1.is_valid())
+
+ f2 = FormForTestingIsValid(data2)
+ self.assertTrue(f2.is_valid())
+ obj = f2.save()
+ self.assertEqual(obj.character, char)
+
def test_missing_fields_attribute(self):
message = (
"Creating a ModelForm without either the 'fields' attribute "