Fixed #12749 -- Corrected a problem with validation of inline primary keys. Thanks to Chris.Wesseling@cwi.nl for the report, and nessita for the test case.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@13034 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
b031fa2376
commit
dd07c23545
|
@ -824,13 +824,8 @@ class ForeignKey(RelatedField, Field):
|
||||||
def validate(self, value, model_instance):
|
def validate(self, value, model_instance):
|
||||||
if self.rel.parent_link:
|
if self.rel.parent_link:
|
||||||
return
|
return
|
||||||
# Don't validate the field if a value wasn't supplied. This is
|
|
||||||
# generally the case when saving new inlines in the admin.
|
|
||||||
# See #12507.
|
|
||||||
if value is None:
|
|
||||||
return
|
|
||||||
super(ForeignKey, self).validate(value, model_instance)
|
super(ForeignKey, self).validate(value, model_instance)
|
||||||
if not value:
|
if value is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
qs = self.rel.to._default_manager.filter(**{self.rel.field_name:value})
|
qs = self.rel.to._default_manager.filter(**{self.rel.field_name:value})
|
||||||
|
|
|
@ -316,12 +316,23 @@ class BaseModelForm(BaseForm):
|
||||||
return self.cleaned_data
|
return self.cleaned_data
|
||||||
|
|
||||||
def _post_clean(self):
|
def _post_clean(self):
|
||||||
exclude = self._get_validation_exclusions()
|
|
||||||
opts = self._meta
|
opts = self._meta
|
||||||
|
|
||||||
# Update the model instance with self.cleaned_data.
|
# Update the model instance with self.cleaned_data.
|
||||||
self.instance = construct_instance(self, self.instance, opts.fields, opts.exclude)
|
self.instance = construct_instance(self, self.instance, opts.fields, opts.exclude)
|
||||||
|
|
||||||
|
exclude = self._get_validation_exclusions()
|
||||||
|
|
||||||
|
# Foreign Keys being used to represent inline relationships
|
||||||
|
# are excluded from basic field value validation. This is for two
|
||||||
|
# reasons: firstly, the value may not be supplied (#12507; the
|
||||||
|
# case of providing new values to the admin); secondly the
|
||||||
|
# object being referred to may not yet fully exist (#12749).
|
||||||
|
# However, these fields *must* be included in uniqueness checks,
|
||||||
|
# so this can't be part of _get_validation_exclusions().
|
||||||
|
for f_name, field in self.fields.items():
|
||||||
|
if isinstance(field, InlineForeignKeyField):
|
||||||
|
exclude.append(f_name)
|
||||||
|
|
||||||
# Clean the model instance's fields.
|
# Clean the model instance's fields.
|
||||||
try:
|
try:
|
||||||
self.instance.clean_fields(exclude=exclude)
|
self.instance.clean_fields(exclude=exclude)
|
||||||
|
@ -762,6 +773,7 @@ class BaseInlineFormSet(BaseModelFormSet):
|
||||||
unique_check = [field for field in unique_check if field != self.fk.name]
|
unique_check = [field for field in unique_check if field != self.fk.name]
|
||||||
return super(BaseInlineFormSet, self).get_unique_error_message(unique_check)
|
return super(BaseInlineFormSet, self).get_unique_error_message(unique_check)
|
||||||
|
|
||||||
|
|
||||||
def _get_foreign_key(parent_model, model, fk_name=None, can_fail=False):
|
def _get_foreign_key(parent_model, model, fk_name=None, can_fail=False):
|
||||||
"""
|
"""
|
||||||
Finds and returns the ForeignKey from model to parent if there is one
|
Finds and returns the ForeignKey from model to parent if there is one
|
||||||
|
|
|
@ -102,6 +102,29 @@ admin.site.register(Holder2, HolderAdmin, inlines=[InnerInline2])
|
||||||
# only Inline media
|
# only Inline media
|
||||||
admin.site.register(Holder3, inlines=[InnerInline3])
|
admin.site.register(Holder3, inlines=[InnerInline3])
|
||||||
|
|
||||||
|
# Models for #12749
|
||||||
|
|
||||||
|
class Person(models.Model):
|
||||||
|
firstname = models.CharField(max_length=15)
|
||||||
|
|
||||||
|
class OutfitItem(models.Model):
|
||||||
|
name = models.CharField(max_length=15)
|
||||||
|
|
||||||
|
class Fashionista(models.Model):
|
||||||
|
person = models.OneToOneField(Person, primary_key=True)
|
||||||
|
weaknesses = models.ManyToManyField(OutfitItem, through='ShoppingWeakness', blank=True)
|
||||||
|
|
||||||
|
class ShoppingWeakness(models.Model):
|
||||||
|
fashionista = models.ForeignKey(Fashionista)
|
||||||
|
item = models.ForeignKey(OutfitItem)
|
||||||
|
|
||||||
|
class InlineWeakness(admin.TabularInline):
|
||||||
|
model = ShoppingWeakness
|
||||||
|
extra = 1
|
||||||
|
|
||||||
|
admin.site.register(Fashionista, inlines=[InlineWeakness])
|
||||||
|
|
||||||
|
|
||||||
__test__ = {'API_TESTS': """
|
__test__ = {'API_TESTS': """
|
||||||
|
|
||||||
# Regression test for #9362
|
# Regression test for #9362
|
||||||
|
|
|
@ -3,7 +3,7 @@ from django.test import TestCase
|
||||||
# local test models
|
# local test models
|
||||||
from models import Holder, Inner, InnerInline
|
from models import Holder, Inner, InnerInline
|
||||||
from models import Holder2, Inner2, Holder3, Inner3
|
from models import Holder2, Inner2, Holder3, Inner3
|
||||||
|
from models import Person, OutfitItem, Fashionista
|
||||||
|
|
||||||
class TestInline(TestCase):
|
class TestInline(TestCase):
|
||||||
fixtures = ['admin-views-users.xml']
|
fixtures = ['admin-views-users.xml']
|
||||||
|
@ -48,6 +48,22 @@ class TestInline(TestCase):
|
||||||
# The '+' is dropped from the autogenerated form prefix (Author_books+)
|
# The '+' is dropped from the autogenerated form prefix (Author_books+)
|
||||||
self.assertContains(response, 'id="id_Author_books-TOTAL_FORMS"')
|
self.assertContains(response, 'id="id_Author_books-TOTAL_FORMS"')
|
||||||
|
|
||||||
|
def test_inline_primary(self):
|
||||||
|
person = Person.objects.create(firstname='Imelda')
|
||||||
|
item = OutfitItem.objects.create(name='Shoes')
|
||||||
|
# Imelda likes shoes, but can't cary her own bags.
|
||||||
|
data = {
|
||||||
|
'shoppingweakness_set-TOTAL_FORMS': 1,
|
||||||
|
'shoppingweakness_set-INITIAL_FORMS': 0,
|
||||||
|
'shoppingweakness_set-MAX_NUM_FORMS': 0,
|
||||||
|
'_save': u'Save',
|
||||||
|
'person': person.id,
|
||||||
|
'max_weight': 0,
|
||||||
|
'shoppingweakness_set-0-item': item.id,
|
||||||
|
}
|
||||||
|
response = self.client.post('/test_admin/admin/admin_inlines/fashionista/add/', data)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertEqual(len(Fashionista.objects.filter(person__firstname='Imelda')), 1)
|
||||||
|
|
||||||
class TestInlineMedia(TestCase):
|
class TestInlineMedia(TestCase):
|
||||||
fixtures = ['admin-views-users.xml']
|
fixtures = ['admin-views-users.xml']
|
||||||
|
|
Loading…
Reference in New Issue