Ticket #16115: 16115.modeladmin-save-related.diff

File 16115.modeladmin-save-related.diff, 7.8 KB (added by Julien Phalip, 6 years ago)
  • django/contrib/admin/options.py

    diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
    index c603210..a8ab3f3 100644
    a b class ModelAdmin(BaseModelAdmin): 
    683683        """
    684684        formset.save()
    685685
     686    def save_related(self, request, form, formsets, change):
     687        """
     688        Given given the ``HttpRequest``, the parent ``ModelForm`` instance,
     689        the list of inline formsets and a boolean value based on whether the
     690        parent is being added or changed, save the related objects to the
     691        database. Note that at this point save_form() and save_model() have
     692        already been called.
     693        """
     694        form.save_m2m()
     695        for formset in formsets:
     696            self.save_formset(request, form, formset, change=change)
     697
    686698    def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
    687699        opts = self.model._meta
    688700        app_label = opts.app_label
    class ModelAdmin(BaseModelAdmin): 
    886898                                  prefix=prefix, queryset=inline.queryset(request))
    887899                formsets.append(formset)
    888900            if all_valid(formsets) and form_validated:
    889                 self.save_model(request, new_object, form, change=False)
    890                 form.save_m2m()
    891                 for formset in formsets:
    892                     self.save_formset(request, form, formset, change=False)
    893 
     901                self.save_model(request, new_object, form, False)
     902                self.save_related(request, form, formsets, False)
    894903                self.log_addition(request, new_object)
    895904                return self.response_add(request, new_object)
    896905        else:
    class ModelAdmin(BaseModelAdmin): 
    988997                formsets.append(formset)
    989998
    990999            if all_valid(formsets) and form_validated:
    991                 self.save_model(request, new_object, form, change=True)
    992                 form.save_m2m()
    993                 for formset in formsets:
    994                     self.save_formset(request, form, formset, change=True)
    995 
     1000                self.save_model(request, new_object, form, True)
     1001                self.save_related(request, form, formsets, True)
    9961002                change_message = self.construct_change_message(request, form, formsets)
    9971003                self.log_change(request, new_object, change_message)
    9981004                return self.response_change(request, new_object)
  • docs/ref/contrib/admin/index.txt

    diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt
    index e5aecb4..e8acae5 100644
    a b templates used by the :class:`ModelAdmin` views: 
    949949                    instance.save()
    950950                formset.save_m2m()
    951951
     952.. method:: ModelAdmin.save_related(self, request, form, formsets, change)
     953
     954    .. versionadded:: 1.4
     955
     956    The ``save_related`` method is given the ``HttpRequest``, the parent
     957    ``ModelForm`` instance, the list of inline formsets and a boolean value
     958    based on whether the parent is being added or changed. Here you can do any
     959    pre- or post-save operations for objects related to the parent. Note
     960    that at this point the parent object and its form have already been saved.
     961
    952962.. method:: ModelAdmin.get_readonly_fields(self, request, obj=None)
    953963
    954964    .. versionadded:: 1.2
  • docs/releases/1.4.txt

    diff --git a/docs/releases/1.4.txt b/docs/releases/1.4.txt
    index 8742103..35a869d 100644
    a b not custom filters. This has been rectified with a simple API previously 
    4646known as "FilterSpec" which was used internally. For more details, see the
    4747documentation for :attr:`~django.contrib.admin.ModelAdmin.list_filter`.
    4848
     49``ModelAdmin.save_related()``
     50~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     51
     52A new :meth:`~django.contrib.admin.ModelAdmin.save_related` hook was added to
     53:mod:`~django.contrib.admin.ModelAdmin` to ease the customization of how
     54related objects are saved in the admin.
     55
    4956Tools for cryptographic signing
    5057~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    5158
  • tests/regressiontests/admin_views/models.py

    diff --git a/tests/regressiontests/admin_views/models.py b/tests/regressiontests/admin_views/models.py
    index 854fb60..689ae0c 100644
    a b class ChildInline(admin.StackedInline): 
    386386class ParentAdmin(admin.ModelAdmin):
    387387    model = Parent
    388388    inlines = [ChildInline]
     389   
     390    def save_related(self, request, form, formsets, change):
     391        super(ParentAdmin, self).save_related(request, form, formsets, change)
     392        first_name, last_name = form.instance.name.split()
     393        for child in form.instance.child_set.all():
     394            if len(child.name.split()) < 2:
     395                child.name = child.name + ' ' + last_name
     396                child.save()
    389397
    390398class EmptyModel(models.Model):
    391399    def __unicode__(self):
  • tests/regressiontests/admin_views/tests.py

    diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py
    index 54d89e4..03db634 100644
    a b from models import (Article, BarAccount, CustomArticle, EmptyModel, 
    3737    Language, Collector, Widget, Grommet, DooHickey, FancyDoodad, Whatsit,
    3838    Category, Post, Plot, FunkyTag, Chapter, Book, Promo, WorkHour, Employee,
    3939    Question, Answer, Inquisition, Actor, FoodDelivery,
    40     RowLevelChangePermissionModel, Paper, CoverLetter, Story, OtherStory)
     40    RowLevelChangePermissionModel, Paper, CoverLetter, Story, OtherStory,
     41    Parent, Child)
    4142
    4243
    4344class AdminViewBasicTest(TestCase):
    class DateHierarchyTests(TestCase): 
    30013002            self.assert_non_localized_year(response, 2000)
    30023003            self.assert_non_localized_year(response, 2003)
    30033004            self.assert_non_localized_year(response, 2005)
     3005
     3006class AdminCustomSaveRelatedTests(TestCase):
     3007    """
     3008    Ensure that one can easily customise the way related objects are saved.
     3009    Refs #16115.
     3010    """
     3011    fixtures = ['admin-views-users.xml']
     3012   
     3013    def setUp(self):
     3014        self.client.login(username='super', password='secret')
     3015   
     3016    def test_should_be_able_to_edit_related_objects_on_add_view(self):
     3017        post = {
     3018            'child_set-TOTAL_FORMS': '3',
     3019            'child_set-INITIAL_FORMS': '0',
     3020            'name': 'Josh Stone',
     3021            'child_set-0-name': 'Paul',
     3022            'child_set-1-name': 'Catherine',
     3023        }
     3024        response = self.client.post('/test_admin/admin/admin_views/parent/add/', post)
     3025        self.assertEqual(1, Parent.objects.count())
     3026        self.assertEqual(2, Child.objects.count())
     3027       
     3028        children_names = list(Child.objects.order_by('name').values_list('name', flat=True))
     3029       
     3030        self.assertEqual('Josh Stone', Parent.objects.latest('id').name)
     3031        self.assertEqual([u'Catherine Stone', u'Paul Stone'], children_names)
     3032   
     3033    def test_should_be_able_to_edit_related_objects_on_change_view(self):
     3034        parent = Parent.objects.create(name='Josh Stone')
     3035        paul = Child.objects.create(parent=parent, name='Paul')
     3036        catherine = Child.objects.create(parent=parent, name='Catherine')
     3037        post = {
     3038            'child_set-TOTAL_FORMS': '5',
     3039            'child_set-INITIAL_FORMS': '2',
     3040            'name': 'Josh Stone',
     3041            'child_set-0-name': 'Paul',
     3042            'child_set-0-id': paul.id,
     3043            'child_set-1-name': 'Catherine',
     3044            'child_set-1-id': catherine.id,
     3045        }
     3046        response = self.client.post('/test_admin/admin/admin_views/parent/%s/' % parent.id, post)
     3047       
     3048        children_names = list(Child.objects.order_by('name').values_list('name', flat=True))
     3049       
     3050        self.assertEqual('Josh Stone', Parent.objects.latest('id').name)
     3051        self.assertEqual([u'Catherine Stone', u'Paul Stone'], children_names)
Back to Top