From 5328a2d98af87ebdc53fe27d2d79f5d2ffd62104 Mon Sep 17 00:00:00 2001 From: Karl Hobley Date: Thu, 11 Sep 2014 10:10:43 +0100 Subject: [PATCH 1/2] Added failing tests for #613 --- wagtail/wagtaildocs/tests.py | 104 +++++++++++++++++++++++++++++++++ wagtail/wagtailimages/tests.py | 98 +++++++++++++++++++++++++++++++ 2 files changed, 202 insertions(+) diff --git a/wagtail/wagtaildocs/tests.py b/wagtail/wagtaildocs/tests.py index 5592635f22..e4ec7f1ee3 100644 --- a/wagtail/wagtaildocs/tests.py +++ b/wagtail/wagtaildocs/tests.py @@ -417,3 +417,107 @@ class TestGetUsage(TestCase, WagtailTestUtils): args=(1,))) # There's no usage so there should be no table rows self.assertRegex(response.content, b'(\s|\n)*') + + +class TestIssue613(TestCase, WagtailTestUtils): + def get_elasticsearch_backend(self): + from django.conf import settings + from wagtail.wagtailsearch.backends import get_search_backend + + backend_path = 'wagtail.wagtailsearch.backends.elasticsearch.ElasticSearch' + + # Search WAGTAILSEARCH_BACKENDS for an entry that uses the given backend path + for backend_name, backend_conf in settings.WAGTAILSEARCH_BACKENDS.items(): + if backend_conf['BACKEND'] == backend_path: + return get_search_backend(backend_name) + else: + # no conf entry found - skip tests for this backend + raise unittest.SkipTest("No WAGTAILSEARCH_BACKENDS entry for the backend %s" % self.backend_path) + + def setUp(self): + self.search_backend = self.get_elasticsearch_backend() + self.login() + + from wagtail.wagtailsearch.signal_handlers import register_signal_handlers + register_signal_handlers() + + def add_document(self, **params): + # Build a fake file + fake_file = ContentFile(b("A boring example document")) + fake_file.name = 'test.txt' + + # Submit + post_data = { + 'title': "Test document", + 'file': fake_file, + } + post_data.update(params) + response = self.client.post(reverse('wagtaildocs_add_document'), post_data) + + # User should be redirected back to the index + self.assertRedirects(response, reverse('wagtaildocs_index')) + + # Document should be created + doc = models.Document.objects.filter(title=post_data['title']) + self.assertTrue(doc.exists()) + return doc.first() + + def edit_document(self, **params): + # Build a fake file + fake_file = ContentFile(b("A boring example document")) + fake_file.name = 'test.txt' + + # Create a document without tags to edit + document = models.Document.objects.create(title="Test document", file=fake_file) + + # Build another fake file + another_fake_file = ContentFile(b("A boring example document")) + another_fake_file.name = 'test.txt' + + # Submit + post_data = { + 'title': "Test document changed!", + 'file': another_fake_file, + } + post_data.update(params) + response = self.client.post(reverse('wagtaildocs_edit_document', args=(document.id,)), post_data) + + # User should be redirected back to the index + self.assertRedirects(response, reverse('wagtaildocs_index')) + + # Document should be changed + doc = models.Document.objects.filter(title=post_data['title']) + self.assertTrue(doc.exists()) + return doc.first() + + def test_issue_613_on_add(self): + # Reset the search index + self.search_backend.reset_index() + self.search_backend.add_type(Document) + + # Add a document with some tags + document = self.add_document(tags="hello") + self.search_backend.refresh_index() + + # Search for it by tag + results = self.search_backend.search("hello", Document) + + # Check + self.assertEqual(len(results), 1) + self.assertEqual(results[0].id, document.id) + + def test_issue_613_on_edit(self): + # Reset the search index + self.search_backend.reset_index() + self.search_backend.add_type(Document) + + # Add a document with some tags + document = self.edit_document(tags="hello") + self.search_backend.refresh_index() + + # Search for it by tag + results = self.search_backend.search("hello", Document) + + # Check + self.assertEqual(len(results), 1) + self.assertEqual(results[0].id, document.id) diff --git a/wagtail/wagtailimages/tests.py b/wagtail/wagtailimages/tests.py index d040a922d8..aa161282b8 100644 --- a/wagtail/wagtailimages/tests.py +++ b/wagtail/wagtailimages/tests.py @@ -1037,3 +1037,101 @@ class TestIssue573(TestCase): # This would crash if the bug is present image.get_rendition('fill-800x600') + +class TestIssue613(TestCase, WagtailTestUtils): + def get_elasticsearch_backend(self): + from django.conf import settings + from wagtail.wagtailsearch.backends import get_search_backend + + backend_path = 'wagtail.wagtailsearch.backends.elasticsearch.ElasticSearch' + + # Search WAGTAILSEARCH_BACKENDS for an entry that uses the given backend path + for backend_name, backend_conf in settings.WAGTAILSEARCH_BACKENDS.items(): + if backend_conf['BACKEND'] == backend_path: + return get_search_backend(backend_name) + else: + # no conf entry found - skip tests for this backend + raise unittest.SkipTest("No WAGTAILSEARCH_BACKENDS entry for the backend %s" % self.backend_path) + + def setUp(self): + self.search_backend = self.get_elasticsearch_backend() + self.login() + + from wagtail.wagtailsearch.signal_handlers import register_signal_handlers + register_signal_handlers() + + def add_image(self, **params): + post_data = { + 'title': "Test image", + 'file': SimpleUploadedFile('test.png', get_test_image_file().file.getvalue()), + } + post_data.update(params) + response = self.client.post(reverse('wagtailimages_add_image'), post_data) + + # Should redirect back to index + self.assertRedirects(response, reverse('wagtailimages_index')) + + # Check that the image was created + images = Image.objects.filter(title="Test image") + self.assertEqual(images.count(), 1) + + # Test that size was populated correctly + image = images.first() + self.assertEqual(image.width, 640) + self.assertEqual(image.height, 480) + + return image + + def edit_image(self, **params): + # Create an image to edit + self.image = Image.objects.create( + title="Test image", + file=get_test_image_file(), + ) + + # Edit it + post_data = { + 'title': "Edited", + } + post_data.update(params) + response = self.client.post(reverse('wagtailimages_edit_image', args=(self.image.id,)), post_data) + + # Should redirect back to index + self.assertRedirects(response, reverse('wagtailimages_index')) + + # Check that the image was edited + image = Image.objects.get(id=self.image.id) + self.assertEqual(image.title, "Edited") + return image + + def test_issue_613_on_add(self): + # Reset the search index + self.search_backend.reset_index() + self.search_backend.add_type(Image) + + # Add an image with some tags + image = self.add_image(tags="hello") + self.search_backend.refresh_index() + + # Search for it by tag + results = self.search_backend.search("hello", Image) + + # Check + self.assertEqual(len(results), 1) + self.assertEqual(results[0].id, image.id) + + def test_issue_613_on_edit(self): + # Reset the search index + self.search_backend.reset_index() + self.search_backend.add_type(Image) + + # Add an image with some tags + image = self.edit_image(tags="hello") + self.search_backend.refresh_index() + + # Search for it by tag + results = self.search_backend.search("hello", Image) + + # Check + self.assertEqual(len(results), 1) + self.assertEqual(results[0].id, image.id) \ No newline at end of file From 0a55e0814ea92ed1d590d6904a266e24af8a99b0 Mon Sep 17 00:00:00 2001 From: Karl Hobley Date: Thu, 11 Sep 2014 10:20:33 +0100 Subject: [PATCH 2/2] Make sure images/documents are reindexed after saving tags. Fixes #613 --- wagtail/wagtaildocs/views/chooser.py | 6 ++++++ wagtail/wagtaildocs/views/documents.py | 11 +++++++++++ wagtail/wagtailimages/views/chooser.py | 6 ++++++ wagtail/wagtailimages/views/images.py | 11 +++++++++++ wagtail/wagtailimages/views/multiple.py | 7 +++++++ wagtail/wagtailsearch/backends/__init__.py | 8 ++++++++ wagtail/wagtailsearch/signal_handlers.py | 11 +---------- 7 files changed, 50 insertions(+), 10 deletions(-) diff --git a/wagtail/wagtaildocs/views/chooser.py b/wagtail/wagtaildocs/views/chooser.py index 55066c9dc1..bf081715d3 100644 --- a/wagtail/wagtaildocs/views/chooser.py +++ b/wagtail/wagtaildocs/views/chooser.py @@ -6,6 +6,7 @@ from django.contrib.auth.decorators import permission_required from wagtail.wagtailadmin.modal_workflow import render_modal_workflow from wagtail.wagtailadmin.forms import SearchForm +from wagtail.wagtailsearch.backends import get_search_backends from wagtail.wagtaildocs.models import Document from wagtail.wagtaildocs.forms import DocumentForm @@ -96,6 +97,11 @@ def chooser_upload(request): if form.is_valid(): form.save() + + # Reindex the document to make sure all tags are indexed + for backend in get_search_backends(): + backend.add(document) + document_json = json.dumps({'id': document.id, 'title': document.title}) return render_modal_workflow( request, None, 'wagtaildocs/chooser/document_chosen.js', diff --git a/wagtail/wagtaildocs/views/documents.py b/wagtail/wagtaildocs/views/documents.py index 488e2ff9a2..51a759ae40 100644 --- a/wagtail/wagtaildocs/views/documents.py +++ b/wagtail/wagtaildocs/views/documents.py @@ -8,6 +8,7 @@ from django.views.decorators.vary import vary_on_headers from django.core.urlresolvers import reverse from wagtail.wagtailadmin.forms import SearchForm +from wagtail.wagtailsearch.backends import get_search_backends from wagtail.wagtaildocs.models import Document from wagtail.wagtaildocs.forms import DocumentForm @@ -83,6 +84,11 @@ def add(request): form = DocumentForm(request.POST, request.FILES, instance=doc) if form.is_valid(): form.save() + + # Reindex the document to make sure all tags are indexed + for backend in get_search_backends(): + backend.add(doc) + messages.success(request, _("Document '{0}' added.").format(doc.title)) return redirect('wagtaildocs_index') else: @@ -112,6 +118,11 @@ def edit(request, document_id): # which definitely isn't what we want... original_file.storage.delete(original_file.name) doc = form.save() + + # Reindex the document to make sure all tags are indexed + for backend in get_search_backends(): + backend.add(doc) + messages.success(request, _("Document '{0}' updated").format(doc.title)) return redirect('wagtaildocs_index') else: diff --git a/wagtail/wagtailimages/views/chooser.py b/wagtail/wagtailimages/views/chooser.py index 221a75581d..0312b3a2e2 100644 --- a/wagtail/wagtailimages/views/chooser.py +++ b/wagtail/wagtailimages/views/chooser.py @@ -6,6 +6,7 @@ from django.contrib.auth.decorators import permission_required from wagtail.wagtailadmin.modal_workflow import render_modal_workflow from wagtail.wagtailadmin.forms import SearchForm +from wagtail.wagtailsearch.backends import get_search_backends from wagtail.wagtailimages.models import get_image_model from wagtail.wagtailimages.forms import get_image_form, ImageInsertionForm @@ -121,6 +122,11 @@ def chooser_upload(request): if form.is_valid(): form.save() + + # Reindex the image to make sure all tags are indexed + for backend in get_search_backends(): + backend.add(image) + if request.GET.get('select_format'): form = ImageInsertionForm(initial={'alt_text': image.default_alt_text}) return render_modal_workflow( diff --git a/wagtail/wagtailimages/views/images.py b/wagtail/wagtailimages/views/images.py index 27ad96a474..d3713f1cf4 100644 --- a/wagtail/wagtailimages/views/images.py +++ b/wagtail/wagtailimages/views/images.py @@ -12,6 +12,7 @@ from django.http import HttpResponse from wagtail.wagtailcore.models import Site from wagtail.wagtailadmin.forms import SearchForm +from wagtail.wagtailsearch.backends import get_search_backends from wagtail.wagtailimages.models import get_image_model, Filter from wagtail.wagtailimages.forms import get_image_form, URLGeneratorForm @@ -96,6 +97,11 @@ def edit(request, image_id): original_file.storage.delete(original_file.name) image.renditions.all().delete() form.save() + + # Reindex the image to make sure all tags are indexed + for backend in get_search_backends(): + backend.add(image) + messages.success(request, _("Image '{0}' updated.").format(image.title)) return redirect('wagtailimages_index') else: @@ -203,6 +209,11 @@ def add(request): form = ImageForm(request.POST, request.FILES, instance=image) if form.is_valid(): form.save() + + # Reindex the image to make sure all tags are indexed + for backend in get_search_backends(): + backend.add(image) + messages.success(request, _("Image '{0}' added.").format(image.title)) return redirect('wagtailimages_index') else: diff --git a/wagtail/wagtailimages/views/multiple.py b/wagtail/wagtailimages/views/multiple.py index 5bc76fa7ce..2fcaa5f588 100644 --- a/wagtail/wagtailimages/views/multiple.py +++ b/wagtail/wagtailimages/views/multiple.py @@ -10,6 +10,8 @@ from django.template import RequestContext from django.template.loader import render_to_string from django.utils.translation import ugettext as _ +from wagtail.wagtailsearch.backends import get_search_backends + from wagtail.wagtailimages.models import get_image_model from wagtail.wagtailimages.forms import get_image_form_for_multi from wagtail.wagtailimages.utils.validators import validate_image_format @@ -79,6 +81,11 @@ def edit(request, image_id, callback=None): if form.is_valid(): form.save() + + # Reindex the image to make sure all tags are indexed + for backend in get_search_backends(): + backend.add(image) + return json_response({ 'success': True, 'image_id': int(image_id), diff --git a/wagtail/wagtailsearch/backends/__init__.py b/wagtail/wagtailsearch/backends/__init__.py index cf33cf5c12..a06f7a4f88 100644 --- a/wagtail/wagtailsearch/backends/__init__.py +++ b/wagtail/wagtailsearch/backends/__init__.py @@ -80,3 +80,11 @@ def get_search_backend(backend='default', **kwargs): # Create backend return backend_cls(params) + + +def get_search_backends(): + if hasattr(settings, 'WAGTAILSEARCH_BACKENDS'): + for backend in settings.WAGTAILSEARCH_BACKENDS.keys(): + yield get_search_backend(backend) + else: + yield get_search_backend('default') diff --git a/wagtail/wagtailsearch/signal_handlers.py b/wagtail/wagtailsearch/signal_handlers.py index 1c55eb59f0..decbfdb514 100644 --- a/wagtail/wagtailsearch/signal_handlers.py +++ b/wagtail/wagtailsearch/signal_handlers.py @@ -1,17 +1,8 @@ from django.db.models.signals import post_save, post_delete from django.db import models -from django.conf import settings from wagtail.wagtailsearch.index import Indexed -from wagtail.wagtailsearch.backends import get_search_backend - - -def get_search_backends(): - if hasattr(settings, 'WAGTAILSEARCH_BACKENDS'): - for backend in settings.WAGTAILSEARCH_BACKENDS.keys(): - yield get_search_backend(backend) - else: - yield get_search_backend('default') +from wagtail.wagtailsearch.backends import get_search_backends def post_save_signal_handler(instance, **kwargs):