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
This commit is contained in:
Jannis Leidel 2011-07-03 17:56:43 +00:00
parent f9fe112f2d
commit 332a485567
5 changed files with 89 additions and 11 deletions

View File

@ -696,6 +696,18 @@ class ModelAdmin(BaseModelAdmin):
""" """
formset.save() formset.save()
def save_related(self, request, form, formsets, change):
"""
Given the ``HttpRequest``, the parent ``ModelForm`` instance, the
list of inline formsets and a boolean value based on whether the
parent is being added or changed, save the related objects to the
database. Note that at this point save_form() and save_model() have
already been called.
"""
form.save_m2m()
for formset in formsets:
self.save_formset(request, form, formset, change=change)
def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None): def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
opts = self.model._meta opts = self.model._meta
app_label = opts.app_label app_label = opts.app_label
@ -899,11 +911,8 @@ class ModelAdmin(BaseModelAdmin):
prefix=prefix, queryset=inline.queryset(request)) prefix=prefix, queryset=inline.queryset(request))
formsets.append(formset) formsets.append(formset)
if all_valid(formsets) and form_validated: if all_valid(formsets) and form_validated:
self.save_model(request, new_object, form, change=False) self.save_model(request, new_object, form, False)
form.save_m2m() self.save_related(request, form, formsets, False)
for formset in formsets:
self.save_formset(request, form, formset, change=False)
self.log_addition(request, new_object) self.log_addition(request, new_object)
return self.response_add(request, new_object) return self.response_add(request, new_object)
else: else:
@ -1001,11 +1010,8 @@ class ModelAdmin(BaseModelAdmin):
formsets.append(formset) formsets.append(formset)
if all_valid(formsets) and form_validated: if all_valid(formsets) and form_validated:
self.save_model(request, new_object, form, change=True) self.save_model(request, new_object, form, True)
form.save_m2m() self.save_related(request, form, formsets, True)
for formset in formsets:
self.save_formset(request, form, formset, change=True)
change_message = self.construct_change_message(request, form, formsets) change_message = self.construct_change_message(request, form, formsets)
self.log_change(request, new_object, change_message) self.log_change(request, new_object, change_message)
return self.response_change(request, new_object) return self.response_change(request, new_object)

View File

@ -978,6 +978,16 @@ templates used by the :class:`ModelAdmin` views:
else: else:
return ['name'] return ['name']
.. method:: ModelAdmin.save_related(self, request, form, formsets, change)
.. versionadded:: 1.4
The ``save_related`` method is given the ``HttpRequest``, the parent
``ModelForm`` instance, the list of inline formsets and a boolean value
based on whether the parent is being added or changed. Here you can do any
pre- or post-save operations for objects related to the parent. Note
that at this point the parent object and its form have already been saved.
.. method:: ModelAdmin.get_readonly_fields(self, request, obj=None) .. method:: ModelAdmin.get_readonly_fields(self, request, obj=None)
.. versionadded:: 1.2 .. versionadded:: 1.2

View File

@ -80,6 +80,13 @@ to work similarly to how desktop GUIs do it. The new hook
:meth:`~django.contrib.admin.ModelAdmin.get_ordering` for specifying the :meth:`~django.contrib.admin.ModelAdmin.get_ordering` for specifying the
ordering dynamically (e.g. depending on the request) has also been added. ordering dynamically (e.g. depending on the request) has also been added.
``ModelAdmin.save_related()``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A new :meth:`~django.contrib.admin.ModelAdmin.save_related` hook was added to
:mod:`~django.contrib.admin.ModelAdmin` to ease the customization of how
related objects are saved in the admin.
Tools for cryptographic signing Tools for cryptographic signing
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -391,6 +391,14 @@ class ParentAdmin(admin.ModelAdmin):
model = Parent model = Parent
inlines = [ChildInline] inlines = [ChildInline]
def save_related(self, request, form, formsets, change):
super(ParentAdmin, self).save_related(request, form, formsets, change)
first_name, last_name = form.instance.name.split()
for child in form.instance.child_set.all():
if len(child.name.split()) < 2:
child.name = child.name + ' ' + last_name
child.save()
class EmptyModel(models.Model): class EmptyModel(models.Model):
def __unicode__(self): def __unicode__(self):
return "Primary key = %s" % self.id return "Primary key = %s" % self.id

View File

@ -38,7 +38,7 @@ from models import (Article, BarAccount, CustomArticle, EmptyModel,
Category, Post, Plot, FunkyTag, Chapter, Book, Promo, WorkHour, Employee, Category, Post, Plot, FunkyTag, Chapter, Book, Promo, WorkHour, Employee,
Question, Answer, Inquisition, Actor, FoodDelivery, Question, Answer, Inquisition, Actor, FoodDelivery,
RowLevelChangePermissionModel, Paper, CoverLetter, Story, OtherStory, RowLevelChangePermissionModel, Paper, CoverLetter, Story, OtherStory,
ComplexSortedPerson) ComplexSortedPerson, Parent, Child)
class AdminViewBasicTest(TestCase): class AdminViewBasicTest(TestCase):
@ -3113,3 +3113,50 @@ class DateHierarchyTests(TestCase):
self.assert_non_localized_year(response, 2000) self.assert_non_localized_year(response, 2000)
self.assert_non_localized_year(response, 2003) self.assert_non_localized_year(response, 2003)
self.assert_non_localized_year(response, 2005) self.assert_non_localized_year(response, 2005)
class AdminCustomSaveRelatedTests(TestCase):
"""
Ensure that one can easily customize the way related objects are saved.
Refs #16115.
"""
fixtures = ['admin-views-users.xml']
def setUp(self):
self.client.login(username='super', password='secret')
def test_should_be_able_to_edit_related_objects_on_add_view(self):
post = {
'child_set-TOTAL_FORMS': '3',
'child_set-INITIAL_FORMS': '0',
'name': 'Josh Stone',
'child_set-0-name': 'Paul',
'child_set-1-name': 'Catherine',
}
response = self.client.post('/test_admin/admin/admin_views/parent/add/', post)
self.assertEqual(1, Parent.objects.count())
self.assertEqual(2, Child.objects.count())
children_names = list(Child.objects.order_by('name').values_list('name', flat=True))
self.assertEqual('Josh Stone', Parent.objects.latest('id').name)
self.assertEqual([u'Catherine Stone', u'Paul Stone'], children_names)
def test_should_be_able_to_edit_related_objects_on_change_view(self):
parent = Parent.objects.create(name='Josh Stone')
paul = Child.objects.create(parent=parent, name='Paul')
catherine = Child.objects.create(parent=parent, name='Catherine')
post = {
'child_set-TOTAL_FORMS': '5',
'child_set-INITIAL_FORMS': '2',
'name': 'Josh Stone',
'child_set-0-name': 'Paul',
'child_set-0-id': paul.id,
'child_set-1-name': 'Catherine',
'child_set-1-id': catherine.id,
}
response = self.client.post('/test_admin/admin/admin_views/parent/%s/' % parent.id, post)
children_names = list(Child.objects.order_by('name').values_list('name', flat=True))
self.assertEqual('Josh Stone', Parent.objects.latest('id').name)
self.assertEqual([u'Catherine Stone', u'Paul Stone'], children_names)