From edd47971a66195e30a9dba5bacc6abec27ce3291 Mon Sep 17 00:00:00 2001 From: Daniel Nigusse Date: Tue, 10 Dec 2024 13:51:32 +0300 Subject: [PATCH 1/2] Refactor: Add dynamic filter logic for Lisan translation handling - Implement dynamic filtering using language-specific model fields. - Replace static filtering with `filter_kwargs` for better flexibility. - Refactor `set_bulk_lisans` to accommodate dynamic translation fields. - Remove redundant `_process_translations` method in favor of bulk setting. - Ensure atomicity in bulk translation updates for data consistency. - Enhance readability and maintainability of the Lisan handling logic. --- lisan/metaclasses.py | 2 -- lisan/mixins.py | 20 +++++++++++++------- lisan/serializers.py | 27 +-------------------------- tests/test_mixins.py | 38 ++++++++++++++++++++++++++------------ tests/test_serializers.py | 24 ------------------------ 5 files changed, 40 insertions(+), 71 deletions(-) diff --git a/lisan/metaclasses.py b/lisan/metaclasses.py index 6c643d0..483cf0a 100644 --- a/lisan/metaclasses.py +++ b/lisan/metaclasses.py @@ -89,8 +89,6 @@ def clean(self): attrs ) - print(f"Generated Lisan model for {model_cls.__name__}") # noqa - return lisan_model diff --git a/lisan/mixins.py b/lisan/mixins.py index 51ed6c1..3dec14d 100644 --- a/lisan/mixins.py +++ b/lisan/mixins.py @@ -33,8 +33,11 @@ def get_lisan(self, language_code=None): """ language_code = language_code or self._current_language try: - return self.Lisan.objects.filter( - language_code=language_code).first() + filter_kwargs = { + "language_code": language_code, + f"{self._meta.model_name}": self + } + return self.Lisan.objects.filter(**filter_kwargs).first() except ObjectDoesNotExist: return None except Exception as e: @@ -63,9 +66,11 @@ def set_lisan(self, language_code, **lisan_fields): try: with transaction.atomic(): - lisan = self.Lisan.objects.filter( - language_code=language_code - ).first() + filter_kwargs = { + "language_code": language_code, + f"{self._meta.model_name}": self + } + lisan = self.Lisan.objects.filter(**filter_kwargs).first() if not lisan: # Check if all lisan_fields exist in the model @@ -159,8 +164,9 @@ def set_bulk_lisans(self, translations): consistency. """ with transaction.atomic(): - for language_code, fields in translations.items(): - self.set_lisan(language_code, **fields) + for translation in translations: + language_code = translation.pop("language_code") + self.set_lisan(language_code, **translation) def get_lisan_field( self, field_name, diff --git a/lisan/serializers.py b/lisan/serializers.py index 8d46cfa..b2bf768 100644 --- a/lisan/serializers.py +++ b/lisan/serializers.py @@ -108,24 +108,6 @@ def to_representation(self, instance): representation['translations'] = translations_representation return representation - def _process_translations( - self, instance, translations, default_language_code): - """ - Process and save the translations for each language provided - in the translations list. This method updates the instance with - language-specific data. - """ - for translation in translations: - lang_code = translation.pop('language_code', None) - if lang_code: - lisan_data = { - field: translation.get(field) - for field in instance.lisan_fields - if translation.get(field) is not None - } - if lisan_data: - instance.set_lisan(lang_code, **lisan_data) - def create(self, validated_data): """ Create a new instance of the model, handling any translations @@ -145,14 +127,7 @@ def create(self, validated_data): # Create the main instance instance = super().create(validated_data) - lisan_fields = { - field: validated_data.get(field) - for field in self.Meta.model.lisan_fields - } - instance.set_lisan(language_code, **lisan_fields) - - # Process and save the translations - self._process_translations(instance, translations, language_code) + instance.set_bulk_lisans(translations) return instance diff --git a/tests/test_mixins.py b/tests/test_mixins.py index 340f9a9..a71ba46 100644 --- a/tests/test_mixins.py +++ b/tests/test_mixins.py @@ -75,16 +75,24 @@ def test_set_bulk_lisans(self): """ Test setting translations in bulk for multiple languages. """ - translations = { - 'am': {'title': "አዲስ ሰላም"}, - 'or': {'title': "Nagaa Addunyaa", 'description': "Fakkeenya"} - } + translations = [ + { + "language_code": "or", + "title": "Nagaa Addunyaa", + "description": "Fakkeenya" + }, + { + "language_code": "am", + "title": "አስደሳች ጉዞ", + "description": "በማይታወቁ መሬቶች ላይ አስገራሚ ጉዞ." + } + ] self.instance.set_bulk_lisans(translations) am_translation = self.instance.get_lisan('am') or_translation = self.instance.get_lisan('or') - self.assertEqual(am_translation.title, "አዲስ ሰላም") + self.assertEqual(am_translation.title, "አስደሳች ጉዞ") self.assertEqual(or_translation.title, "Nagaa Addunyaa") self.assertEqual(or_translation.description, "Fakkeenya") @@ -246,17 +254,23 @@ def test_set_bulk_lisans_updates_and_creates_translations(self): Test that set_bulk_lisans updates existing translations and creates new translations as needed. """ - translations = { - # Update existing translation - 'am': {'title': "አዲስ ሰላም"}, - # New translation - 'or': {'title': "Nagaa Addunyaa", 'description': "Fakkeenya"}, - } + translations = [ + { + "language_code": "or", + "title": "Nagaa Addunyaa", + "description": "Fakkeenya" + }, + { + "language_code": "am", + "title": "አስደሳች ጉዞ", + "description": "ምሳሌ መግለጫ" + } + ] self.instance.set_bulk_lisans(translations) # Verify the 'am' translation was updated am_translation = self.instance.get_lisan('am') - self.assertEqual(am_translation.title, "አዲስ ሰላም") + self.assertEqual(am_translation.title, "አስደሳች ጉዞ") # Unchanged field self.assertEqual(am_translation.description, "ምሳሌ መግለጫ") diff --git a/tests/test_serializers.py b/tests/test_serializers.py index 2d7faee..644e405 100644 --- a/tests/test_serializers.py +++ b/tests/test_serializers.py @@ -213,30 +213,6 @@ def test_representation_unsupported_language(self): self.assertEqual(representation['title'], "Hello World") self.assertEqual(representation['description'], "Sample description") - def test_process_translations_empty_data(self): - """ - Test that `_process_translations` handles empty - translations gracefully. - """ - serializer = self.serializer_class(context={'request': self.request}) - serializer._process_translations(self.model_instance, [], 'en') - # Ensure no exceptions and instance remains unchanged - self.assertEqual(self.model_instance.get_lisan_field( - 'title', 'am'), "ሰላም") - - def test_process_translations_with_missing_language_code(self): - """ - Test `_process_translations` with a translation missing a - language_code. - """ - serializer = self.serializer_class(context={'request': self.request}) - translations = [{'title': "Missing Language Code"}] - serializer._process_translations( - self.model_instance, translations, 'en') - # Ensure no exceptions and instance remains unchanged - self.assertEqual(self.model_instance.get_lisan_field( - 'title', 'am'), "ሰላም") - def test_validate_translations_partial(self): """ Test validation with partial translations in partial update mode. From c2efa7f46f7818cccc1ba7e54593d85ca5d1c46e Mon Sep 17 00:00:00 2001 From: Daniel Nigusse Date: Tue, 10 Dec 2024 18:44:02 +0300 Subject: [PATCH 2/2] chore: release v0.1.7 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 82efd6c..5348171 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ def read_file(filename): setup( name='lisan', - version='0.1.6', + version='0.1.7', packages=find_packages(), include_package_data=True, license='MIT',