Commit dd07c235 authored by Russell Keith-Magee's avatar Russell Keith-Magee
Browse files

Fixed #12749 -- Corrected a problem with validation of inline primary keys....

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
parent b031fa23
Loading
Loading
Loading
Loading
+1 −6
Original line number Diff line number Diff line
@@ -824,13 +824,8 @@ class ForeignKey(RelatedField, Field):
    def validate(self, value, model_instance):
        if self.rel.parent_link:
            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)
        if not value:
        if value is None:
            return

        qs = self.rel.to._default_manager.filter(**{self.rel.field_name:value})
+14 −2
Original line number Diff line number Diff line
@@ -316,12 +316,23 @@ class BaseModelForm(BaseForm):
        return self.cleaned_data

    def _post_clean(self):
        exclude = self._get_validation_exclusions()
        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()

        # 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.
        try:
            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]
        return super(BaseInlineFormSet, self).get_unique_error_message(unique_check)


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
+23 −0
Original line number Diff line number Diff line
@@ -102,6 +102,29 @@ admin.site.register(Holder2, HolderAdmin, inlines=[InnerInline2])
# only Inline media
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': """

# Regression test for #9362
+17 −1
Original line number Diff line number Diff line
@@ -3,7 +3,7 @@ from django.test import TestCase
# local test models
from models import Holder, Inner, InnerInline
from models import Holder2, Inner2, Holder3, Inner3

from models import Person, OutfitItem, Fashionista

class TestInline(TestCase):
    fixtures = ['admin-views-users.xml']
@@ -48,6 +48,22 @@ class TestInline(TestCase):
        # The '+' is dropped from the autogenerated form prefix (Author_books+)
        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):
    fixtures = ['admin-views-users.xml']