summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Zapletal <adamzap@gmail.com>2015-04-24 20:31:26 -0500
committerTim Graham <timograham@gmail.com>2015-05-02 19:41:10 -0400
commit0894643e40327e48397573b7844585618200442b (patch)
tree12d7083c0551103a0fa90191cc832368fe2e1a3d
parenta2c3c2a1e26be4451c5f28e60725bd36e1cb2b5c (diff)
Fixed #23387 -- Kept "Save as new" button after validation errors in admin.
When "Save as new" is chosen and errors occur, only show the "Save as new" button and not the other save buttons. Thanks to Tino de Bruijn for doing the real work on this fix.
-rw-r--r--django/contrib/admin/options.py13
-rw-r--r--django/contrib/admin/templatetags/admin_modify.py6
-rw-r--r--tests/admin_views/admin.py1
-rw-r--r--tests/admin_views/models.py9
-rw-r--r--tests/admin_views/tests.py61
5 files changed, 75 insertions, 15 deletions
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index 50e7227cd0..5f150aca2b 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -1342,9 +1342,8 @@ class ModelAdmin(BaseModelAdmin):
'name': force_text(opts.verbose_name), 'key': escape(object_id)})
if request.method == 'POST' and "_saveasnew" in request.POST:
- return self.add_view(request, form_url=reverse('admin:%s_%s_add' % (
- opts.app_label, opts.model_name),
- current_app=self.admin_site.name))
+ object_id = None
+ obj = None
ModelForm = self.get_form(request, obj)
if request.method == 'POST':
@@ -1366,6 +1365,8 @@ class ModelAdmin(BaseModelAdmin):
else:
self.log_change(request, new_object, change_message)
return self.response_change(request, new_object)
+ else:
+ form_validated = False
else:
if add:
initial = self.get_changeform_initial_data(request)
@@ -1401,6 +1402,12 @@ class ModelAdmin(BaseModelAdmin):
preserved_filters=self.get_preserved_filters(request),
)
+ # Hide the "Save" and "Save and continue" buttons if "Save as New" was
+ # previously chosen to prevent the interface from getting confusing.
+ if request.method == 'POST' and not form_validated and "_saveasnew" in request.POST:
+ context['show_save'] = False
+ context['show_save_and_continue'] = False
+
context.update(extra_context or {})
return self.render_change_form(request, context, add=add, change=not add, obj=obj, form_url=form_url)
diff --git a/django/contrib/admin/templatetags/admin_modify.py b/django/contrib/admin/templatetags/admin_modify.py
index 64fb4ebce7..30f9ac4a15 100644
--- a/django/contrib/admin/templatetags/admin_modify.py
+++ b/django/contrib/admin/templatetags/admin_modify.py
@@ -30,6 +30,8 @@ def submit_row(context):
change = context['change']
is_popup = context['is_popup']
save_as = context['save_as']
+ show_save = context.get('show_save', True)
+ show_save_and_continue = context.get('show_save_and_continue', True)
ctx = {
'opts': opts,
'show_delete_link': (
@@ -41,9 +43,9 @@ def submit_row(context):
context['has_add_permission'] and not is_popup and
(not save_as or context['add'])
),
- 'show_save_and_continue': not is_popup and context['has_change_permission'],
+ 'show_save_and_continue': not is_popup and context['has_change_permission'] and show_save_and_continue,
'is_popup': is_popup,
- 'show_save': True,
+ 'show_save': show_save,
'preserved_filters': context.get('preserved_filters'),
}
if context.get('original') is not None:
diff --git a/tests/admin_views/admin.py b/tests/admin_views/admin.py
index d070e5a55e..aa040138e0 100644
--- a/tests/admin_views/admin.py
+++ b/tests/admin_views/admin.py
@@ -288,6 +288,7 @@ class ChildInline(admin.StackedInline):
class ParentAdmin(admin.ModelAdmin):
model = Parent
inlines = [ChildInline]
+ save_as = True
list_editable = ('name',)
diff --git a/tests/admin_views/models.py b/tests/admin_views/models.py
index 845d3d9ee2..0be371fa7c 100644
--- a/tests/admin_views/models.py
+++ b/tests/admin_views/models.py
@@ -10,6 +10,7 @@ from django.contrib.contenttypes.fields import (
GenericForeignKey, GenericRelation,
)
from django.contrib.contenttypes.models import ContentType
+from django.core.exceptions import ValidationError
from django.core.files.storage import FileSystemStorage
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
@@ -311,11 +312,19 @@ class Vodcast(Media):
class Parent(models.Model):
name = models.CharField(max_length=128)
+ def clean(self):
+ if self.name == '_invalid':
+ raise ValidationError('invalid')
+
class Child(models.Model):
parent = models.ForeignKey(Parent, editable=False)
name = models.CharField(max_length=30, blank=True)
+ def clean(self):
+ if self.name == '_invalid':
+ raise ValidationError('invalid')
+
@python_2_unicode_compatible
class EmptyModel(models.Model):
diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py
index 8ead8ee93c..93c3a7a710 100644
--- a/tests/admin_views/tests.py
+++ b/tests/admin_views/tests.py
@@ -1151,18 +1151,59 @@ class SaveAsTests(TestCase):
self.assertEqual(len(Person.objects.filter(name='John M')), 1)
self.assertEqual(len(Person.objects.filter(id=self.per1.pk)), 1)
- def test_save_as_display(self):
+ def test_save_as_new_with_validation_errors(self):
"""
- Ensure that 'save as' is displayed when activated and after submitting
- invalid data aside save_as_new will not show us a form to overwrite the
- initial model.
+ Ensure that when you click "Save as new" and have a validation error,
+ you only see the "Save as new" button and not the other save buttons,
+ and that only the "Save as" button is visible.
"""
- change_url = reverse('admin:admin_views_person_change', args=(self.per1.pk,))
- response = self.client.get(change_url)
- self.assertTrue(response.context['save_as'])
- post_data = {'_saveasnew': '', 'name': 'John M', 'gender': 3, 'alive': 'checked'}
- response = self.client.post(change_url, post_data)
- self.assertEqual(response.context['form_url'], reverse('admin:admin_views_person_add'))
+ response = self.client.post(reverse('admin:admin_views_person_change', args=(self.per1.pk,)), {
+ '_saveasnew': '',
+ 'gender': 'invalid',
+ '_addanother': 'fail',
+ })
+ self.assertContains(response, 'Please correct the errors below.')
+ self.assertFalse(response.context['show_save_and_add_another'])
+ self.assertFalse(response.context['show_save_and_continue'])
+ self.assertTrue(response.context['show_save_as_new'])
+
+ def test_save_as_new_with_validation_errors_with_inlines(self):
+ parent = Parent.objects.create(name='Father')
+ child = Child.objects.create(parent=parent, name='Child')
+ response = self.client.post(reverse('admin:admin_views_parent_change', args=(parent.pk,)), {
+ '_saveasnew': 'Save as new',
+ 'child_set-0-parent': parent.pk,
+ 'child_set-0-id': child.pk,
+ 'child_set-0-name': 'Child',
+ 'child_set-INITIAL_FORMS': 1,
+ 'child_set-MAX_NUM_FORMS': 1000,
+ 'child_set-MIN_NUM_FORMS': 0,
+ 'child_set-TOTAL_FORMS': 4,
+ 'name': '_invalid',
+ })
+ self.assertContains(response, 'Please correct the error below.')
+ self.assertFalse(response.context['show_save_and_add_another'])
+ self.assertFalse(response.context['show_save_and_continue'])
+ self.assertTrue(response.context['show_save_as_new'])
+
+ def test_save_as_new_with_inlines_with_validation_errors(self):
+ parent = Parent.objects.create(name='Father')
+ child = Child.objects.create(parent=parent, name='Child')
+ response = self.client.post(reverse('admin:admin_views_parent_change', args=(parent.pk,)), {
+ '_saveasnew': 'Save as new',
+ 'child_set-0-parent': parent.pk,
+ 'child_set-0-id': child.pk,
+ 'child_set-0-name': '_invalid',
+ 'child_set-INITIAL_FORMS': 1,
+ 'child_set-MAX_NUM_FORMS': 1000,
+ 'child_set-MIN_NUM_FORMS': 0,
+ 'child_set-TOTAL_FORMS': 4,
+ 'name': 'Father',
+ })
+ self.assertContains(response, 'Please correct the error below.')
+ self.assertFalse(response.context['show_save_and_add_another'])
+ self.assertFalse(response.context['show_save_and_continue'])
+ self.assertTrue(response.context['show_save_as_new'])
@override_settings(ROOT_URLCONF="admin_views.urls")