diff --git a/django/contrib/admin/helpers.py b/django/contrib/admin/helpers.py index 0055e83190d..0c2f287fe54 100644 --- a/django/contrib/admin/helpers.py +++ b/django/contrib/admin/helpers.py @@ -193,7 +193,8 @@ class InlineAdminFormSet(object): """ A wrapper around an inline formset for use in the admin system. """ - def __init__(self, inline, formset, fieldsets, readonly_fields=None, model_admin=None): + def __init__(self, inline, formset, fieldsets, prepopulated_fields=None, + readonly_fields=None, model_admin=None): self.opts = inline self.formset = formset self.fieldsets = fieldsets @@ -201,18 +202,21 @@ class InlineAdminFormSet(object): if readonly_fields is None: readonly_fields = () self.readonly_fields = readonly_fields + if prepopulated_fields is None: + prepopulated_fields = {} + self.prepopulated_fields = prepopulated_fields def __iter__(self): for form, original in zip(self.formset.initial_forms, self.formset.get_queryset()): yield InlineAdminForm(self.formset, form, self.fieldsets, - self.opts.prepopulated_fields, original, self.readonly_fields, + self.prepopulated_fields, original, self.readonly_fields, model_admin=self.opts) for form in self.formset.extra_forms: yield InlineAdminForm(self.formset, form, self.fieldsets, - self.opts.prepopulated_fields, None, self.readonly_fields, + self.prepopulated_fields, None, self.readonly_fields, model_admin=self.opts) yield InlineAdminForm(self.formset, self.formset.empty_form, - self.fieldsets, self.opts.prepopulated_fields, None, + self.fieldsets, self.prepopulated_fields, None, self.readonly_fields, model_admin=self.opts) def fields(self): diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index fbda8b7a35b..d08e22b77a5 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -189,8 +189,17 @@ class BaseModelAdmin(object): declared_fieldsets = property(_declared_fieldsets) def get_readonly_fields(self, request, obj=None): + """ + Hook for specifying custom readonly fields. + """ return self.readonly_fields + def get_prepopulated_fields(self, request, obj=None): + """ + Hook for specifying custom prepopulated fields. + """ + return self.prepopulated_fields + def queryset(self, request): """ Returns a QuerySet of all model instances that can be edited by the @@ -909,7 +918,8 @@ class ModelAdmin(BaseModelAdmin): formsets.append(formset) adminForm = helpers.AdminForm(form, list(self.get_fieldsets(request)), - self.prepopulated_fields, self.get_readonly_fields(request), + self.get_prepopulated_fields(request), + self.get_readonly_fields(request), model_admin=self) media = self.media + adminForm.media @@ -917,8 +927,9 @@ class ModelAdmin(BaseModelAdmin): for inline, formset in zip(self.inline_instances, formsets): fieldsets = list(inline.get_fieldsets(request)) readonly = list(inline.get_readonly_fields(request)) + prepopulated = dict(inline.get_prepopulated_fields(request)) inline_admin_formset = helpers.InlineAdminFormSet(inline, formset, - fieldsets, readonly, model_admin=self) + fieldsets, prepopulated, readonly, model_admin=self) inline_admin_formsets.append(inline_admin_formset) media = media + inline_admin_formset.media @@ -1000,7 +1011,8 @@ class ModelAdmin(BaseModelAdmin): formsets.append(formset) adminForm = helpers.AdminForm(form, self.get_fieldsets(request, obj), - self.prepopulated_fields, self.get_readonly_fields(request, obj), + self.get_prepopulated_fields(request, obj), + self.get_readonly_fields(request, obj), model_admin=self) media = self.media + adminForm.media @@ -1008,8 +1020,9 @@ class ModelAdmin(BaseModelAdmin): for inline, formset in zip(self.inline_instances, formsets): fieldsets = list(inline.get_fieldsets(request, obj)) readonly = list(inline.get_readonly_fields(request, obj)) + prepopulated = dict(inline.get_prepopulated_fields(request, obj)) inline_admin_formset = helpers.InlineAdminFormSet(inline, formset, - fieldsets, readonly, model_admin=self) + fieldsets, prepopulated, readonly, model_admin=self) inline_admin_formsets.append(inline_admin_formset) media = media + inline_admin_formset.media diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt index 415e1fe172b..f97d4eb2606 100644 --- a/docs/ref/contrib/admin/index.txt +++ b/docs/ref/contrib/admin/index.txt @@ -839,6 +839,15 @@ templates used by the :class:`ModelAdmin` views: a ``list`` or ``tuple`` of field names that will be displayed as read-only, as described above in the :attr:`ModelAdmin.readonly_fields` section. +.. method:: ModelAdmin.get_prepopulated_fields(self, request, obj=None) + + .. versionadded:: 1.4 + + The ``get_prepopulated_fields`` method is given the ``HttpRequest`` and the + ``obj`` being edited (or ``None`` on an add form) and is expected to return + a ``dictionary``, as described above in the :attr:`ModelAdmin.prepopulated_fields` + section. + .. method:: ModelAdmin.get_urls(self) The ``get_urls`` method on a ``ModelAdmin`` returns the URLs to be used for diff --git a/tests/regressiontests/admin_views/fixtures/admin-views-users.xml b/tests/regressiontests/admin_views/fixtures/admin-views-users.xml index f1ff2961a17..1c85e1c9097 100644 --- a/tests/regressiontests/admin_views/fixtures/admin-views-users.xml +++ b/tests/regressiontests/admin_views/fixtures/admin-views-users.xml @@ -88,6 +88,9 @@ 2009-03-18 11:54:58 1 - - - \ No newline at end of file + + A Long Title + True + a-long-title + + diff --git a/tests/regressiontests/admin_views/models.py b/tests/regressiontests/admin_views/models.py index b65f8a4b37b..97f6708e406 100644 --- a/tests/regressiontests/admin_views/models.py +++ b/tests/regressiontests/admin_views/models.py @@ -529,6 +529,51 @@ class LinkInline(admin.TabularInline): readonly_fields = ("posted",) +class PrePopulatedPost(models.Model): + title = models.CharField(max_length=100) + published = models.BooleanField() + slug = models.SlugField() + +class PrePopulatedSubPost(models.Model): + post = models.ForeignKey(PrePopulatedPost) + subtitle = models.CharField(max_length=100) + subslug = models.SlugField() + +class SubPostInline(admin.TabularInline): + model = PrePopulatedSubPost + + prepopulated_fields = { + 'subslug' : ('subtitle',) + } + + def get_readonly_fields(self, request, obj=None): + if obj and obj.published: + return ('subslug',) + return self.readonly_fields + + def get_prepopulated_fields(self, request, obj=None): + if obj and obj.published: + return {} + return self.prepopulated_fields + +class PrePopulatedPostAdmin(admin.ModelAdmin): + list_display = ['title', 'slug'] + prepopulated_fields = { + 'slug' : ('title',) + } + + inlines = [SubPostInline] + + def get_readonly_fields(self, request, obj=None): + if obj and obj.published: + return ('slug',) + return self.readonly_fields + + def get_prepopulated_fields(self, request, obj=None): + if obj and obj.published: + return {} + return self.prepopulated_fields + class Post(models.Model): title = models.CharField(max_length=100, help_text="Some help text for the title (with unicode ŠĐĆŽćžšđ)") content = models.TextField(help_text="Some help text for the content (with unicode ŠĐĆŽćžšđ)") @@ -818,3 +863,4 @@ admin.site.register(Topping) admin.site.register(Album, AlbumAdmin) admin.site.register(Question) admin.site.register(Answer) +admin.site.register(PrePopulatedPost, PrePopulatedPostAdmin) diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py index e3727a849db..9b1a73803f5 100644 --- a/tests/regressiontests/admin_views/tests.py +++ b/tests/regressiontests/admin_views/tests.py @@ -2579,6 +2579,30 @@ class NeverCacheTests(TestCase): self.assertEqual(get_max_age(response), None) +class PrePopulatedTest(TestCase): + fixtures = ['admin-views-users.xml'] + + def setUp(self): + self.client.login(username='super', password='secret') + + def tearDown(self): + self.client.logout() + + def test_prepopulated_on(self): + response = self.client.get('/test_admin/admin/admin_views/prepopulatedpost/add/') + self.assertEqual(response.status_code, 200) + self.assertContains(response, "id: '#id_slug',") + self.assertContains(response, "field['dependency_ids'].push('#id_title');") + self.assertContains(response, "id: '#id_prepopulatedsubpost_set-0-subslug',") + + def test_prepopulated_off(self): + response = self.client.get('/test_admin/admin/admin_views/prepopulatedpost/1/') + self.assertEqual(response.status_code, 200) + self.assertContains(response, "A Long Title") + self.assertNotContains(response, "id: '#id_slug'") + self.assertNotContains(response, "field['dependency_ids'].push('#id_title');") + self.assertNotContains(response, "id: '#id_prepopulatedsubpost_set-0-subslug',") + class ReadonlyTest(TestCase): fixtures = ['admin-views-users.xml']