kopia lustrzana https://github.com/wagtail/wagtail
Add non-field errors to image add/edit forms
rodzic
8b1699437f
commit
18b8b9d5fc
|
@ -11,15 +11,7 @@
|
|||
|
||||
{% block furniture %}
|
||||
<main class="content-wrapper" role="main">
|
||||
{% if form.non_field_errors %}
|
||||
<div class="messages">
|
||||
<ul>
|
||||
{% for error in form.non_field_errors %}
|
||||
<li class="error">{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% include "wagtailadmin/shared/non_field_errors.html" %}
|
||||
|
||||
<form method="post" novalidate>
|
||||
{% csrf_token %}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
{% if form.non_field_errors %}
|
||||
<div class="messages">
|
||||
<ul>
|
||||
{% for error in form.non_field_errors %}
|
||||
<li class="error">{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
|
@ -5,15 +5,7 @@
|
|||
{% trans "Add redirect" as add_red_str %}
|
||||
{% include "wagtailadmin/shared/header.html" with title=add_red_str icon="redirect" %}
|
||||
|
||||
{% if form.non_field_errors %}
|
||||
<div class="messages">
|
||||
<ul>
|
||||
{% for error in form.non_field_errors %}
|
||||
<li class="error">{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% include "wagtailadmin/shared/non_field_errors.html" %}
|
||||
|
||||
<form action="{% url 'wagtailredirects:add' %}" method="POST" class="nice-padding" novalidate>
|
||||
{% csrf_token %}
|
||||
|
|
|
@ -26,15 +26,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{% if form.non_field_errors %}
|
||||
<div class="messages">
|
||||
<ul>
|
||||
{% for error in form.non_field_errors %}
|
||||
<li class="error">{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% include "wagtailadmin/shared/non_field_errors.html" %}
|
||||
|
||||
<form action="" method="POST" class="nice-padding" novalidate enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
|
|
|
@ -7,15 +7,7 @@
|
|||
{% trans "Confirm import" as header_subtitle %}
|
||||
{% include "wagtailadmin/shared/header.html" with title=header_title subtitle=header_subtitle icon="redirect" %}
|
||||
|
||||
{% if form.non_field_errors %}
|
||||
<div class="messages">
|
||||
<ul>
|
||||
{% for error in form.non_field_errors %}
|
||||
<li class="error">{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% include "wagtailadmin/shared/non_field_errors.html" %}
|
||||
|
||||
<form action="{% url 'wagtailredirects:process_import' %}" method="POST" class="nice-padding" novalidate enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
|
|
|
@ -5,15 +5,7 @@
|
|||
{% trans "Editing" as editing_str %}
|
||||
{% include "wagtailadmin/shared/header.html" with title=editing_str subtitle=redirect.title icon="redirect" %}
|
||||
|
||||
{% if form.non_field_errors %}
|
||||
<div class="messages">
|
||||
<ul>
|
||||
{% for error in form.non_field_errors %}
|
||||
<li class="error">{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% include "wagtailadmin/shared/non_field_errors.html" %}
|
||||
|
||||
<form action="{% url 'wagtailredirects:edit' redirect.id %}" method="POST" class="nice-padding" novalidate>
|
||||
{% csrf_token %}
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
</section>
|
||||
{% if uploadform %}
|
||||
<section id="upload" class="{% if uploadform.errors %}active{% endif %} nice-padding">
|
||||
{% include "wagtailadmin/shared/non_field_errors.html" with form=uploadform %}
|
||||
<form class="image-upload" action="{% url 'wagtailimages:chooser_upload' %}{% if will_select_format %}?select_format=true{% endif %}" method="POST" enctype="multipart/form-data" novalidate>
|
||||
{% csrf_token %}
|
||||
<ul class="fields">
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
{% include "wagtailadmin/shared/header.html" with title=add_str icon="image" %}
|
||||
|
||||
<div class="nice-padding">
|
||||
{% include "wagtailadmin/shared/non_field_errors.html" %}
|
||||
|
||||
<form action="{% url 'wagtailimages:add' %}" method="POST" enctype="multipart/form-data" novalidate>
|
||||
{% csrf_token %}
|
||||
<ul class="fields">
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
{% block content %}
|
||||
{% trans "Editing" as editing_str %}
|
||||
{% include "wagtailadmin/shared/header.html" with title=editing_str subtitle=image.title icon="image" %}
|
||||
{% include "wagtailadmin/shared/non_field_errors.html" %}
|
||||
|
||||
<form action="{% url 'wagtailimages:edit' image.id %}" method="POST" enctype="multipart/form-data" novalidate>
|
||||
{% csrf_token %}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
{% load i18n %}
|
||||
|
||||
{% include "wagtailadmin/shared/non_field_errors.html" %}
|
||||
|
||||
<form action="{{ edit_action }}" method="POST" enctype="multipart/form-data" novalidate>
|
||||
<ul class="fields">
|
||||
{% csrf_token %}
|
||||
|
|
|
@ -331,6 +331,31 @@ class TestImageAddView(TestCase, WagtailTestUtils):
|
|||
image = images.first()
|
||||
self.assertEqual(image.collection, evil_plans_collection)
|
||||
|
||||
@override_settings(WAGTAILIMAGES_IMAGE_MODEL='tests.CustomImage')
|
||||
def test_unique_together_validation_error(self):
|
||||
root_collection = Collection.get_first_root_node()
|
||||
evil_plans_collection = root_collection.add_child(name="Evil plans")
|
||||
|
||||
# another image with a title to collide with
|
||||
CustomImage.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(),
|
||||
collection=evil_plans_collection
|
||||
)
|
||||
|
||||
response = self.post({
|
||||
'title': "Test image",
|
||||
'file': SimpleUploadedFile('test.png', get_test_image_file().file.getvalue()),
|
||||
'collection': evil_plans_collection.id,
|
||||
})
|
||||
|
||||
# Shouldn't redirect anywhere
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, 'wagtailimages/images/add.html')
|
||||
|
||||
# error message should be output on the page as a non-field error
|
||||
self.assertContains(response, "Custom image with this Title and Collection already exists.")
|
||||
|
||||
|
||||
class TestImageAddViewWithLimitedCollectionPermissions(TestCase, WagtailTestUtils):
|
||||
def setUp(self):
|
||||
|
@ -617,6 +642,36 @@ class TestImageEditView(TestCase, WagtailTestUtils):
|
|||
response = self.client.get(reverse('wagtailimages:edit', args=(large_image.id,)))
|
||||
self.assertContains(response, 'data-original-width="1024"')
|
||||
|
||||
@override_settings(WAGTAILIMAGES_IMAGE_MODEL='tests.CustomImage')
|
||||
def test_unique_together_validation_error(self):
|
||||
root_collection = Collection.get_first_root_node()
|
||||
evil_plans_collection = root_collection.add_child(name="Evil plans")
|
||||
|
||||
# Create an image to edit
|
||||
self.image = CustomImage.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
|
||||
# another image with a title to collide with
|
||||
CustomImage.objects.create(
|
||||
title="Edited",
|
||||
file=get_test_image_file(),
|
||||
collection=evil_plans_collection
|
||||
)
|
||||
|
||||
response = self.post({
|
||||
'title': "Edited",
|
||||
'collection': evil_plans_collection.id,
|
||||
})
|
||||
|
||||
# Shouldn't redirect anywhere
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, 'wagtailimages/images/edit.html')
|
||||
|
||||
# error message should be output on the page as a non-field error
|
||||
self.assertContains(response, "Custom image with this Title and Collection already exists.")
|
||||
|
||||
|
||||
@override_settings(WAGTAILIMAGES_IMAGE_MODEL='tests.CustomImage')
|
||||
class TestImageEditViewWithCustomImageModel(TestCase, WagtailTestUtils):
|
||||
|
@ -972,6 +1027,30 @@ class TestImageChooserUploadView(TestCase, WagtailTestUtils):
|
|||
# Check that the image was created
|
||||
self.assertTrue(Image.objects.filter(title="Test image").exists())
|
||||
|
||||
@override_settings(WAGTAILIMAGES_IMAGE_MODEL='tests.CustomImage')
|
||||
def test_unique_together_validation(self):
|
||||
root_collection = Collection.get_first_root_node()
|
||||
evil_plans_collection = root_collection.add_child(name="Evil plans")
|
||||
# another image with a title to collide with
|
||||
CustomImage.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(),
|
||||
collection=evil_plans_collection
|
||||
)
|
||||
|
||||
response = self.client.post(reverse('wagtailimages:chooser_upload'), {
|
||||
'image-chooser-upload-title': "Test image",
|
||||
'image-chooser-upload-file': SimpleUploadedFile('test.png', get_test_image_file().file.getvalue()),
|
||||
'image-chooser-upload-collection': evil_plans_collection.id
|
||||
})
|
||||
|
||||
# Shouldn't redirect anywhere
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, 'wagtailimages/chooser/chooser.html')
|
||||
|
||||
# The form should have an error
|
||||
self.assertContains(response, "Custom image with this Title and Collection already exists.")
|
||||
|
||||
|
||||
class TestImageChooserUploadViewWithLimitedPermissions(TestCase, WagtailTestUtils):
|
||||
def setUp(self):
|
||||
|
@ -1307,7 +1386,7 @@ class TestMultipleImageUploaderWithCustomImageModel(TestCase, WagtailTestUtils):
|
|||
|
||||
# Create an image for running tests on
|
||||
self.image = CustomImage.objects.create(
|
||||
title="Test image",
|
||||
title="test-image.png",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
|
||||
|
@ -1381,6 +1460,36 @@ class TestMultipleImageUploaderWithCustomImageModel(TestCase, WagtailTestUtils):
|
|||
response_json['error_message'], 'Upload a valid image. The file you uploaded was either not an image or a corrupted image.'
|
||||
)
|
||||
|
||||
def test_unique_together_validation_error(self):
|
||||
"""
|
||||
If unique_together validation fails, create an UploadedImage and return a form so the
|
||||
user can fix it
|
||||
"""
|
||||
root_collection = Collection.get_first_root_node()
|
||||
new_collection = root_collection.add_child(name="holiday snaps")
|
||||
self.image.collection = new_collection
|
||||
self.image.save()
|
||||
|
||||
image_count_before = CustomImage.objects.count()
|
||||
uploaded_image_count_before = UploadedImage.objects.count()
|
||||
|
||||
response = self.client.post(reverse('wagtailimages:add_multiple'), {
|
||||
'files[]': SimpleUploadedFile('test-image.png', get_test_image_file().file.getvalue()),
|
||||
'collection': new_collection.id,
|
||||
}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||
|
||||
image_count_after = CustomImage.objects.count()
|
||||
uploaded_image_count_after = UploadedImage.objects.count()
|
||||
|
||||
# an UploadedImage should have been created now, but not a CustomImage
|
||||
self.assertEqual(image_count_after, image_count_before)
|
||||
self.assertEqual(uploaded_image_count_after, uploaded_image_count_before + 1)
|
||||
|
||||
# Check response
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response['Content-Type'], 'application/json')
|
||||
self.assertTemplateUsed(response, 'wagtailimages/multiple/edit_form.html')
|
||||
|
||||
def test_edit_post(self):
|
||||
"""
|
||||
This tests that a POST request to the edit view edits the image
|
||||
|
@ -1410,6 +1519,40 @@ class TestMultipleImageUploaderWithCustomImageModel(TestCase, WagtailTestUtils):
|
|||
self.assertEqual(new_image.caption, "a boot stamping on a human face, forever")
|
||||
self.assertIn('footwear', new_image.tags.names())
|
||||
|
||||
def test_edit_fails_unique_together_validation(self):
|
||||
"""
|
||||
Check that the form returned on failing a unique-together validation error
|
||||
includes that error message, despite it being a non-field error
|
||||
"""
|
||||
root_collection = Collection.get_first_root_node()
|
||||
new_collection = root_collection.add_child(name="holiday snaps")
|
||||
# create another image for the edited title to collide with
|
||||
CustomImage.objects.create(
|
||||
title="The Eiffel Tower",
|
||||
file=get_test_image_file(),
|
||||
collection=new_collection
|
||||
)
|
||||
|
||||
response = self.client.post(reverse('wagtailimages:edit_multiple', args=(self.image.id, )), {
|
||||
('image-%d-title' % self.image.id): "The Eiffel Tower",
|
||||
('image-%d-collection' % self.image.id): new_collection.id,
|
||||
('image-%d-tags' % self.image.id): "",
|
||||
('image-%d-caption' % self.image.id): "ooh la la",
|
||||
}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||
|
||||
# Check response
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response['Content-Type'], 'application/json')
|
||||
self.assertTemplateUsed(response, 'wagtailimages/multiple/edit_form.html')
|
||||
|
||||
response_json = json.loads(response.content.decode())
|
||||
# Check JSON
|
||||
self.assertEqual(response_json['image_id'], self.image.id)
|
||||
self.assertFalse(response_json['success'])
|
||||
|
||||
# Check that a form error was raised
|
||||
self.assertIn("Custom image with this Title and Collection already exists.", response_json['form'])
|
||||
|
||||
def test_delete_post(self):
|
||||
"""
|
||||
This tests that a POST request to the delete view deletes the image
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
# Generated by Django 2.2.13 on 2020-07-01 14:25
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wagtailcore', '0046_site_name_remove_null'),
|
||||
('tests', '0051_tag_verbose_name'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='customdocument',
|
||||
options={},
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='customdocument',
|
||||
unique_together={('title', 'collection')},
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='customimage',
|
||||
unique_together={('title', 'collection')},
|
||||
),
|
||||
]
|
|
@ -923,6 +923,11 @@ class CustomImage(AbstractImage):
|
|||
'fancy_caption',
|
||||
)
|
||||
|
||||
class Meta:
|
||||
unique_together = [
|
||||
('title', 'collection')
|
||||
]
|
||||
|
||||
|
||||
class CustomRendition(AbstractRendition):
|
||||
image = models.ForeignKey(CustomImage, related_name='renditions', on_delete=models.CASCADE)
|
||||
|
@ -959,6 +964,11 @@ class CustomDocument(AbstractDocument):
|
|||
'fancy_description'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
unique_together = [
|
||||
('title', 'collection')
|
||||
]
|
||||
|
||||
|
||||
class StreamModel(models.Model):
|
||||
body = StreamField([
|
||||
|
|
Ładowanie…
Reference in New Issue