Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #16115 -- Added ModelAdmin.save_related method to be able to do…

… pre- or post-save operations for objects related to the parent object currently displayed. Thanks, Julien Phalip.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@16498 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 332a485567420e844887344b6429d3b4db4b260b 1 parent f9fe112
Jannis Leidel authored July 03, 2011
26  django/contrib/admin/options.py
@@ -696,6 +696,18 @@ def save_formset(self, request, form, formset, change):
696 696
         """
697 697
         formset.save()
698 698
 
  699
+    def save_related(self, request, form, formsets, change):
  700
+        """
  701
+        Given the ``HttpRequest``, the parent ``ModelForm`` instance, the
  702
+        list of inline formsets and a boolean value based on whether the
  703
+        parent is being added or changed, save the related objects to the
  704
+        database. Note that at this point save_form() and save_model() have
  705
+        already been called.
  706
+        """
  707
+        form.save_m2m()
  708
+        for formset in formsets:
  709
+            self.save_formset(request, form, formset, change=change)
  710
+
699 711
     def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
700 712
         opts = self.model._meta
701 713
         app_label = opts.app_label
@@ -899,11 +911,8 @@ def add_view(self, request, form_url='', extra_context=None):
899 911
                                   prefix=prefix, queryset=inline.queryset(request))
900 912
                 formsets.append(formset)
901 913
             if all_valid(formsets) and form_validated:
902  
-                self.save_model(request, new_object, form, change=False)
903  
-                form.save_m2m()
904  
-                for formset in formsets:
905  
-                    self.save_formset(request, form, formset, change=False)
906  
-
  914
+                self.save_model(request, new_object, form, False)
  915
+                self.save_related(request, form, formsets, False)
907 916
                 self.log_addition(request, new_object)
908 917
                 return self.response_add(request, new_object)
909 918
         else:
@@ -1001,11 +1010,8 @@ def change_view(self, request, object_id, extra_context=None):
1001 1010
                 formsets.append(formset)
1002 1011
 
1003 1012
             if all_valid(formsets) and form_validated:
1004  
-                self.save_model(request, new_object, form, change=True)
1005  
-                form.save_m2m()
1006  
-                for formset in formsets:
1007  
-                    self.save_formset(request, form, formset, change=True)
1008  
-
  1013
+                self.save_model(request, new_object, form, True)
  1014
+                self.save_related(request, form, formsets, True)
1009 1015
                 change_message = self.construct_change_message(request, form, formsets)
1010 1016
                 self.log_change(request, new_object, change_message)
1011 1017
                 return self.response_change(request, new_object)
10  docs/ref/contrib/admin/index.txt
@@ -978,6 +978,16 @@ templates used by the :class:`ModelAdmin` views:
978 978
                 else:
979 979
                     return ['name']
980 980
 
  981
+.. method:: ModelAdmin.save_related(self, request, form, formsets, change)
  982
+
  983
+    .. versionadded:: 1.4
  984
+
  985
+    The ``save_related`` method is given the ``HttpRequest``, the parent
  986
+    ``ModelForm`` instance, the list of inline formsets and a boolean value
  987
+    based on whether the parent is being added or changed. Here you can do any
  988
+    pre- or post-save operations for objects related to the parent. Note
  989
+    that at this point the parent object and its form have already been saved.
  990
+
981 991
 .. method:: ModelAdmin.get_readonly_fields(self, request, obj=None)
982 992
 
983 993
     .. versionadded:: 1.2
7  docs/releases/1.4.txt
@@ -80,6 +80,13 @@ to work similarly to how desktop GUIs do it. The new hook
80 80
 :meth:`~django.contrib.admin.ModelAdmin.get_ordering` for specifying the
81 81
 ordering dynamically (e.g. depending on the request) has also been added.
82 82
 
  83
+``ModelAdmin.save_related()``
  84
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  85
+
  86
+A new :meth:`~django.contrib.admin.ModelAdmin.save_related` hook was added to
  87
+:mod:`~django.contrib.admin.ModelAdmin` to ease the customization of how
  88
+related objects are saved in the admin.
  89
+
83 90
 Tools for cryptographic signing
84 91
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
85 92
 
8  tests/regressiontests/admin_views/models.py
@@ -391,6 +391,14 @@ class ParentAdmin(admin.ModelAdmin):
391 391
     model = Parent
392 392
     inlines = [ChildInline]
393 393
 
  394
+    def save_related(self, request, form, formsets, change):
  395
+        super(ParentAdmin, self).save_related(request, form, formsets, change)
  396
+        first_name, last_name = form.instance.name.split()
  397
+        for child in form.instance.child_set.all():
  398
+            if len(child.name.split()) < 2:
  399
+                child.name = child.name + ' ' + last_name
  400
+                child.save()
  401
+
394 402
 class EmptyModel(models.Model):
395 403
     def __unicode__(self):
396 404
         return "Primary key = %s" % self.id
49  tests/regressiontests/admin_views/tests.py
@@ -38,7 +38,7 @@
38 38
     Category, Post, Plot, FunkyTag, Chapter, Book, Promo, WorkHour, Employee,
39 39
     Question, Answer, Inquisition, Actor, FoodDelivery,
40 40
     RowLevelChangePermissionModel, Paper, CoverLetter, Story, OtherStory,
41  
-    ComplexSortedPerson)
  41
+    ComplexSortedPerson, Parent, Child)
42 42
 
43 43
 
44 44
 class AdminViewBasicTest(TestCase):
@@ -3113,3 +3113,50 @@ def test_multiple_years(self):
3113 3113
             self.assert_non_localized_year(response, 2000)
3114 3114
             self.assert_non_localized_year(response, 2003)
3115 3115
             self.assert_non_localized_year(response, 2005)
  3116
+
  3117
+class AdminCustomSaveRelatedTests(TestCase):
  3118
+    """
  3119
