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
+
+
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']