From 5097d3c5faab2b6582c4cebee2b265fcdbb893eb Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Fri, 28 Dec 2012 23:16:13 +0100 Subject: [PATCH] [1.5.x] Fix #19524 -- Incorrect caching of parents of unsaved model instances. Thanks qcwxezdas for the report. Refs #13839. Backport of e9c24be. --- django/db/models/base.py | 9 +++++++++ django/forms/models.py | 5 ++++- tests/regressiontests/admin_inlines/admin.py | 6 +++++- tests/regressiontests/admin_inlines/models.py | 13 ++++++++++++- tests/regressiontests/admin_inlines/tests.py | 19 ++++++++++++++++++- 5 files changed, 48 insertions(+), 4 deletions(-) diff --git a/django/db/models/base.py b/django/db/models/base.py index bf9854d289..bd638e2499 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -583,6 +583,15 @@ class Model(six.with_metaclass(ModelBase, object)): if field: setattr(self, field.attname, self._get_pk_val(parent._meta)) + # Since we didn't have an instance of the parent handy, we + # set attname directly, bypassing the descriptor. + # Invalidate the related object cache, in case it's been + # accidentally populated. A fresh instance will be + # re-built from the database if necessary. + cache_name = field.get_cache_name() + if hasattr(self, cache_name): + delattr(self, cache_name) + if meta.proxy: return diff --git a/django/forms/models.py b/django/forms/models.py index 0913a4e8b8..27c246b668 100644 --- a/django/forms/models.py +++ b/django/forms/models.py @@ -692,7 +692,10 @@ class BaseInlineFormSet(BaseModelFormSet): self.rel_name = RelatedObject(self.fk.rel.to, self.model, self.fk).get_accessor_name() if queryset is None: queryset = self.model._default_manager - qs = queryset.filter(**{self.fk.name: self.instance}) + if self.instance.pk: + qs = queryset.filter(**{self.fk.name: self.instance}) + else: + qs = queryset.none() super(BaseInlineFormSet, self).__init__(data, files, prefix=prefix, queryset=qs, **kwargs) diff --git a/tests/regressiontests/admin_inlines/admin.py b/tests/regressiontests/admin_inlines/admin.py index cf51fa49b4..536c3ec079 100644 --- a/tests/regressiontests/admin_inlines/admin.py +++ b/tests/regressiontests/admin_inlines/admin.py @@ -124,6 +124,9 @@ class ChildModel1Inline(admin.TabularInline): class ChildModel2Inline(admin.StackedInline): model = ChildModel2 +# admin for #19524 +class SightingInline(admin.TabularInline): + model = Sighting site.register(TitleCollection, inlines=[TitleInline]) # Test bug #12561 and #12778 @@ -141,4 +144,5 @@ site.register(Holder4, Holder4Admin) site.register(Author, AuthorAdmin) site.register(CapoFamiglia, inlines=[ConsigliereInline, SottoCapoInline]) site.register(ProfileCollection, inlines=[ProfileInline]) -site.register(ParentModelWithCustomPk, inlines=[ChildModel1Inline, ChildModel2Inline]) \ No newline at end of file +site.register(ParentModelWithCustomPk, inlines=[ChildModel1Inline, ChildModel2Inline]) +site.register(ExtraTerrestrial, inlines=[SightingInline]) diff --git a/tests/regressiontests/admin_inlines/models.py b/tests/regressiontests/admin_inlines/models.py index b004d5f85a..147873a9e8 100644 --- a/tests/regressiontests/admin_inlines/models.py +++ b/tests/regressiontests/admin_inlines/models.py @@ -90,7 +90,6 @@ class Inner4Tabular(models.Model): dummy = models.IntegerField(help_text="Awesome tabular help text is awesome.") holder = models.ForeignKey(Holder4) - # Models for #12749 class Person(models.Model): @@ -133,6 +132,7 @@ class Chapter(models.Model): # Models for #16838 + class CapoFamiglia(models.Model): name = models.CharField(max_length=100) @@ -170,6 +170,17 @@ class ChildModel2(models.Model): def get_absolute_url(self): return '/child_model2/' +# Models for #19524 + +class LifeForm(models.Model): + pass + +class ExtraTerrestrial(LifeForm): + name = models.CharField(max_length=100) + +class Sighting(models.Model): + et = models.ForeignKey(ExtraTerrestrial) + place = models.CharField(max_length=100) # Other models diff --git a/tests/regressiontests/admin_inlines/tests.py b/tests/regressiontests/admin_inlines/tests.py index 3c868012fa..54283bf509 100644 --- a/tests/regressiontests/admin_inlines/tests.py +++ b/tests/regressiontests/admin_inlines/tests.py @@ -12,7 +12,7 @@ from .admin import InnerInline, TitleInline, site from .models import (Holder, Inner, Holder2, Inner2, Holder3, Inner3, Person, OutfitItem, Fashionista, Teacher, Parent, Child, Author, Book, Profile, ProfileCollection, ParentModelWithCustomPk, ChildModel1, ChildModel2, - Title) + Sighting, Title) @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) @@ -172,6 +172,23 @@ class TestInline(TestCase): self.assertContains(response, child1_shortcut) self.assertContains(response, child2_shortcut) + def test_create_inlines_on_inherited_model(self): + """ + Ensure that an object can be created with inlines when it inherits + another class. Bug #19524. + """ + data = { + 'name': 'Martian', + 'sighting_set-TOTAL_FORMS': 1, + 'sighting_set-INITIAL_FORMS': 0, + 'sighting_set-MAX_NUM_FORMS': 0, + 'sighting_set-0-place': 'Zone 51', + '_save': 'Save', + } + response = self.client.post('/admin/admin_inlines/extraterrestrial/add/', data) + self.assertEqual(response.status_code, 302) + self.assertEqual(Sighting.objects.filter(et__name='Martian').count(), 1) + @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) class TestInlineMedia(TestCase):