Loading django/contrib/admin/options.py +6 −1 Original line number Diff line number Diff line Loading @@ -487,6 +487,7 @@ class ModelAdmin(BaseModelAdmin): search_fields = () date_hierarchy = None save_as = False save_as_continue = True save_on_top = False paginator = Paginator preserve_filters = True Loading Loading @@ -1102,7 +1103,11 @@ class ModelAdmin(BaseModelAdmin): 'popup_response_data': popup_response_data, }) elif "_continue" in request.POST: elif "_continue" in request.POST or ( # Redirecting after "Save as new". "_saveasnew" in request.POST and self.save_as_continue and self.has_change_permission(request, obj) ): msg = format_html( _('The {name} "{obj}" was added successfully. You may edit it again below.'), **msg_dict Loading docs/ref/contrib/admin/index.txt +10 −0 Original line number Diff line number Diff line Loading @@ -1146,6 +1146,16 @@ subclass:: By default, ``save_as`` is set to ``False``. .. attribute:: ModelAdmin.save_as_continue .. versionadded:: 1.10 When :attr:`save_as=True <save_as>`, the default redirect after saving the new object is to the change view for that object. If you set ``save_as_continue=False``, the redirect will be to the changelist view. By default, ``save_as_continue`` is set to ``True``. .. attribute:: ModelAdmin.save_on_top Set ``save_on_top`` to add save buttons across the top of your admin change Loading docs/releases/1.10.txt +7 −0 Original line number Diff line number Diff line Loading @@ -753,6 +753,13 @@ Miscellaneous model, you must convert them to attributes or properties as described in :ref:`the deprecation note <user-is-auth-anon-deprecation>`. * When using :attr:`ModelAdmin.save_as=True <django.contrib.admin.ModelAdmin.save_as>`, the "Save as new" button now redirects to the change view for the new object instead of to the model's changelist. If you need the previous behavior, set the new :attr:`ModelAdmin.save_as_continue <django.contrib.admin.ModelAdmin.save_as_continue>` attribute to ``False``. .. _deprecated-features-1.10: Features deprecated in 1.10 Loading tests/admin_views/admin.py +1 −0 Original line number Diff line number Diff line Loading @@ -1013,6 +1013,7 @@ site2.register( list_editable=['parent'], raw_id_fields=['parent'], ) site2.register(Person, save_as_continue=False) site7 = admin.AdminSite(name="admin7") site7.register(Article, ArticleAdmin2) tests/admin_views/tests.py +24 −1 Original line number Diff line number Diff line Loading @@ -1119,9 +1119,23 @@ class SaveAsTests(TestCase): def test_save_as_duplication(self): """Ensure save as actually creates a new person""" post_data = {'_saveasnew': '', 'name': 'John M', 'gender': 1, 'age': 42} self.client.post(reverse('admin:admin_views_person_change', args=(self.per1.pk,)), post_data) response = self.client.post(reverse('admin:admin_views_person_change', args=(self.per1.pk,)), post_data) self.assertEqual(len(Person.objects.filter(name='John M')), 1) self.assertEqual(len(Person.objects.filter(id=self.per1.pk)), 1) new_person = Person.objects.latest('id') self.assertRedirects(response, reverse('admin:admin_views_person_change', args=(new_person.pk,))) def test_save_as_continue_false(self): """ Saving a new object using "Save as new" redirects to the changelist instead of the change view when ModelAdmin.save_as_continue=False. """ post_data = {'_saveasnew': '', 'name': 'John M', 'gender': 1, 'age': 42} url = reverse('admin:admin_views_person_change', args=(self.per1.pk,), current_app=site2.name) response = self.client.post(url, post_data) self.assertEqual(len(Person.objects.filter(name='John M')), 1) self.assertEqual(len(Person.objects.filter(id=self.per1.pk)), 1) self.assertRedirects(response, reverse('admin:admin_views_person_changelist', current_app=site2.name)) def test_save_as_new_with_validation_errors(self): """ Loading Loading @@ -1689,6 +1703,15 @@ class AdminViewPermissionsTest(TestCase): self.assertEqual(post.status_code, 403) self.assertEqual(Article.objects.count(), article_count) # User with both add and change permissions should be redirected to the # change page for the newly created object. article_count = Article.objects.count() self.client.force_login(self.superuser) post = self.client.post(article_change_url, change_dict_save_as_new) self.assertEqual(Article.objects.count(), article_count + 1) new_article = Article.objects.latest('id') self.assertRedirects(post, reverse('admin:admin_views_article_change', args=(new_article.pk,))) def test_delete_view(self): """Delete view should restrict access and actually delete items.""" delete_dict = {'post': 'yes'} Loading Loading
django/contrib/admin/options.py +6 −1 Original line number Diff line number Diff line Loading @@ -487,6 +487,7 @@ class ModelAdmin(BaseModelAdmin): search_fields = () date_hierarchy = None save_as = False save_as_continue = True save_on_top = False paginator = Paginator preserve_filters = True Loading Loading @@ -1102,7 +1103,11 @@ class ModelAdmin(BaseModelAdmin): 'popup_response_data': popup_response_data, }) elif "_continue" in request.POST: elif "_continue" in request.POST or ( # Redirecting after "Save as new". "_saveasnew" in request.POST and self.save_as_continue and self.has_change_permission(request, obj) ): msg = format_html( _('The {name} "{obj}" was added successfully. You may edit it again below.'), **msg_dict Loading
docs/ref/contrib/admin/index.txt +10 −0 Original line number Diff line number Diff line Loading @@ -1146,6 +1146,16 @@ subclass:: By default, ``save_as`` is set to ``False``. .. attribute:: ModelAdmin.save_as_continue .. versionadded:: 1.10 When :attr:`save_as=True <save_as>`, the default redirect after saving the new object is to the change view for that object. If you set ``save_as_continue=False``, the redirect will be to the changelist view. By default, ``save_as_continue`` is set to ``True``. .. attribute:: ModelAdmin.save_on_top Set ``save_on_top`` to add save buttons across the top of your admin change Loading
docs/releases/1.10.txt +7 −0 Original line number Diff line number Diff line Loading @@ -753,6 +753,13 @@ Miscellaneous model, you must convert them to attributes or properties as described in :ref:`the deprecation note <user-is-auth-anon-deprecation>`. * When using :attr:`ModelAdmin.save_as=True <django.contrib.admin.ModelAdmin.save_as>`, the "Save as new" button now redirects to the change view for the new object instead of to the model's changelist. If you need the previous behavior, set the new :attr:`ModelAdmin.save_as_continue <django.contrib.admin.ModelAdmin.save_as_continue>` attribute to ``False``. .. _deprecated-features-1.10: Features deprecated in 1.10 Loading
tests/admin_views/admin.py +1 −0 Original line number Diff line number Diff line Loading @@ -1013,6 +1013,7 @@ site2.register( list_editable=['parent'], raw_id_fields=['parent'], ) site2.register(Person, save_as_continue=False) site7 = admin.AdminSite(name="admin7") site7.register(Article, ArticleAdmin2)
tests/admin_views/tests.py +24 −1 Original line number Diff line number Diff line Loading @@ -1119,9 +1119,23 @@ class SaveAsTests(TestCase): def test_save_as_duplication(self): """Ensure save as actually creates a new person""" post_data = {'_saveasnew': '', 'name': 'John M', 'gender': 1, 'age': 42} self.client.post(reverse('admin:admin_views_person_change', args=(self.per1.pk,)), post_data) response = self.client.post(reverse('admin:admin_views_person_change', args=(self.per1.pk,)), post_data) self.assertEqual(len(Person.objects.filter(name='John M')), 1) self.assertEqual(len(Person.objects.filter(id=self.per1.pk)), 1) new_person = Person.objects.latest('id') self.assertRedirects(response, reverse('admin:admin_views_person_change', args=(new_person.pk,))) def test_save_as_continue_false(self): """ Saving a new object using "Save as new" redirects to the changelist instead of the change view when ModelAdmin.save_as_continue=False. """ post_data = {'_saveasnew': '', 'name': 'John M', 'gender': 1, 'age': 42} url = reverse('admin:admin_views_person_change', args=(self.per1.pk,), current_app=site2.name) response = self.client.post(url, post_data) self.assertEqual(len(Person.objects.filter(name='John M')), 1) self.assertEqual(len(Person.objects.filter(id=self.per1.pk)), 1) self.assertRedirects(response, reverse('admin:admin_views_person_changelist', current_app=site2.name)) def test_save_as_new_with_validation_errors(self): """ Loading Loading @@ -1689,6 +1703,15 @@ class AdminViewPermissionsTest(TestCase): self.assertEqual(post.status_code, 403) self.assertEqual(Article.objects.count(), article_count) # User with both add and change permissions should be redirected to the # change page for the newly created object. article_count = Article.objects.count() self.client.force_login(self.superuser) post = self.client.post(article_change_url, change_dict_save_as_new) self.assertEqual(Article.objects.count(), article_count + 1) new_article = Article.objects.latest('id') self.assertRedirects(post, reverse('admin:admin_views_article_change', args=(new_article.pk,))) def test_delete_view(self): """Delete view should restrict access and actually delete items.""" delete_dict = {'post': 'yes'} Loading