From 8b32d63e05d06c5f183d87caad5012c3001f70e1 Mon Sep 17 00:00:00 2001 From: Andrei Satsevich Date: Fri, 22 Dec 2023 17:15:05 +0300 Subject: [PATCH] Prevent translation object duplicates via TranslationCreator (#756) --- wagtail_localize/operations.py | 8 ++- wagtail_localize/tests/test_operations.py | 84 +++++++++++++++++++++++ 2 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 wagtail_localize/tests/test_operations.py diff --git a/wagtail_localize/operations.py b/wagtail_localize/operations.py index edbf0804..1df4ac2f 100644 --- a/wagtail_localize/operations.py +++ b/wagtail_localize/operations.py @@ -50,7 +50,7 @@ def create_translations(self, instance, include_related_objects=True): ) # Support disabling the out of the box translation mode. - # The value set on the model takes precendence over the global setting. + # The value set on the model takes precedence over the global setting. if hasattr(instance, "localize_default_translation_mode"): translation_mode = instance.localize_default_translation_mode else: @@ -61,9 +61,13 @@ def create_translations(self, instance, include_related_objects=True): # Set up translation records for target_locale in self.target_locales: + # Skip if target_locale is the same as source locale + if target_locale == source.locale: + continue + # Create translation if it doesn't exist yet, re-enable if translation was disabled # Note that the form won't show this locale as an option if the translation existed - # in this langauge, so this shouldn't overwrite any unmanaged translations. + # in this language, so this shouldn't overwrite any unmanaged translations. translation, created = Translation.objects.update_or_create( source=source, target_locale=target_locale, diff --git a/wagtail_localize/tests/test_operations.py b/wagtail_localize/tests/test_operations.py new file mode 100644 index 00000000..3e786411 --- /dev/null +++ b/wagtail_localize/tests/test_operations.py @@ -0,0 +1,84 @@ +from django.contrib.auth import get_user_model +from django.test import TestCase +from wagtail.models import Locale, Page + +from wagtail_localize.models import Translation, TranslationSource +from wagtail_localize.operations import TranslationCreator +from wagtail_localize.segments import RelatedObjectSegmentValue +from wagtail_localize.test.models import TestPage + + +def create_test_page(**kwargs): + parent = kwargs.pop("parent", None) or Page.objects.get(id=1) + page = parent.add_child(instance=TestPage(**kwargs)) + page_revision = page.save_revision() + page_revision.publish() + page.refresh_from_db() + + source, created = TranslationSource.get_or_create_from_instance(page) + prepare_source(source) + + return page + + +def prepare_source(source): + # Recurse into any related objects + for segment in source.relatedobjectsegment_set.all(): + if isinstance(segment, RelatedObjectSegmentValue): + related_source, created = TranslationSource.get_or_create_from_instance( + segment.get_instance(source.locale) + ) + prepare_source(related_source) + + +class TranslationOperationsTest(TestCase): + def setUp(self): + # Create a Belarusian locale for testing + self.be_locale = Locale.objects.create(language_code="be") + + # Create a test page + self.page = create_test_page( + title="Test page", + slug="test-page", + test_charfield="This is some test content", + ) + + # Create a user + self.user = get_user_model().objects.create(username="testuser") + + # Instantiate TranslationCreator with the default and Belarusian locales for target_locales + self.target_locales = [self.page.locale, self.be_locale] + self.translation_creator = TranslationCreator( + user=self.user, target_locales=self.target_locales + ) + + def test_create_translations_skips_duplicate(self): + # Call create_translations() to check that only one translation has been created + self.translation_creator.create_translations(self.page) + + # Assert statements for better readability + self.assertEqual( + TranslationSource.objects.filter( + object_id=self.page.translation_key + ).count(), + 1, + "Only one TranslationSource should be created for the source page", + ) + + self.assertEqual( + Translation.objects.filter( + source__object_id=self.page.translation_key, + target_locale=self.be_locale, + ).count(), + 1, + "Only one Translation object should be created for the Belarusian locale", + ) + + self.assertEqual( + Translation.objects.filter( + source__object_id=self.page.translation_key, + target_locale=self.page.locale, + ).count(), + 0, + "No Translation object should be created for the default locale", + )