mirror of
				https://github.com/SectorLabs/django-localized-fields.git
				synced 2025-10-26 08:58:58 +03:00 
			
		
		
		
	Compare commits
	
		
			13 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | a9906dd159 | ||
|  | a66b3492cd | ||
|  | bc494694f5 | ||
|  | b0cfaea2b4 | ||
|  | 8c7d0773f7 | ||
|  | cc911d4909 | ||
|  | 0f30cc1493 | ||
|  | 0d1e9510cf | ||
|  | 25c1c24ccb | ||
|  | bd3005a7e9 | ||
|  | 7902d8225a | ||
|  | f024e4feb5 | ||
|  | 92cb5e8b1f | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -15,6 +15,7 @@ reports/ | |||||||
| # Ignore build results | # Ignore build results | ||||||
| *.egg-info/ | *.egg-info/ | ||||||
| dist/ | dist/ | ||||||
|  | build/ | ||||||
| pip-wheel-metadata | pip-wheel-metadata | ||||||
|  |  | ||||||
| # Ignore stupid .DS_Store | # Ignore stupid .DS_Store | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ | |||||||
| | :white_check_mark: | **Tests** | [](https://circleci.com/gh/SectorLabs/django-localized-fields/tree/master) | | | :white_check_mark: | **Tests** | [](https://circleci.com/gh/SectorLabs/django-localized-fields/tree/master) | | ||||||
| | :memo: | **License** | [](http://doge.mit-license.org) | | | :memo: | **License** | [](http://doge.mit-license.org) | | ||||||
| | :package: | **PyPi** | [](https://pypi.python.org/pypi/django-localized-fields) | | | :package: | **PyPi** | [](https://pypi.python.org/pypi/django-localized-fields) | | ||||||
| | <img src="https://icon-library.net/images/django-icon/django-icon-0.jpg" width="22px" height="22px" align="center" /> | **Django Versions** | 2.0, 2.1, 2.2, 3.0, 3.1 | | | <img src="https://cdn.iconscout.com/icon/free/png-256/django-1-282754.png" width="22px" height="22px" align="center" /> | **Django Versions** | 2.0, 2.1, 2.2, 3.0, 3.1, 3.2 | | ||||||
| | <img src="http://www.iconarchive.com/download/i73027/cornmanthe3rd/plex/Other-python.ico" width="22px" height="22px" align="center" /> | **Python Versions** | 3.6, 3.7, 3.8, 3.9 | | | <img src="http://www.iconarchive.com/download/i73027/cornmanthe3rd/plex/Other-python.ico" width="22px" height="22px" align="center" /> | **Python Versions** | 3.6, 3.7, 3.8, 3.9 | | ||||||
| | :book: | **Documentation** | [Read The Docs](https://django-localized-fields.readthedocs.io) | | | :book: | **Documentation** | [Read The Docs](https://django-localized-fields.readthedocs.io) | | ||||||
| | :warning: | **Upgrade** | [Upgrade fom v5.x](https://django-localized-fields.readthedocs.io/en/latest/releases.html#v6-0) | | :warning: | **Upgrade** | [Upgrade fom v5.x](https://django-localized-fields.readthedocs.io/en/latest/releases.html#v6-0) | ||||||
| @@ -38,7 +38,7 @@ | |||||||
|  |  | ||||||
| 3. Create a postgres user for use in tests (skip if your default user is a postgres superuser): | 3. Create a postgres user for use in tests (skip if your default user is a postgres superuser): | ||||||
|  |  | ||||||
|        λ createuser --superuser psqlextra --pwprompt |        λ createuser --superuser localized_fields --pwprompt | ||||||
|        λ export DATABASE_URL=postgres://localized_fields:<password>@localhost/localized_fields |        λ export DATABASE_URL=postgres://localized_fields:<password>@localhost/localized_fields | ||||||
|  |  | ||||||
|    Hint: if you're using virtualenvwrapper, you might find it beneficial to put |    Hint: if you're using virtualenvwrapper, you might find it beneficial to put | ||||||
|   | |||||||
| @@ -1 +1,4 @@ | |||||||
|  | import django | ||||||
|  |  | ||||||
|  | if django.VERSION < (3, 2): | ||||||
|     default_app_config = "localized_fields.apps.LocalizedFieldsConfig" |     default_app_config = "localized_fields.apps.LocalizedFieldsConfig" | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| from . import widgets | from . import widgets | ||||||
| from .fields import ( | from .fields import ( | ||||||
|  |     LocalizedBooleanField, | ||||||
|     LocalizedCharField, |     LocalizedCharField, | ||||||
|     LocalizedField, |     LocalizedField, | ||||||
|     LocalizedFileField, |     LocalizedFileField, | ||||||
| @@ -11,6 +12,7 @@ FORMFIELD_FOR_LOCALIZED_FIELDS_DEFAULTS = { | |||||||
|     LocalizedCharField: {"widget": widgets.AdminLocalizedCharFieldWidget}, |     LocalizedCharField: {"widget": widgets.AdminLocalizedCharFieldWidget}, | ||||||
|     LocalizedTextField: {"widget": widgets.AdminLocalizedFieldWidget}, |     LocalizedTextField: {"widget": widgets.AdminLocalizedFieldWidget}, | ||||||
|     LocalizedFileField: {"widget": widgets.AdminLocalizedFileFieldWidget}, |     LocalizedFileField: {"widget": widgets.AdminLocalizedFileFieldWidget}, | ||||||
|  |     LocalizedBooleanField: {"widget": widgets.AdminLocalizedBooleanFieldWidget}, | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| from .autoslug_field import LocalizedAutoSlugField | from .autoslug_field import LocalizedAutoSlugField | ||||||
|  | from .boolean_field import LocalizedBooleanField | ||||||
| from .char_field import LocalizedCharField | from .char_field import LocalizedCharField | ||||||
| from .field import LocalizedField | from .field import LocalizedField | ||||||
| from .file_field import LocalizedFileField | from .file_field import LocalizedFileField | ||||||
| @@ -16,6 +17,7 @@ __all__ = [ | |||||||
|     "LocalizedFileField", |     "LocalizedFileField", | ||||||
|     "LocalizedIntegerField", |     "LocalizedIntegerField", | ||||||
|     "LocalizedFloatField", |     "LocalizedFloatField", | ||||||
|  |     "LocalizedBooleanField", | ||||||
| ] | ] | ||||||
|  |  | ||||||
| try: | try: | ||||||
|   | |||||||
| @@ -16,14 +16,14 @@ from .field import LocalizedField | |||||||
| class LocalizedAutoSlugField(LocalizedField): | class LocalizedAutoSlugField(LocalizedField): | ||||||
|     """Automatically provides slugs for a localized field upon saving.""" |     """Automatically provides slugs for a localized field upon saving.""" | ||||||
|  |  | ||||||
|  |     def __init__(self, *args, **kwargs): | ||||||
|  |         """Initializes a new instance of :see:LocalizedAutoSlugField.""" | ||||||
|  |  | ||||||
|         warnings.warn( |         warnings.warn( | ||||||
|             "LocalizedAutoSlug is deprecated and will be removed in the next major version.", |             "LocalizedAutoSlug is deprecated and will be removed in the next major version.", | ||||||
|             DeprecationWarning, |             DeprecationWarning, | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|     def __init__(self, *args, **kwargs): |  | ||||||
|         """Initializes a new instance of :see:LocalizedAutoSlugField.""" |  | ||||||
|  |  | ||||||
|         self.populate_from = kwargs.pop("populate_from", None) |         self.populate_from = kwargs.pop("populate_from", None) | ||||||
|         self.include_time = kwargs.pop("include_time", False) |         self.include_time = kwargs.pop("include_time", False) | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										110
									
								
								localized_fields/fields/boolean_field.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								localized_fields/fields/boolean_field.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,110 @@ | |||||||
|  | from typing import Dict, Optional, Union | ||||||
|  |  | ||||||
|  | from django.conf import settings | ||||||
|  | from django.db.utils import IntegrityError | ||||||
|  |  | ||||||
|  | from ..forms import LocalizedBooleanFieldForm | ||||||
|  | from ..value import LocalizedBooleanValue, LocalizedValue | ||||||
|  | from .field import LocalizedField | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedBooleanField(LocalizedField): | ||||||
|  |     """Stores booleans as a localized value.""" | ||||||
|  |  | ||||||
|  |     attr_class = LocalizedBooleanValue | ||||||
|  |  | ||||||
|  |     @classmethod | ||||||
|  |     def from_db_value(cls, value, *_) -> Optional[LocalizedBooleanValue]: | ||||||
|  |         db_value = super().from_db_value(value) | ||||||
|  |  | ||||||
|  |         if db_value is None: | ||||||
|  |             return db_value | ||||||
|  |  | ||||||
|  |         if isinstance(db_value, str): | ||||||
|  |             if db_value.lower() == "true": | ||||||
|  |                 return True | ||||||
|  |             return False | ||||||
|  |  | ||||||
|  |         if not isinstance(db_value, LocalizedValue): | ||||||
|  |             return db_value | ||||||
|  |  | ||||||
|  |         return cls._convert_localized_value(db_value) | ||||||
|  |  | ||||||
|  |     def to_python( | ||||||
|  |         self, value: Union[Dict[str, str], str, None] | ||||||
|  |     ) -> LocalizedBooleanValue: | ||||||
|  |         """Converts the value from a database value into a Python value.""" | ||||||
|  |  | ||||||
|  |         db_value = super().to_python(value) | ||||||
|  |         return self._convert_localized_value(db_value) | ||||||
|  |  | ||||||
|  |     def get_prep_value(self, value: LocalizedBooleanValue) -> dict: | ||||||
|  |         """Gets the value in a format to store into the database.""" | ||||||
|  |  | ||||||
|  |         # apply default values | ||||||
|  |         default_values = LocalizedBooleanValue(self.default) | ||||||
|  |         if isinstance(value, LocalizedBooleanValue): | ||||||
|  |             for lang_code, _ in settings.LANGUAGES: | ||||||
|  |                 local_value = value.get(lang_code) | ||||||
|  |                 if local_value is None: | ||||||
|  |                     value.set(lang_code, default_values.get(lang_code, None)) | ||||||
|  |  | ||||||
|  |         prepped_value = super().get_prep_value(value) | ||||||
|  |         if prepped_value is None: | ||||||
|  |             return None | ||||||
|  |  | ||||||
|  |         # make sure all values are proper values to be converted to bool | ||||||
|  |         for lang_code, _ in settings.LANGUAGES: | ||||||
|  |             local_value = prepped_value[lang_code] | ||||||
|  |  | ||||||
|  |             if local_value is not None and local_value.lower() not in ( | ||||||
|  |                 "false", | ||||||
|  |                 "true", | ||||||
|  |             ): | ||||||
|  |                 raise IntegrityError( | ||||||
|  |                     'non-boolean value in column "%s.%s" violates ' | ||||||
|  |                     "boolean constraint" % (self.name, lang_code) | ||||||
|  |                 ) | ||||||
|  |  | ||||||
|  |             # convert to a string before saving because the underlying | ||||||
|  |             # type is hstore, which only accept strings | ||||||
|  |             prepped_value[lang_code] = ( | ||||||
|  |                 str(local_value) if local_value is not None else None | ||||||
|  |             ) | ||||||
|  |  | ||||||
|  |         return prepped_value | ||||||
|  |  | ||||||
|  |     def formfield(self, **kwargs): | ||||||
|  |         """Gets the form field associated with this field.""" | ||||||
|  |         defaults = {"form_class": LocalizedBooleanFieldForm} | ||||||
|  |  | ||||||
|  |         defaults.update(kwargs) | ||||||
|  |         return super().formfield(**defaults) | ||||||
|  |  | ||||||
|  |     @staticmethod | ||||||
|  |     def _convert_localized_value( | ||||||
|  |         value: LocalizedValue, | ||||||
|  |     ) -> LocalizedBooleanValue: | ||||||
|  |         """Converts from :see:LocalizedValue to :see:LocalizedBooleanValue.""" | ||||||
|  |  | ||||||
|  |         integer_values = {} | ||||||
|  |         for lang_code, _ in settings.LANGUAGES: | ||||||
|  |             local_value = value.get(lang_code, None) | ||||||
|  |  | ||||||
|  |             if isinstance(local_value, str): | ||||||
|  |                 if local_value.lower() == "false": | ||||||
|  |                     local_value = False | ||||||
|  |                 elif local_value.lower() == "true": | ||||||
|  |                     local_value = True | ||||||
|  |                 else: | ||||||
|  |                     raise ValueError( | ||||||
|  |                         f"Could not convert value {local_value} to boolean." | ||||||
|  |                     ) | ||||||
|  |  | ||||||
|  |                 integer_values[lang_code] = local_value | ||||||
|  |             elif local_value is not None: | ||||||
|  |                 raise TypeError( | ||||||
|  |                     f"Expected value of type str instead of {type(local_value)}." | ||||||
|  |                 ) | ||||||
|  |  | ||||||
|  |         return LocalizedBooleanValue(integer_values) | ||||||
| @@ -6,12 +6,14 @@ from django.core.exceptions import ValidationError | |||||||
| from django.forms.widgets import FILE_INPUT_CONTRADICTION | from django.forms.widgets import FILE_INPUT_CONTRADICTION | ||||||
|  |  | ||||||
| from .value import ( | from .value import ( | ||||||
|  |     LocalizedBooleanValue, | ||||||
|     LocalizedFileValue, |     LocalizedFileValue, | ||||||
|     LocalizedIntegerValue, |     LocalizedIntegerValue, | ||||||
|     LocalizedStringValue, |     LocalizedStringValue, | ||||||
|     LocalizedValue, |     LocalizedValue, | ||||||
| ) | ) | ||||||
| from .widgets import ( | from .widgets import ( | ||||||
|  |     AdminLocalizedBooleanFieldWidget, | ||||||
|     AdminLocalizedIntegerFieldWidget, |     AdminLocalizedIntegerFieldWidget, | ||||||
|     LocalizedCharFieldWidget, |     LocalizedCharFieldWidget, | ||||||
|     LocalizedFieldWidget, |     LocalizedFieldWidget, | ||||||
| @@ -102,6 +104,15 @@ class LocalizedIntegerFieldForm(LocalizedFieldForm): | |||||||
|     value_class = LocalizedIntegerValue |     value_class = LocalizedIntegerValue | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedBooleanFieldForm(LocalizedFieldForm, forms.BooleanField): | ||||||
|  |     """Form for a localized boolean field, allows editing the field in multiple | ||||||
|  |     languages.""" | ||||||
|  |  | ||||||
|  |     widget = AdminLocalizedBooleanFieldWidget | ||||||
|  |     field_class = forms.fields.BooleanField | ||||||
|  |     value_class = LocalizedBooleanValue | ||||||
|  |  | ||||||
|  |  | ||||||
| class LocalizedFileFieldForm(LocalizedFieldForm, forms.FileField): | class LocalizedFileFieldForm(LocalizedFieldForm, forms.FileField): | ||||||
|     """Form for a localized file field, allows editing the field in multiple |     """Form for a localized file field, allows editing the field in multiple | ||||||
|     languages.""" |     languages.""" | ||||||
|   | |||||||
| @@ -136,6 +136,15 @@ class LocalizedValue(dict): | |||||||
|  |  | ||||||
|         return None |         return None | ||||||
|  |  | ||||||
|  |     def is_empty(self) -> bool: | ||||||
|  |         """Gets whether all the languages contain the default value.""" | ||||||
|  |  | ||||||
|  |         for lang_code, _ in settings.LANGUAGES: | ||||||
|  |             if self.get(lang_code) != self.default_value: | ||||||
|  |                 return False | ||||||
|  |  | ||||||
|  |         return True | ||||||
|  |  | ||||||
|     def __str__(self) -> str: |     def __str__(self) -> str: | ||||||
|         """Gets the value in the current language or falls back to the next |         """Gets the value in the current language or falls back to the next | ||||||
|         language if there's no value in the current language.""" |         language if there's no value in the current language.""" | ||||||
| @@ -228,6 +237,35 @@ class LocalizedFileValue(LocalizedValue): | |||||||
|         return self.get(translation.get_language()) |         return self.get(translation.get_language()) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedBooleanValue(LocalizedValue): | ||||||
|  |     def translate(self): | ||||||
|  |         """Gets the value in the current language, or in the configured fallbck | ||||||
|  |         language.""" | ||||||
|  |  | ||||||
|  |         value = super().translate() | ||||||
|  |         if value is None or (isinstance(value, str) and value.strip() == ""): | ||||||
|  |             return None | ||||||
|  |  | ||||||
|  |         if isinstance(value, bool): | ||||||
|  |             return value | ||||||
|  |  | ||||||
|  |         if value.lower() == "true": | ||||||
|  |             return True | ||||||
|  |         return False | ||||||
|  |  | ||||||
|  |     def __bool__(self): | ||||||
|  |         """Gets the value in the current language as a boolean.""" | ||||||
|  |         value = self.translate() | ||||||
|  |  | ||||||
|  |         return value | ||||||
|  |  | ||||||
|  |     def __str__(self): | ||||||
|  |         """Returns string representation of value.""" | ||||||
|  |  | ||||||
|  |         value = self.translate() | ||||||
|  |         return str(value) if value is not None else "" | ||||||
|  |  | ||||||
|  |  | ||||||
| class LocalizedNumericValue(LocalizedValue): | class LocalizedNumericValue(LocalizedValue): | ||||||
|     def __int__(self): |     def __int__(self): | ||||||
|         """Gets the value in the current language as an integer.""" |         """Gets the value in the current language as an integer.""" | ||||||
|   | |||||||
| @@ -120,6 +120,18 @@ class AdminLocalizedFieldWidget(LocalizedFieldWidget): | |||||||
|     widget = widgets.AdminTextareaWidget |     widget = widgets.AdminTextareaWidget | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class AdminLocalizedBooleanFieldWidget(LocalizedFieldWidget): | ||||||
|  |     widget = forms.Select | ||||||
|  |  | ||||||
|  |     def __init__(self, *args, **kwargs): | ||||||
|  |         """Initializes a new instance of :see:LocalizedBooleanFieldWidget.""" | ||||||
|  |  | ||||||
|  |         super().__init__(*args, **kwargs) | ||||||
|  |  | ||||||
|  |         for widget in self.widgets: | ||||||
|  |             widget.choices = [("False", False), ("True", True)] | ||||||
|  |  | ||||||
|  |  | ||||||
| class AdminLocalizedCharFieldWidget(AdminLocalizedFieldWidget): | class AdminLocalizedCharFieldWidget(AdminLocalizedFieldWidget): | ||||||
|     widget = widgets.AdminTextInputWidget |     widget = widgets.AdminTextInputWidget | ||||||
|  |  | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ SECRET_KEY = 'this is my secret key'  # NOQA | |||||||
| TEST_RUNNER = 'django.test.runner.DiscoverRunner' | TEST_RUNNER = 'django.test.runner.DiscoverRunner' | ||||||
|  |  | ||||||
| DATABASES = { | DATABASES = { | ||||||
|     'default': dj_database_url.config(default='postgres:///psqlextra'), |     'default': dj_database_url.config(default='postgres:///localized_fields'), | ||||||
| } | } | ||||||
|  |  | ||||||
| DATABASES['default']['ENGINE'] = 'psqlextra.backend' | DATABASES['default']['ENGINE'] = 'psqlextra.backend' | ||||||
|   | |||||||
							
								
								
									
										7
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								setup.py
									
									
									
									
									
								
							| @@ -36,7 +36,7 @@ with open( | |||||||
|  |  | ||||||
| setup( | setup( | ||||||
|     name="django-localized-fields", |     name="django-localized-fields", | ||||||
|     version="6.3", |     version="6.7", | ||||||
|     packages=find_packages(exclude=["tests"]), |     packages=find_packages(exclude=["tests"]), | ||||||
|     include_package_data=True, |     include_package_data=True, | ||||||
|     license="MIT License", |     license="MIT License", | ||||||
| @@ -64,6 +64,9 @@ setup( | |||||||
|         "Operating System :: OS Independent", |         "Operating System :: OS Independent", | ||||||
|         "Programming Language :: Python", |         "Programming Language :: Python", | ||||||
|         "Programming Language :: Python :: 3.6", |         "Programming Language :: Python :: 3.6", | ||||||
|  |         "Programming Language :: Python :: 3.7", | ||||||
|  |         "Programming Language :: Python :: 3.8", | ||||||
|  |         "Programming Language :: Python :: 3.9", | ||||||
|         "Topic :: Internet :: WWW/HTTP", |         "Topic :: Internet :: WWW/HTTP", | ||||||
|         "Topic :: Internet :: WWW/HTTP :: Dynamic Content", |         "Topic :: Internet :: WWW/HTTP :: Dynamic Content", | ||||||
|     ], |     ], | ||||||
| @@ -87,7 +90,7 @@ setup( | |||||||
|             "psycopg2==2.8.4", |             "psycopg2==2.8.4", | ||||||
|         ], |         ], | ||||||
|         "analysis": [ |         "analysis": [ | ||||||
|             "black==19.3b0", |             "black==22.3.0", | ||||||
|             "flake8==3.7.7", |             "flake8==3.7.7", | ||||||
|             "autoflake==1.3", |             "autoflake==1.3", | ||||||
|             "autopep8==1.4.4", |             "autopep8==1.4.4", | ||||||
|   | |||||||
							
								
								
									
										211
									
								
								tests/test_boolean_field.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								tests/test_boolean_field.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,211 @@ | |||||||
|  | from django.conf import settings | ||||||
|  | from django.db import connection | ||||||
|  | from django.db.utils import IntegrityError | ||||||
|  | from django.test import TestCase | ||||||
|  | from django.utils import translation | ||||||
|  |  | ||||||
|  | from localized_fields.fields import LocalizedBooleanField | ||||||
|  | from localized_fields.value import LocalizedBooleanValue | ||||||
|  |  | ||||||
|  | from .fake_model import get_fake_model | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedBooleanFieldTestCase(TestCase): | ||||||
|  |     """Tests whether the :see:LocalizedBooleanField and | ||||||
|  |     :see:LocalizedIntegerValue works properly.""" | ||||||
|  |  | ||||||
|  |     TestModel = None | ||||||
|  |  | ||||||
|  |     @classmethod | ||||||
|  |     def setUpClass(cls): | ||||||
|  |         super().setUpClass() | ||||||
|  |  | ||||||
|  |         cls.TestModel = get_fake_model({"translated": LocalizedBooleanField()}) | ||||||
|  |  | ||||||
|  |     def test_basic(self): | ||||||
|  |         """Tests the basics of storing boolean values.""" | ||||||
|  |  | ||||||
|  |         obj = self.TestModel() | ||||||
|  |         for lang_code, _ in settings.LANGUAGES: | ||||||
|  |             obj.translated.set(lang_code, False) | ||||||
|  |         obj.save() | ||||||
|  |  | ||||||
|  |         obj = self.TestModel.objects.all().first() | ||||||
|  |         for lang_code, _ in settings.LANGUAGES: | ||||||
|  |             assert obj.translated.get(lang_code) is False | ||||||
|  |  | ||||||
|  |     def test_primary_language_required(self): | ||||||
|  |         """Tests whether the primary language is required by default and all | ||||||
|  |         other languages are optional.""" | ||||||
|  |  | ||||||
|  |         # not filling in anything should raise IntegrityError, | ||||||
|  |         # the primary language is required | ||||||
|  |         with self.assertRaises(IntegrityError): | ||||||
|  |             obj = self.TestModel() | ||||||
|  |             obj.save() | ||||||
|  |  | ||||||
|  |         # when filling all other languages besides the primary language | ||||||
|  |         # should still raise an error because the primary is always required | ||||||
|  |         with self.assertRaises(IntegrityError): | ||||||
|  |             obj = self.TestModel() | ||||||
|  |             for lang_code, _ in settings.LANGUAGES: | ||||||
|  |                 if lang_code == settings.LANGUAGE_CODE: | ||||||
|  |                     continue | ||||||
|  |                 obj.translated.set(lang_code, True) | ||||||
|  |             obj.save() | ||||||
|  |  | ||||||
|  |     def test_default_value_none(self): | ||||||
|  |         """Tests whether the default value for optional languages is | ||||||
|  |         NoneType.""" | ||||||
|  |  | ||||||
|  |         obj = self.TestModel() | ||||||
|  |         obj.translated.set(settings.LANGUAGE_CODE, True) | ||||||
|  |         obj.save() | ||||||
|  |  | ||||||
|  |         for lang_code, _ in settings.LANGUAGES: | ||||||
|  |             if lang_code == settings.LANGUAGE_CODE: | ||||||
|  |                 continue | ||||||
|  |  | ||||||
|  |             assert obj.translated.get(lang_code) is None | ||||||
|  |  | ||||||
|  |     def test_translate(self): | ||||||
|  |         """Tests whether casting the value to a boolean results in the value | ||||||
|  |         being returned in the currently active language as a boolean.""" | ||||||
|  |  | ||||||
|  |         obj = self.TestModel() | ||||||
|  |         for lang_code, _ in settings.LANGUAGES: | ||||||
|  |             obj.translated.set(lang_code, True) | ||||||
|  |         obj.save() | ||||||
|  |  | ||||||
|  |         obj.refresh_from_db() | ||||||
|  |         for lang_code, _ in settings.LANGUAGES: | ||||||
|  |             with translation.override(lang_code): | ||||||
|  |                 assert bool(obj.translated) is True | ||||||
|  |                 assert obj.translated.translate() is True | ||||||
|  |  | ||||||
|  |     def test_translate_primary_fallback(self): | ||||||
|  |         """Tests whether casting the value to a boolean results in the value | ||||||
|  |         being returned in the active language and falls back to the primary | ||||||
|  |         language if there is no value in that language.""" | ||||||
|  |  | ||||||
|  |         obj = self.TestModel() | ||||||
|  |         obj.translated.set(settings.LANGUAGE_CODE, True) | ||||||
|  |  | ||||||
|  |         secondary_language = settings.LANGUAGES[-1][0] | ||||||
|  |         assert obj.translated.get(secondary_language) is None | ||||||
|  |  | ||||||
|  |         with translation.override(secondary_language): | ||||||
|  |             assert obj.translated.translate() is True | ||||||
|  |             assert bool(obj.translated) is True | ||||||
|  |  | ||||||
|  |     def test_get_default_value(self): | ||||||
|  |         """Tests whether getting the value in a specific language properly | ||||||
|  |         returns the specified default in case it is not available.""" | ||||||
|  |  | ||||||
|  |         obj = self.TestModel() | ||||||
|  |         obj.translated.set(settings.LANGUAGE_CODE, True) | ||||||
|  |  | ||||||
|  |         secondary_language = settings.LANGUAGES[-1][0] | ||||||
|  |         assert obj.translated.get(secondary_language) is None | ||||||
|  |         assert obj.translated.get(secondary_language, False) is False | ||||||
|  |  | ||||||
|  |     def test_completely_optional(self): | ||||||
|  |         """Tests whether having all languages optional works properly.""" | ||||||
|  |  | ||||||
|  |         model = get_fake_model( | ||||||
|  |             { | ||||||
|  |                 "translated": LocalizedBooleanField( | ||||||
|  |                     null=True, required=[], blank=True | ||||||
|  |                 ) | ||||||
|  |             } | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         obj = model() | ||||||
|  |         obj.save() | ||||||
|  |  | ||||||
|  |         for lang_code, _ in settings.LANGUAGES: | ||||||
|  |             assert getattr(obj.translated, lang_code) is None | ||||||
|  |  | ||||||
|  |     def test_store_string(self): | ||||||
|  |         """Tests whether the field properly raises an error when trying to | ||||||
|  |         store a non-boolean.""" | ||||||
|  |  | ||||||
|  |         for lang_code, _ in settings.LANGUAGES: | ||||||
|  |             obj = self.TestModel() | ||||||
|  |             with self.assertRaises(IntegrityError): | ||||||
|  |                 obj.translated.set(lang_code, "haha") | ||||||
|  |                 obj.save() | ||||||
|  |  | ||||||
|  |     def test_none_if_illegal_value_stored(self): | ||||||
|  |         """Tests whether None is returned for a language if the value stored in | ||||||
|  |         the database is not a boolean.""" | ||||||
|  |  | ||||||
|  |         obj = self.TestModel() | ||||||
|  |         obj.translated.set(settings.LANGUAGE_CODE, False) | ||||||
|  |         obj.save() | ||||||
|  |  | ||||||
|  |         with connection.cursor() as cursor: | ||||||
|  |             table_name = self.TestModel._meta.db_table | ||||||
|  |             cursor.execute("update %s set translated = 'en=>haha'" % table_name) | ||||||
|  |  | ||||||
|  |         with self.assertRaises(ValueError): | ||||||
|  |             obj.refresh_from_db() | ||||||
|  |  | ||||||
|  |     def test_default_value(self): | ||||||
|  |         """Tests whether a default is properly set when specified.""" | ||||||
|  |  | ||||||
|  |         model = get_fake_model( | ||||||
|  |             { | ||||||
|  |                 "translated": LocalizedBooleanField( | ||||||
|  |                     default={settings.LANGUAGE_CODE: True} | ||||||
|  |                 ) | ||||||
|  |             } | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         obj = model.objects.create() | ||||||
|  |         assert obj.translated.get(settings.LANGUAGE_CODE) is True | ||||||
|  |  | ||||||
|  |         obj = model() | ||||||
|  |         for lang_code, _ in settings.LANGUAGES: | ||||||
|  |             obj.translated.set(lang_code, None) | ||||||
|  |         obj.save() | ||||||
|  |  | ||||||
|  |         for lang_code, _ in settings.LANGUAGES: | ||||||
|  |             if lang_code == settings.LANGUAGE_CODE: | ||||||
|  |                 assert obj.translated.get(lang_code) is True | ||||||
|  |             else: | ||||||
|  |                 assert obj.translated.get(lang_code) is None | ||||||
|  |  | ||||||
|  |     def test_default_value_update(self): | ||||||
|  |         """Tests whether a default is properly set when specified during | ||||||
|  |         updates.""" | ||||||
|  |  | ||||||
|  |         model = get_fake_model( | ||||||
|  |             { | ||||||
|  |                 "translated": LocalizedBooleanField( | ||||||
|  |                     default={settings.LANGUAGE_CODE: True}, null=True | ||||||
|  |                 ) | ||||||
|  |             } | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         obj = model.objects.create( | ||||||
|  |             translated=LocalizedBooleanValue({settings.LANGUAGE_CODE: False}) | ||||||
|  |         ) | ||||||
|  |         assert obj.translated.get(settings.LANGUAGE_CODE) is False | ||||||
|  |  | ||||||
|  |         model.objects.update( | ||||||
|  |             translated=LocalizedBooleanValue({settings.LANGUAGE_CODE: None}) | ||||||
|  |         ) | ||||||
|  |         obj.refresh_from_db() | ||||||
|  |         assert obj.translated.get(settings.LANGUAGE_CODE) is True | ||||||
|  |  | ||||||
|  |     def test_callable_default_value(self): | ||||||
|  |         output = {"en": True} | ||||||
|  |  | ||||||
|  |         def func(): | ||||||
|  |             return output | ||||||
|  |  | ||||||
|  |         model = get_fake_model({"test": LocalizedBooleanField(default=func)}) | ||||||
|  |         obj = model.objects.create() | ||||||
|  |  | ||||||
|  |         assert obj.test["en"] == output["en"] | ||||||
| @@ -121,8 +121,8 @@ class LocalizedFieldTestCase(TestCase): | |||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def test_get_prep_value(): |     def test_get_prep_value(): | ||||||
|         """"Tests whether the :see:get_prep_value function produces the |         """Tests whether the :see:get_prep_value function produces the expected | ||||||
|         expected dictionary.""" |         dictionary.""" | ||||||
|  |  | ||||||
|         input_data = get_init_values() |         input_data = get_init_values() | ||||||
|         localized_value = LocalizedValue(input_data) |         localized_value = LocalizedValue(input_data) | ||||||
|   | |||||||
| @@ -38,6 +38,17 @@ class LocalizedValueTestCase(TestCase): | |||||||
|         for lang_code, _ in settings.LANGUAGES: |         for lang_code, _ in settings.LANGUAGES: | ||||||
|             assert getattr(value, lang_code) is None |             assert getattr(value, lang_code) is None | ||||||
|  |  | ||||||
|  |     @staticmethod | ||||||
|  |     def test_is_empty(): | ||||||
|  |         """Tests whether a newly constructed :see:LocalizedValue without any | ||||||
|  |         content is considered "empty".""" | ||||||
|  |  | ||||||
|  |         value = LocalizedValue() | ||||||
|  |         assert value.is_empty() | ||||||
|  |  | ||||||
|  |         value.set(settings.LANGUAGE_CODE, "my value") | ||||||
|  |         assert not value.is_empty() | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def test_init_array(): |     def test_init_array(): | ||||||
|         """Tests whether the __init__ function of :see:LocalizedValue properly |         """Tests whether the __init__ function of :see:LocalizedValue properly | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user