diff --git a/AUTHORS b/AUTHORS index eac35f2f8b..4b5fc953b8 100644 --- a/AUTHORS +++ b/AUTHORS @@ -73,7 +73,7 @@ answer newbie questions, and generally made Django that much better: James Bennett Julian Bez Arvis Bickovskis - Natalia Bidart + Natalia Bidart Paul Bissex Simon Blanchard David Blewett diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index b110596d8a..43a9984c9a 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -1187,6 +1187,7 @@ class InlineModelAdmin(BaseModelAdmin): template = None verbose_name = None verbose_name_plural = None + can_delete = True def __init__(self, parent_model, admin_site): self.admin_site = admin_site @@ -1232,6 +1233,7 @@ class InlineModelAdmin(BaseModelAdmin): "formfield_callback": curry(self.formfield_for_dbfield, request=request), "extra": self.extra, "max_num": self.max_num, + "can_delete": self.can_delete, } defaults.update(kwargs) return inlineformset_factory(self.parent_model, self.model, **defaults) diff --git a/django/contrib/contenttypes/generic.py b/django/contrib/contenttypes/generic.py index 054e8a6246..5dffe2081d 100644 --- a/django/contrib/contenttypes/generic.py +++ b/django/contrib/contenttypes/generic.py @@ -390,7 +390,7 @@ class GenericInlineModelAdmin(InlineModelAdmin): "formfield_callback": self.formfield_for_dbfield, "formset": self.formset, "extra": self.extra, - "can_delete": True, + "can_delete": self.can_delete, "can_order": False, "fields": fields, "max_num": self.max_num, diff --git a/tests/regressiontests/admin_inlines/fixtures/admin-views-users.xml b/tests/regressiontests/admin_inlines/fixtures/admin-views-users.xml new file mode 100644 index 0000000000..aba8f4aace --- /dev/null +++ b/tests/regressiontests/admin_inlines/fixtures/admin-views-users.xml @@ -0,0 +1,17 @@ + + + + super + Super + User + super@example.com + sha1$995a3$6011485ea3834267d719b4c801409b8b1ddd0158 + True + True + True + 2007-05-30 13:20:10 + 2007-05-30 13:20:10 + + + + diff --git a/tests/regressiontests/admin_inlines/models.py b/tests/regressiontests/admin_inlines/models.py index 1893bfc508..303c06a3d9 100644 --- a/tests/regressiontests/admin_inlines/models.py +++ b/tests/regressiontests/admin_inlines/models.py @@ -3,6 +3,7 @@ Testing of admin inline formsets. """ from django.db import models +from django.contrib import admin from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes import generic @@ -29,6 +30,24 @@ class Child(models.Model): def __unicode__(self): return u'I am %s, a child of %s' % (self.name, self.parent) + +class Holder(models.Model): + dummy = models.IntegerField() + + +class Inner(models.Model): + dummy = models.IntegerField() + holder = models.ForeignKey(Holder) + + +class InnerInline(admin.StackedInline): + model = Inner + can_delete = False + + +# Test bug #12561 +admin.site.register(Holder, inlines=[InnerInline]) + __test__ = {'API_TESTS': """ # Regression test for #9362 @@ -48,4 +67,4 @@ inline form. """ -} \ No newline at end of file +} diff --git a/tests/regressiontests/admin_inlines/tests.py b/tests/regressiontests/admin_inlines/tests.py new file mode 100644 index 0000000000..75a6658599 --- /dev/null +++ b/tests/regressiontests/admin_inlines/tests.py @@ -0,0 +1,30 @@ +from django.test import TestCase + +# local test models +from models import Holder, Inner, InnerInline + +class TestInline(TestCase): + fixtures = ['admin-views-users.xml'] + + def setUp(self): + holder = Holder(dummy=13) + holder.save() + Inner(dummy=42, holder=holder).save() + self.change_url = '/test_admin/admin/admin_inlines/holder/%i/' % holder.id + + result = self.client.login(username='super', password='secret') + self.failUnlessEqual(result, True) + + def tearDown(self): + self.client.logout() + + def test_can_delete(self): + """ + can_delete should be passed to inlineformset factory. + """ + response = self.client.get(self.change_url) + inner_formset = response.context[-1]['inline_admin_formsets'][0].formset + expected = InnerInline.can_delete + actual = inner_formset.can_delete + self.assertEqual(expected, actual, 'can_delete must be equal') + diff --git a/tests/regressiontests/generic_inline_admin/models.py b/tests/regressiontests/generic_inline_admin/models.py index 2925b7a801..329c4877eb 100644 --- a/tests/regressiontests/generic_inline_admin/models.py +++ b/tests/regressiontests/generic_inline_admin/models.py @@ -93,3 +93,16 @@ class PhoneNumberInline(generic.GenericTabularInline): model = PhoneNumber admin.site.register(Contact, inlines=[PhoneNumberInline]) + +# +# Generic inline with can_delete=False +# + +class EpisodePermanent(Episode): + pass + +class MediaPermanentInline(generic.GenericTabularInline): + model = Media + can_delete = False + +admin.site.register(EpisodePermanent, inlines=[MediaPermanentInline]) diff --git a/tests/regressiontests/generic_inline_admin/tests.py b/tests/regressiontests/generic_inline_admin/tests.py index 6853a896a4..0626699292 100644 --- a/tests/regressiontests/generic_inline_admin/tests.py +++ b/tests/regressiontests/generic_inline_admin/tests.py @@ -5,7 +5,8 @@ from django.conf import settings from django.contrib.contenttypes.generic import generic_inlineformset_factory # local test models -from models import Episode, EpisodeExtra, EpisodeMaxNum, EpisodeExclude, Media +from models import Episode, EpisodeExtra, EpisodeMaxNum, EpisodeExclude, \ + Media, EpisodePermanent, MediaPermanentInline class GenericAdminViewTest(TestCase): fixtures = ['users.xml'] @@ -201,3 +202,11 @@ class GenericInlineAdminWithUniqueTogetherTest(TestCase): response = self.client.get('/generic_inline_admin/admin/generic_inline_admin/contact/add/') response = self.client.post('/generic_inline_admin/admin/generic_inline_admin/contact/add/', post_data) self.failUnlessEqual(response.status_code, 302) # redirect somewhere + +class NoInlineDeletionTest(TestCase): + def test_no_deletion(self): + fake_site = object() + inline = MediaPermanentInline(EpisodePermanent, fake_site) + fake_request = object() + formset = inline.get_formset(fake_request) + self.assertFalse(formset.can_delete) diff --git a/tests/regressiontests/modeladmin/models.py b/tests/regressiontests/modeladmin/models.py index ef05b5a048..ae5c1aecf0 100644 --- a/tests/regressiontests/modeladmin/models.py +++ b/tests/regressiontests/modeladmin/models.py @@ -291,6 +291,7 @@ blank=True for the model field. Finally, the widget should have the ... form = AdminConcertForm ... model = Concert ... fk_name = 'main_band' +... can_delete = True >>> class BandAdmin(ModelAdmin): ... inlines = [