From 6acaa5238668593d6d854b28dbfa65e95796585c Mon Sep 17 00:00:00 2001 From: Greg Chapple Date: Wed, 26 Feb 2014 22:34:19 +0000 Subject: [PATCH] Fixed #22135 -- Added ModelAdmin.get_changeform_initial_data(). Allows custom behavior for setting initial form data in ModelAdmin. By default, initial data is set via GET params. The new method allows this behavior to be overridden. Thanks egasimus for the suggestion. --- django/contrib/admin/options.py | 27 +++++++++++++++++---------- docs/ref/contrib/admin/index.txt | 15 +++++++++++++++ docs/releases/1.7.txt | 4 ++++ tests/admin_views/admin.py | 4 ++++ tests/admin_views/tests.py | 13 +++++++++++++ 5 files changed, 53 insertions(+), 10 deletions(-) diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 6a614b80a03..b4fa54af6cc 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -1308,6 +1308,22 @@ class ModelAdmin(BaseModelAdmin): inline_admin_formsets.append(inline_admin_formset) return inline_admin_formsets + def get_changeform_initial_data(self, request): + """ + Get the initial form data. + Unless overridden, this populates from the GET params. + """ + initial = dict(request.GET.items()) + for k in initial: + try: + f = self.model._meta.get_field(k) + except models.FieldDoesNotExist: + continue + # We have to special-case M2Ms as a list of comma-separated PKs. + if isinstance(f, models.ManyToManyField): + initial[k] = initial[k].split(",") + return initial + @csrf_protect_m @transaction.atomic def changeform_view(self, request, object_id=None, form_url='', extra_context=None): @@ -1358,16 +1374,7 @@ class ModelAdmin(BaseModelAdmin): return self.response_change(request, new_object) else: if add: - # Prepare the dict of initial data from the request. - # We have to special-case M2Ms as a list of comma-separated PKs. - initial = dict(request.GET.items()) - for k in initial: - try: - f = opts.get_field(k) - except models.FieldDoesNotExist: - continue - if isinstance(f, models.ManyToManyField): - initial[k] = initial[k].split(",") + initial = self.get_changeform_initial_data(request) form = ModelForm(initial=initial) formsets, inline_instances = self._create_formsets(request, self.model()) else: diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt index 2c852a3ee4c..f2fdd397140 100644 --- a/docs/ref/contrib/admin/index.txt +++ b/docs/ref/contrib/admin/index.txt @@ -1709,6 +1709,21 @@ templates used by the :class:`ModelAdmin` views: ``obj_display`` is a string with the name of the deleted object. +.. method:: ModelAdmin.get_changeform_initial_data(request) + + .. versionadded:: 1.7 + + A hook for the initial data on admin change forms. By default, fields are + given initial values from ``GET`` parameters. For instance, + ``?name=initial_value`` will set the ``name`` field's initial value to be + ``initial_value``. + + This method should return a dictionary in the form + ``{'fieldname': 'fieldval'}``:: + + def get_changeform_initial_data(self, request): + return {'name': 'custom_initial_value'} + Other methods ~~~~~~~~~~~~~ diff --git a/docs/releases/1.7.txt b/docs/releases/1.7.txt index f9400032e52..305cf6da663 100644 --- a/docs/releases/1.7.txt +++ b/docs/releases/1.7.txt @@ -308,6 +308,10 @@ Minor features ` value by prefixing the ``admin_order_field`` value with a hyphen. +* The :meth:`ModelAdmin.get_changeform_initial_data() + ` method may be + overridden to define custom behavior for setting initial change form data. + :mod:`django.contrib.auth` ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/admin_views/admin.py b/tests/admin_views/admin.py index ae625b6a535..e9c6d4dc4c4 100644 --- a/tests/admin_views/admin.py +++ b/tests/admin_views/admin.py @@ -796,6 +796,10 @@ class RestaurantAdmin(admin.ModelAdmin): inlines = [WorkerInlineAdmin] view_on_site = False + def get_changeform_initial_data(self, request): + return {'name': 'overridden_value'} + + site = admin.AdminSite(name="admin") site.register(Article, ArticleAdmin) site.register(CustomArticle, CustomArticleAdmin) diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py index 382ef56647e..5e4edf69097 100644 --- a/tests/admin_views/tests.py +++ b/tests/admin_views/tests.py @@ -765,6 +765,19 @@ class AdminViewFormUrlTest(TestCase): self.assertTrue('form_url' in response.context, msg='form_url not present in response.context') self.assertEqual(response.context['form_url'], 'pony') + def testInitialDataCanBeOverridden(self): + """ + Tests that the behavior for setting initial + form data can be overridden in the ModelAdmin class. + + Usually, the initial value is set via the GET params. + """ + response = self.client.get('/test_admin/%s/admin_views/restaurant/add/' % self.urlbit, {'name': 'test_value'}) + # this would be the usual behaviour + self.assertNotContains(response, 'value="test_value"') + # this is the overridden behaviour + self.assertContains(response, 'value="overridden_value"') + @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) class AdminJavaScriptTest(TestCase):