+    Ensure that one can easily customize the way related objects are saved.
  3120
+    Refs #16115.
  3121
+    """
  3122
+    fixtures = ['admin-views-users.xml']
  3123
+
  3124
+    def setUp(self):
  3125
+        self.client.login(username='super', password='secret')
  3126
+
  3127
+    def test_should_be_able_to_edit_related_objects_on_add_view(self):
  3128
+        post = {
  3129
+            'child_set-TOTAL_FORMS': '3',
  3130
+            'child_set-INITIAL_FORMS': '0',
  3131
+            'name': 'Josh Stone',
  3132
+            'child_set-0-name': 'Paul',
  3133
+            'child_set-1-name': 'Catherine',
  3134
+        }
  3135
+        response = self.client.post('/test_admin/admin/admin_views/parent/add/', post)
  3136
+        self.assertEqual(1, Parent.objects.count())
  3137
+        self.assertEqual(2, Child.objects.count())
  3138
+
  3139
+        children_names = list(Child.objects.order_by('name').values_list('name', flat=True))
  3140
+
  3141
+        self.assertEqual('Josh Stone', Parent.objects.latest('id').name)
  3142
+        self.assertEqual([u'Catherine Stone', u'Paul Stone'], children_names)
  3143
+
  3144
+    def test_should_be_able_to_edit_related_objects_on_change_view(self):
  3145
+        parent = Parent.objects.create(name='Josh Stone')
  3146
+        paul = Child.objects.create(parent=parent, name='Paul')
  3147
+        catherine = Child.objects.create(parent=parent, name='Catherine')
  3148
+        post = {
  3149
+            'child_set-TOTAL_FORMS': '5',
  3150
+            'child_set-INITIAL_FORMS': '2',
  3151
+            'name': 'Josh Stone',
  3152
+            'child_set-0-name': 'Paul',
  3153
+            'child_set-0-id': paul.id,
  3154
+            'child_set-1-name': 'Catherine',
  3155
+            'child_set-1-id': catherine.id,
  3156
+        }
  3157
+        response = self.client.post('/test_admin/admin/admin_views/parent/%s/' % parent.id, post)
  3158
+
  3159
+        children_names = list(Child.objects.order_by('name').values_list('name', flat=True))
  3160
+
  3161
+        self.assertEqual('Josh Stone', Parent.objects.latest('id').name)
  3162
+        self.assertEqual([u'Catherine Stone', u'Paul Stone'], children_names)

0 notes on commit 332a485

Please sign in to comment.
Something went wrong with that request. Please try again.