mirror of
				https://github.com/SectorLabs/django-localized-fields.git
				synced 2025-10-26 00:48:57 +03:00 
			
		
		
		
	Compare commits
	
		
			53 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | a198440a64 | ||
|  | 5945c3f531 | ||
|  | 3fd862ce4d | ||
|  | dad7f164cd | ||
|  | 6490f3908c | ||
|  | 54ad6eb434 | ||
|  | 893fe0f5ab | ||
|  | 3de1492a58 | ||
|  | 946e9a67c4 | ||
|  | 36f6e946b0 | ||
|  | 909ebfee69 | ||
|  | 95284e6fd0 | ||
|  | e84a5e4ff1 | ||
|  | 472c7bbc41 | ||
|  | 8cc50889ec | ||
|  | 8494615d05 | ||
|  | f0541c047b | ||
|  | 84658f6010 | ||
|  | 0e29871458 | ||
|  | ffe235d3ac | ||
|  | d7db2c58c0 | ||
|  | 6a7545a910 | ||
|  | 11bf4ee88a | ||
|  | d3223eca53 | ||
|  | f0ac0f7f25 | ||
|  | 93ffce557c | ||
|  | 7c432baec7 | ||
|  | 476a20ba88 | ||
|  | ad99b77bcd | ||
|  | 151250505d | ||
|  | d8b872758c | ||
|  | a0ca977cab | ||
|  | eb2cb6b244 | ||
|  | ed15fb0079 | ||
|  | f59904f8ea | ||
|  | f20966d6d2 | ||
|  | 25417b5815 | ||
|  | abd1587ca0 | ||
|  | 60fc79e9ff | ||
|  | ca470fc577 | ||
|  | fac1a595aa | ||
|  | d66f5085a8 | ||
|  | c6e8321ae7 | ||
|  | b2f50ec82b | ||
|  | ff836836bf | ||
|  | acf8867974 | ||
|  | 4922a1b93f | ||
|  | d308e773cf | ||
|  | b3b88d6d28 | ||
|  | 6c902229ce | ||
|  | 4f83cbf4ed | ||
|  | 8ba0540237 | ||
|  | b3624916b2 | 
							
								
								
									
										92
									
								
								.circleci/config.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								.circleci/config.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | |||||||
|  | version: 2 | ||||||
|  | jobs: | ||||||
|  |     test-python35: | ||||||
|  |         docker: | ||||||
|  |             - image: python:3.5-alpine | ||||||
|  |             - image: postgres:11.0 | ||||||
|  |               environment: | ||||||
|  |                   POSTGRES_DB: 'localizedfields' | ||||||
|  |                   POSTGRES_USER: 'localizedfields' | ||||||
|  |                   POSTGRES_PASSWORD: 'localizedfields' | ||||||
|  |         steps: | ||||||
|  |             - checkout | ||||||
|  |             - run: | ||||||
|  |                 name: Install packages | ||||||
|  |                 command: apk add postgresql-libs gcc musl-dev postgresql-dev git | ||||||
|  |             - run: | ||||||
|  |                 name: Install Python packages | ||||||
|  |                 command: pip install -r requirements/test.txt | ||||||
|  |             - run: | ||||||
|  |                 name: Run tests | ||||||
|  |                 command: tox -e 'py35-dj{111,20,21}' | ||||||
|  |                 environment: | ||||||
|  |                     DATABASE_URL: 'postgres://localizedfields:localizedfields@localhost:5432/localizedfields' | ||||||
|  |  | ||||||
|  |     test-python36: | ||||||
|  |         docker: | ||||||
|  |             - image: python:3.6-alpine | ||||||
|  |             - image: postgres:11.0 | ||||||
|  |               environment: | ||||||
|  |                   POSTGRES_DB: 'localizedfields' | ||||||
|  |                   POSTGRES_USER: 'localizedfields' | ||||||
|  |                   POSTGRES_PASSWORD: 'localizedfields' | ||||||
|  |         steps: | ||||||
|  |             - checkout | ||||||
|  |             - run: | ||||||
|  |                 name: Install packages | ||||||
|  |                 command: apk add postgresql-libs gcc musl-dev postgresql-dev git | ||||||
|  |             - run: | ||||||
|  |                 name: Install Python packages | ||||||
|  |                 command: pip install -r requirements/test.txt | ||||||
|  |             - run: | ||||||
|  |                 name: Run tests | ||||||
|  |                 command: tox -e 'py36-dj{111,20,21}' | ||||||
|  |                 environment: | ||||||
|  |                     DATABASE_URL: 'postgres://localizedfields:localizedfields@localhost:5432/localizedfields' | ||||||
|  |  | ||||||
|  |     test-python37: | ||||||
|  |         docker: | ||||||
|  |             - image: python:3.7-alpine | ||||||
|  |             - image: postgres:11.0 | ||||||
|  |               environment: | ||||||
|  |                   POSTGRES_DB: 'localizedfields' | ||||||
|  |                   POSTGRES_USER: 'localizedfields' | ||||||
|  |                   POSTGRES_PASSWORD: 'localizedfields' | ||||||
|  |         steps: | ||||||
|  |             - checkout | ||||||
|  |             - run: | ||||||
|  |                 name: Install packages | ||||||
|  |                 command: apk add postgresql-libs gcc musl-dev postgresql-dev git | ||||||
|  |             - run: | ||||||
|  |                 name: Install Python packages | ||||||
|  |                 command: pip install -r requirements/test.txt | ||||||
|  |             - run: | ||||||
|  |                 name: Run tests | ||||||
|  |                 command: tox -e 'py37-dj{111,20,21}' | ||||||
|  |                 environment: | ||||||
|  |                     DATABASE_URL: 'postgres://localizedfields:localizedfields@localhost:5432/localizedfields' | ||||||
|  |  | ||||||
|  |     lint: | ||||||
|  |         docker: | ||||||
|  |             - image: python:3.5-alpine | ||||||
|  |         steps: | ||||||
|  |             - checkout | ||||||
|  |             - run: | ||||||
|  |                 name: Install packages | ||||||
|  |                 command: apk add postgresql-libs gcc musl-dev postgresql-dev git | ||||||
|  |             - run: | ||||||
|  |                 name: Install Python packages | ||||||
|  |                 command: pip install -r requirements/test.txt | ||||||
|  |             - run: | ||||||
|  |                 name: Lint code | ||||||
|  |                 command: python setup.py lint | ||||||
|  |  | ||||||
|  |  | ||||||
|  | workflows: | ||||||
|  |     version: 2 | ||||||
|  |     build: | ||||||
|  |         jobs: | ||||||
|  |             - test-python35 | ||||||
|  |             - test-python36 | ||||||
|  |             - test-python37 | ||||||
|  |             - lint | ||||||
| @@ -1,5 +1,3 @@ | |||||||
| [run] | [run] | ||||||
| include = localized_fields/* | include = localized_fields/* | ||||||
| omit = *migrations*, *tests* | omit = *migrations*, *tests* | ||||||
| plugins = |  | ||||||
|     django_coverage_plugin |  | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,5 +1,6 @@ | |||||||
| # Ignore virtual environments | # Ignore virtual environments | ||||||
| env/ | env/ | ||||||
|  | .env/ | ||||||
|  |  | ||||||
| # Ignore Python byte code cache | # Ignore Python byte code cache | ||||||
| *.pyc | *.pyc | ||||||
|   | |||||||
| @@ -1,34 +0,0 @@ | |||||||
| checks: |  | ||||||
|     python: |  | ||||||
|         code_rating: true |  | ||||||
|         duplicate_code: true |  | ||||||
| tools: |  | ||||||
|     pylint: |  | ||||||
|         python_version: '3' |  | ||||||
|         config_file: .pylintrc |  | ||||||
| filter: |  | ||||||
|     excluded_paths: |  | ||||||
|         - '*/tests/*' |  | ||||||
|         - '*/migrations/*' |  | ||||||
| build: |  | ||||||
|     environment: |  | ||||||
|         python: '3.5.0' |  | ||||||
|         variables: |  | ||||||
|             DJANGO_SETTINGS_MODULES: settings |  | ||||||
|             DATABASE_URL: postgres://scrutinizer:scrutinizer@localhost:5434/localized_fields |  | ||||||
|         postgresql: true |  | ||||||
|  |  | ||||||
|     dependencies: |  | ||||||
|         override: |  | ||||||
|             - 'pip install -r requirements/test.txt' |  | ||||||
|     tests: |  | ||||||
|         override: |  | ||||||
|             - |  | ||||||
|                 command: pep8 ./localized_fields/ |  | ||||||
|             - |  | ||||||
|                 command: flake8 ./localized_fields/ |  | ||||||
|             - |  | ||||||
|                 command: tox |  | ||||||
|                 coverage: |  | ||||||
|                     file: '.coverage' |  | ||||||
|                     format: 'py-cc' |  | ||||||
							
								
								
									
										18
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,18 +0,0 @@ | |||||||
| env: |  | ||||||
|  - DJANGO_SETTINGS_MODULE=settings |  | ||||||
| sudo: true |  | ||||||
| before_install: |  | ||||||
|   - sudo apt-get update -qq |  | ||||||
|   - sudo apt-get install -qq build-essential gettext python-dev zlib1g-dev libpq-dev xvfb |  | ||||||
|   - sudo apt-get install -qq libtiff4-dev libjpeg8-dev libfreetype6-dev liblcms1-dev libwebp-dev |  | ||||||
|   - sudo apt-get install -qq graphviz-dev python-setuptools python3-dev python-virtualenv python-pip |  | ||||||
|   - sudo apt-get install -qq firefox automake libtool libreadline6 libreadline6-dev libreadline-dev |  | ||||||
|   - sudo apt-get install -qq libsqlite3-dev libxml2 libxml2-dev libssl-dev libbz2-dev wget curl llvm |  | ||||||
| language: python |  | ||||||
| python: |  | ||||||
| - "3.5" |  | ||||||
| services: |  | ||||||
|  - postgresql |  | ||||||
| install: "pip install -r requirements/test.txt" |  | ||||||
| script: |  | ||||||
|     - coverage run manage.py test |  | ||||||
							
								
								
									
										33
									
								
								README.rst
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								README.rst
									
									
									
									
									
								
							| @@ -1,11 +1,8 @@ | |||||||
| django-localized-fields | django-localized-fields | ||||||
| ======================= | ======================= | ||||||
|  |  | ||||||
| .. image:: https://scrutinizer-ci.com/g/SectorLabs/django-localized-fields/badges/quality-score.png | .. image:: https://circleci.com/gh/SectorLabs/django-localized-fields/tree/v5.x.svg?style=svg | ||||||
|     :target: https://scrutinizer-ci.com/g/SectorLabs/django-localized-fields/ |     :target: https://circleci.com/gh/SectorLabs/django-localized-fields/tree/v5.x | ||||||
|  |  | ||||||
| .. image:: https://scrutinizer-ci.com/g/SectorLabs/django-localized-fields/badges/coverage.png |  | ||||||
|     :target: https://scrutinizer-ci.com/g/SectorLabs/django-localized-fields/ |  | ||||||
|  |  | ||||||
| .. image:: https://img.shields.io/github/license/SectorLabs/django-localized-fields.svg | .. image:: https://img.shields.io/github/license/SectorLabs/django-localized-fields.svg | ||||||
|     :target: https://github.com/SectorLabs/django-localized-fields/blob/master/LICENSE.md |     :target: https://github.com/SectorLabs/django-localized-fields/blob/master/LICENSE.md | ||||||
| @@ -17,11 +14,17 @@ django-localized-fields | |||||||
|  |  | ||||||
| This package requires Python 3.5 or newer, Django 1.11 or newer and PostgreSQL 9.6 or newer. | This package requires Python 3.5 or newer, Django 1.11 or newer and PostgreSQL 9.6 or newer. | ||||||
|  |  | ||||||
|  | ---- | ||||||
|  |  | ||||||
|  | **This README is for v5.x. Check out the master branch for v6.x and newer.** | ||||||
|  |  | ||||||
|  | ---- | ||||||
|  |  | ||||||
| Contributors | Contributors | ||||||
| ------------ | ------------ | ||||||
|  |  | ||||||
| * `seroy <https://github.com/seroy/>`_ | * `seroy <https://github.com/seroy/>`_ | ||||||
| * `unaizalakain <https://github.com/unaizalakain/>`_ | * `umazalakain <https://github.com/umazalakain/>`_ | ||||||
|  |  | ||||||
| Installation | Installation | ||||||
| ------------ | ------------ | ||||||
| @@ -29,14 +32,14 @@ Installation | |||||||
|  |  | ||||||
|     .. code-block:: bash |     .. code-block:: bash | ||||||
|  |  | ||||||
|         $ pip install django-localized-fields |         $ pip install django-localized-fields==5.4.1 | ||||||
|  |  | ||||||
| 2. Add ``localized_fields`` and ``django.contrib.postgres`` to your ``INSTALLED_APPS``: | 2. Add ``localized_fields`` and ``django.contrib.postgres`` to your ``INSTALLED_APPS``: | ||||||
|  |  | ||||||
|      .. code-block:: bash |      .. code-block:: bash | ||||||
|  |  | ||||||
|         INSTALLED_APPS = [ |         INSTALLED_APPS = [ | ||||||
|             .... |             ... | ||||||
|  |  | ||||||
|             'django.contrib.postgres', |             'django.contrib.postgres', | ||||||
|             'localized_fields.apps.LocalizedFieldsConfig' |             'localized_fields.apps.LocalizedFieldsConfig' | ||||||
| @@ -64,6 +67,13 @@ Installation | |||||||
|              ('ro', 'Romanian') |              ('ro', 'Romanian') | ||||||
|          ) |          ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 4. Apply migrations to enable the HStore extension: | ||||||
|  |  | ||||||
|  |      .. code-block:: bash | ||||||
|  |  | ||||||
|  | 	 python manage.py migrate | ||||||
|  |  | ||||||
| Usage | Usage | ||||||
| ----- | ----- | ||||||
|  |  | ||||||
| @@ -282,7 +292,7 @@ Besides ``LocalizedField``, there's also: | |||||||
|  |  | ||||||
|     Allows storing integers in multiple languages. This works exactly like ``LocalizedField`` except that |     Allows storing integers in multiple languages. This works exactly like ``LocalizedField`` except that | ||||||
|     all values must be integers. Do note that values are stored as strings in your database because |     all values must be integers. Do note that values are stored as strings in your database because | ||||||
|     the backing field type is ``hstore``, which only allows storing integers. The ``LocalizedIntegerField`` |     the backing field type is ``hstore``, which only allows storing strings. The ``LocalizedIntegerField`` | ||||||
|     takes care of ensuring that all values are integers and converts the stored strings back to integers |     takes care of ensuring that all values are integers and converts the stored strings back to integers | ||||||
|     when retrieving them from the database. Do not expect to be able to do queries such as: |     when retrieving them from the database. Do not expect to be able to do queries such as: | ||||||
|  |  | ||||||
| @@ -348,6 +358,7 @@ Experimental feature | |||||||
| ^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^ | ||||||
| Enables the following experimental features: | Enables the following experimental features: | ||||||
|     * ``LocalizedField`` will return ``None`` instead of an empty ``LocalizedValue`` if there is no database value. |     * ``LocalizedField`` will return ``None`` instead of an empty ``LocalizedValue`` if there is no database value. | ||||||
|  |     * ``LocalizedField`` lookups will lookup by currently active language instead of HStoreField | ||||||
|  |  | ||||||
| .. code-block:: python | .. code-block:: python | ||||||
|  |  | ||||||
| @@ -371,6 +382,10 @@ To enable widgets in the admin, you need to inherit from ``LocalizedFieldsAdminM | |||||||
|     admin.site.register(MyLocalizedModel, MyLocalizedModelAdmin) |     admin.site.register(MyLocalizedModel, MyLocalizedModelAdmin) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | .. image:: ./images/admin-widget.png | ||||||
|  |     :alt: The appearance of admin widget | ||||||
|  |  | ||||||
|  |  | ||||||
| Frequently asked questions (FAQ) | Frequently asked questions (FAQ) | ||||||
| -------------------------------- | -------------------------------- | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										
											BIN
										
									
								
								images/admin-widget.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								images/admin-widget.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 57 KiB | 
| @@ -0,0 +1 @@ | |||||||
|  | default_app_config = 'localized_fields.apps.LocalizedFieldsConfig' | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| from django.contrib.admin import ModelAdmin |  | ||||||
|  |  | ||||||
| from . import widgets | from . import widgets | ||||||
| from .fields import LocalizedField, LocalizedCharField, LocalizedTextField, \ | from .fields import LocalizedField, LocalizedCharField, LocalizedTextField, \ | ||||||
|     LocalizedFileField |     LocalizedFileField | ||||||
| @@ -13,7 +11,7 @@ FORMFIELD_FOR_LOCALIZED_FIELDS_DEFAULTS = { | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| class LocalizedFieldsAdminMixin(ModelAdmin): | class LocalizedFieldsAdminMixin: | ||||||
|     """Mixin for making the fancy widgets work in Django Admin.""" |     """Mixin for making the fancy widgets work in Django Admin.""" | ||||||
|  |  | ||||||
|     class Media: |     class Media: | ||||||
| @@ -24,6 +22,7 @@ class LocalizedFieldsAdminMixin(ModelAdmin): | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         js = ( |         js = ( | ||||||
|  |             'admin/js/jquery.init.js', | ||||||
|             'localized_fields/localized-fields-admin.js', |             'localized_fields/localized-fields-admin.js', | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,5 +1,21 @@ | |||||||
|  | import inspect | ||||||
|  |  | ||||||
| from django.apps import AppConfig | from django.apps import AppConfig | ||||||
|  | from django.conf import settings | ||||||
|  |  | ||||||
|  | from . import lookups | ||||||
|  | from .fields import LocalizedField | ||||||
|  | from .lookups import LocalizedLookupMixin | ||||||
|  |  | ||||||
|  |  | ||||||
| class LocalizedFieldsConfig(AppConfig): | class LocalizedFieldsConfig(AppConfig): | ||||||
|     name = 'localized_fields' |     name = 'localized_fields' | ||||||
|  |  | ||||||
|  |     def ready(self): | ||||||
|  |         if getattr(settings, 'LOCALIZED_FIELDS_EXPERIMENTAL', False): | ||||||
|  |             for _, clazz in inspect.getmembers(lookups): | ||||||
|  |                 if not inspect.isclass(clazz) or clazz is LocalizedLookupMixin: | ||||||
|  |                     continue | ||||||
|  |  | ||||||
|  |                 if issubclass(clazz, LocalizedLookupMixin): | ||||||
|  |                     LocalizedField.register_lookup(clazz) | ||||||
|   | |||||||
| @@ -41,7 +41,7 @@ class LocalizedValueDescriptor: | |||||||
|  |  | ||||||
|         if self.field.name in instance.__dict__: |         if self.field.name in instance.__dict__: | ||||||
|             value = instance.__dict__[self.field.name] |             value = instance.__dict__[self.field.name] | ||||||
|         elif instance.pk is not None: |         elif not instance._state.adding: | ||||||
|             instance.refresh_from_db(fields=[self.field.name]) |             instance.refresh_from_db(fields=[self.field.name]) | ||||||
|             value = getattr(instance, self.field.name) |             value = getattr(instance, self.field.name) | ||||||
|         else: |         else: | ||||||
|   | |||||||
| @@ -17,17 +17,17 @@ class LocalizedAutoSlugField(LocalizedField): | |||||||
|     """Automatically provides slugs for a localized |     """Automatically provides slugs for a localized | ||||||
|     field upon saving.""" |     field upon saving.""" | ||||||
|  |  | ||||||
|  |     warnings.warn( | ||||||
|  |         'LocalizedAutoSlug is deprecated and will be removed in the next major version.', | ||||||
|  |         DeprecationWarning | ||||||
|  |     ) | ||||||
|  |  | ||||||
|     def __init__(self, *args, **kwargs): |     def __init__(self, *args, **kwargs): | ||||||
|         """Initializes a new instance of :see:LocalizedAutoSlugField.""" |         """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) | ||||||
|  |  | ||||||
|         warnings.warn( |  | ||||||
|             'LocalizedAutoSlug is deprecated and will be removed in the next major version.', |  | ||||||
|             DeprecationWarning |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|         super(LocalizedAutoSlugField, self).__init__( |         super(LocalizedAutoSlugField, self).__init__( | ||||||
|             *args, |             *args, | ||||||
|             **kwargs |             **kwargs | ||||||
|   | |||||||
| @@ -165,7 +165,7 @@ class LocalizedField(HStoreField): | |||||||
|         can store in the database. |         can store in the database. | ||||||
|  |  | ||||||
|         For example, when all the language fields are |         For example, when all the language fields are | ||||||
|         left empty, and the field is allows to be null, |         left empty, and the field is allowed to be null, | ||||||
|         we will store None instead of empty keys. |         we will store None instead of empty keys. | ||||||
|  |  | ||||||
|         Arguments: |         Arguments: | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ from django.db.utils import IntegrityError | |||||||
|  |  | ||||||
| from .field import LocalizedField | from .field import LocalizedField | ||||||
| from ..value import LocalizedValue, LocalizedIntegerValue | from ..value import LocalizedValue, LocalizedIntegerValue | ||||||
|  | from ..forms import LocalizedIntegerFieldForm | ||||||
|  |  | ||||||
|  |  | ||||||
| class LocalizedIntegerField(LocalizedField): | class LocalizedIntegerField(LocalizedField): | ||||||
| @@ -63,6 +64,15 @@ class LocalizedIntegerField(LocalizedField): | |||||||
|  |  | ||||||
|         return prepped_value |         return prepped_value | ||||||
|  |  | ||||||
|  |     def formfield(self, **kwargs): | ||||||
|  |         """Gets the form field associated with this field.""" | ||||||
|  |         defaults = { | ||||||
|  |             'form_class': LocalizedIntegerFieldForm | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         defaults.update(kwargs) | ||||||
|  |         return super().formfield(**defaults) | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def _convert_localized_value(value: LocalizedValue) -> LocalizedIntegerValue: |     def _convert_localized_value(value: LocalizedValue) -> LocalizedIntegerValue: | ||||||
|         """Converts from :see:LocalizedValue to :see:LocalizedIntegerValue.""" |         """Converts from :see:LocalizedValue to :see:LocalizedIntegerValue.""" | ||||||
|   | |||||||
| @@ -6,9 +6,9 @@ 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 LocalizedValue, LocalizedStringValue, \ | from .value import LocalizedValue, LocalizedStringValue, \ | ||||||
|     LocalizedFileValue |     LocalizedFileValue, LocalizedIntegerValue | ||||||
| from .widgets import LocalizedFieldWidget, LocalizedCharFieldWidget, \ | from .widgets import LocalizedFieldWidget, LocalizedCharFieldWidget, \ | ||||||
|     LocalizedFileWidget |     LocalizedFileWidget, AdminLocalizedIntegerFieldWidget | ||||||
|  |  | ||||||
|  |  | ||||||
| class LocalizedFieldForm(forms.MultiValueField): | class LocalizedFieldForm(forms.MultiValueField): | ||||||
| @@ -79,6 +79,14 @@ class LocalizedTextFieldForm(LocalizedFieldForm): | |||||||
|     value_class = LocalizedStringValue |     value_class = LocalizedStringValue | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedIntegerFieldForm(LocalizedFieldForm): | ||||||
|  |     """Form for a localized integer field, allows editing | ||||||
|  |     the field in multiple languages.""" | ||||||
|  |  | ||||||
|  |     widget = AdminLocalizedIntegerFieldWidget | ||||||
|  |     value_class = LocalizedIntegerValue | ||||||
|  |  | ||||||
|  |  | ||||||
| class LocalizedFileFieldForm(LocalizedFieldForm, forms.FileField): | class LocalizedFileFieldForm(LocalizedFieldForm, forms.FileField): | ||||||
|     """Form for a localized file field, allows editing |     """Form for a localized file field, allows editing | ||||||
|     the field in multiple languages.""" |     the field in multiple languages.""" | ||||||
|   | |||||||
							
								
								
									
										80
									
								
								localized_fields/lookups.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								localized_fields/lookups.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | |||||||
|  | from django.conf import settings | ||||||
|  | from django.contrib.postgres.fields.hstore import KeyTransform | ||||||
|  | from django.contrib.postgres.lookups import (SearchLookup, TrigramSimilar, | ||||||
|  |                                              Unaccent) | ||||||
|  | from django.db.models.expressions import Col | ||||||
|  | from django.db.models.lookups import (Contains, EndsWith, Exact, IContains, | ||||||
|  |                                       IEndsWith, IExact, In, IRegex, IsNull, | ||||||
|  |                                       IStartsWith, Regex, StartsWith) | ||||||
|  | from django.utils import translation | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedLookupMixin(): | ||||||
|  |     def process_lhs(self, qn, connection): | ||||||
|  |         if isinstance(self.lhs, Col): | ||||||
|  |             language = translation.get_language() or settings.LANGUAGE_CODE | ||||||
|  |             self.lhs = KeyTransform(language, self.lhs) | ||||||
|  |         return super().process_lhs(qn, connection) | ||||||
|  |  | ||||||
|  |     def get_prep_lookup(self): | ||||||
|  |         return str(self.rhs) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedSearchLookup(LocalizedLookupMixin, SearchLookup): | ||||||
|  |     pass | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedUnaccent(LocalizedLookupMixin, Unaccent): | ||||||
|  |     pass | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedTrigramSimilair(LocalizedLookupMixin, TrigramSimilar): | ||||||
|  |     pass | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedExact(LocalizedLookupMixin, Exact): | ||||||
|  |     pass | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedIExact(LocalizedLookupMixin, IExact): | ||||||
|  |     pass | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedIn(LocalizedLookupMixin, In): | ||||||
|  |     pass | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedContains(LocalizedLookupMixin, Contains): | ||||||
|  |     pass | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedIContains(LocalizedLookupMixin, IContains): | ||||||
|  |     pass | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedStartsWith(LocalizedLookupMixin, StartsWith): | ||||||
|  |     pass | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedIStartsWith(LocalizedLookupMixin, IStartsWith): | ||||||
|  |     pass | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedEndsWith(LocalizedLookupMixin, EndsWith): | ||||||
|  |     pass | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedIEndsWith(LocalizedLookupMixin, IEndsWith): | ||||||
|  |     pass | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedIsNullWith(LocalizedLookupMixin, IsNull): | ||||||
|  |     pass | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedRegexWith(LocalizedLookupMixin, Regex): | ||||||
|  |     pass | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedIRegexWith(LocalizedLookupMixin, IRegex): | ||||||
|  |     pass | ||||||
							
								
								
									
										14
									
								
								localized_fields/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								localized_fields/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | # Generated by Django 2.1 on 2018-08-27 08:05 | ||||||
|  |  | ||||||
|  | from django.contrib.postgres.operations import HStoreExtension | ||||||
|  | from django.db import migrations | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  |  | ||||||
|  |     dependencies = [ | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  |     operations = [ | ||||||
|  |         HStoreExtension(), | ||||||
|  |     ] | ||||||
							
								
								
									
										0
									
								
								localized_fields/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								localized_fields/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -1,7 +1,8 @@ | |||||||
| import deprecation | import deprecation | ||||||
| import collections |  | ||||||
|  |  | ||||||
| from typing import Optional | from typing import Optional | ||||||
|  | from collections.abc import Iterable | ||||||
|  |  | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
| from django.utils import translation | from django.utils import translation | ||||||
|  |  | ||||||
| @@ -97,7 +98,7 @@ class LocalizedValue(dict): | |||||||
|                 lang_value = value.get(lang_code, self.default_value) |                 lang_value = value.get(lang_code, self.default_value) | ||||||
|                 self.set(lang_code, lang_value) |                 self.set(lang_code, lang_value) | ||||||
|  |  | ||||||
|         elif isinstance(value, collections.Iterable): |         elif isinstance(value, Iterable): | ||||||
|             for val in value: |             for val in value: | ||||||
|                 self._interpret_value(val) |                 self._interpret_value(val) | ||||||
|  |  | ||||||
| @@ -228,3 +229,9 @@ class LocalizedIntegerValue(LocalizedValue): | |||||||
|             return self.default_value |             return self.default_value | ||||||
|  |  | ||||||
|         return int(value) |         return int(value) | ||||||
|  |  | ||||||
|  |     def __str__(self) -> str: | ||||||
|  |         """Returns string representation of value""" | ||||||
|  |  | ||||||
|  |         value = self.translate() | ||||||
|  |         return str(value) if value is not None else '' | ||||||
|   | |||||||
| @@ -120,3 +120,7 @@ class AdminLocalizedCharFieldWidget(AdminLocalizedFieldWidget): | |||||||
|  |  | ||||||
| class AdminLocalizedFileFieldWidget(AdminLocalizedFieldWidget): | class AdminLocalizedFileFieldWidget(AdminLocalizedFieldWidget): | ||||||
|     widget = widgets.AdminFileWidget |     widget = widgets.AdminFileWidget | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class AdminLocalizedIntegerFieldWidget(AdminLocalizedFieldWidget): | ||||||
|  |     widget = widgets.AdminIntegerFieldWidget | ||||||
|   | |||||||
| @@ -1,2 +1,2 @@ | |||||||
| django-postgres-extra==1.21a9 | django-postgres-extra==1.22 | ||||||
| deprecation==2.0.3 | deprecation==2.0.7 | ||||||
|   | |||||||
| @@ -2,15 +2,9 @@ | |||||||
|  |  | ||||||
| django-autoslug==1.9.3 | django-autoslug==1.9.3 | ||||||
| django-bleach==0.3.0 | django-bleach==0.3.0 | ||||||
| django-coverage-plugin==1.3.1 |  | ||||||
| psycopg2==2.7.3.2 | psycopg2==2.7.3.2 | ||||||
| pylint==1.8.1 |  | ||||||
| pylint-common==0.2.5 |  | ||||||
| pylint-django==0.8.0 |  | ||||||
| pylint-plugin-utils==0.2.6 |  | ||||||
| coverage==4.4.2 | coverage==4.4.2 | ||||||
| django-coverage-plugin==1.3.1 | flake8==3.6.0 | ||||||
| flake8==3.5.0 |  | ||||||
| pep8==1.7.1 | pep8==1.7.1 | ||||||
| dj-database-url==0.4.2 | dj-database-url==0.4.2 | ||||||
| tox==2.9.1 | tox==2.9.1 | ||||||
|   | |||||||
							
								
								
									
										26
									
								
								settings.py
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								settings.py
									
									
									
									
									
								
							| @@ -21,10 +21,36 @@ LANGUAGES = ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| INSTALLED_APPS = ( | INSTALLED_APPS = ( | ||||||
|  |     'django.contrib.auth', | ||||||
|  |     'django.contrib.contenttypes', | ||||||
|  |     'django.contrib.admin', | ||||||
|  |     'django.contrib.messages', | ||||||
|     'localized_fields', |     'localized_fields', | ||||||
|     'tests', |     'tests', | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | TEMPLATES = [ | ||||||
|  |     { | ||||||
|  |         "BACKEND": "django.template.backends.django.DjangoTemplates", | ||||||
|  |         "DIRS": [], | ||||||
|  |         "APP_DIRS": True, | ||||||
|  |         "OPTIONS": { | ||||||
|  |             "context_processors": [ | ||||||
|  |                 "django.template.context_processors.debug", | ||||||
|  |                 "django.template.context_processors.request", | ||||||
|  |                 "django.contrib.auth.context_processors.auth", | ||||||
|  |                 "django.contrib.messages.context_processors.messages", | ||||||
|  |             ], | ||||||
|  |         }, | ||||||
|  |     }, | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | MIDDLEWARE = [ | ||||||
|  |     'django.contrib.messages.middleware.MessageMiddleware', | ||||||
|  |     'django.contrib.sessions.middleware.SessionMiddleware', | ||||||
|  |     'django.contrib.auth.middleware.AuthenticationMiddleware', | ||||||
|  | ] | ||||||
|  |  | ||||||
| # set to a lower number than the default, since | # set to a lower number than the default, since | ||||||
| # we want the tests to be fast, default is 100 | # we want the tests to be fast, default is 100 | ||||||
| LOCALIZED_FIELDS_MAX_RETRIES = 3 | LOCALIZED_FIELDS_MAX_RETRIES = 3 | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| [flake8] | [flake8] | ||||||
| max-line-length = 120 | max-line-length = 120 | ||||||
|  | ignore = E252, W605 | ||||||
| exclude = env,.tox,.git,config/settings,*/migrations/*,*/static/CACHE/*,docs,node_modules | exclude = env,.tox,.git,config/settings,*/migrations/*,*/static/CACHE/*,docs,node_modules | ||||||
|  |  | ||||||
| [pep8] | [pep8] | ||||||
|   | |||||||
							
								
								
									
										40
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								setup.py
									
									
									
									
									
								
							| @@ -1,13 +1,39 @@ | |||||||
| import os | import os | ||||||
|  | import distutils.cmd | ||||||
|  | import subprocess | ||||||
|  |  | ||||||
| from setuptools import find_packages, setup | from setuptools import find_packages, setup | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class BaseCommand(distutils.cmd.Command): | ||||||
|  |     user_options = [] | ||||||
|  |  | ||||||
|  |     def initialize_options(self): | ||||||
|  |         pass | ||||||
|  |  | ||||||
|  |     def finalize_options(self): | ||||||
|  |         pass | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def create_command(text, commands): | ||||||
|  |     """Creates a custom setup.py command.""" | ||||||
|  |  | ||||||
|  |     class CustomCommand(BaseCommand): | ||||||
|  |         description = text | ||||||
|  |  | ||||||
|  |         def run(self): | ||||||
|  |             for cmd in commands: | ||||||
|  |                 subprocess.check_call(cmd) | ||||||
|  |  | ||||||
|  |     return CustomCommand | ||||||
|  |  | ||||||
|  |  | ||||||
| with open(os.path.join(os.path.dirname(__file__), 'README.rst'), encoding='utf-8') as readme: | with open(os.path.join(os.path.dirname(__file__), 'README.rst'), encoding='utf-8') as readme: | ||||||
|     README = readme.read() |     README = readme.read() | ||||||
|  |  | ||||||
| setup( | setup( | ||||||
|     name='django-localized-fields', |     name='django-localized-fields', | ||||||
|     version='5.0a3', |     version='5.4.1', | ||||||
|     packages=find_packages(exclude=['tests']), |     packages=find_packages(exclude=['tests']), | ||||||
|     include_package_data=True, |     include_package_data=True, | ||||||
|     license='MIT License', |     license='MIT License', | ||||||
| @@ -18,9 +44,9 @@ setup( | |||||||
|     author_email='open-source@sectorlabs.ro', |     author_email='open-source@sectorlabs.ro', | ||||||
|     keywords=['django', 'localized', 'language', 'models', 'fields'], |     keywords=['django', 'localized', 'language', 'models', 'fields'], | ||||||
|     install_requires=[ |     install_requires=[ | ||||||
|         'django-postgres-extra>=1.21a11', |         'django-postgres-extra>=1.22,<2.0', | ||||||
|         'Django>=1.11', |         'Django>=1.11', | ||||||
|         'deprecation==2.0.3' |         'deprecation==2.0.7' | ||||||
|     ], |     ], | ||||||
|     classifiers=[ |     classifiers=[ | ||||||
|         'Environment :: Web Environment', |         'Environment :: Web Environment', | ||||||
| @@ -32,5 +58,11 @@ setup( | |||||||
|         'Programming Language :: Python :: 3.5', |         'Programming Language :: Python :: 3.5', | ||||||
|         'Topic :: Internet :: WWW/HTTP', |         'Topic :: Internet :: WWW/HTTP', | ||||||
|         'Topic :: Internet :: WWW/HTTP :: Dynamic Content', |         'Topic :: Internet :: WWW/HTTP :: Dynamic Content', | ||||||
|     ] |     ], | ||||||
|  |     cmdclass={ | ||||||
|  |         'lint': create_command( | ||||||
|  |             'Lints the code', | ||||||
|  |             [['flake8', 'setup.py', 'localized_fields', 'tests']], | ||||||
|  |         ), | ||||||
|  |     }, | ||||||
| ) | ) | ||||||
|   | |||||||
							
								
								
									
										87
									
								
								tests/test_admin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								tests/test_admin.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | |||||||
|  | from django.apps import apps | ||||||
|  | from django.contrib import admin | ||||||
|  | from django.contrib.admin.checks import check_admin_app | ||||||
|  | from django.db import models | ||||||
|  | from django.test import TestCase | ||||||
|  |  | ||||||
|  | from localized_fields.fields import LocalizedField | ||||||
|  | from localized_fields.admin import LocalizedFieldsAdminMixin | ||||||
|  |  | ||||||
|  | from tests.fake_model import get_fake_model | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedFieldsAdminMixinTestCase(TestCase): | ||||||
|  |     """Tests the :see:LocalizedFieldsAdminMixin class.""" | ||||||
|  |  | ||||||
|  |     TestModel = None | ||||||
|  |     TestRelModel = None | ||||||
|  |  | ||||||
|  |     @classmethod | ||||||
|  |     def setUpClass(cls): | ||||||
|  |         """Creates the test model in the database.""" | ||||||
|  |  | ||||||
|  |         super(LocalizedFieldsAdminMixinTestCase, cls).setUpClass() | ||||||
|  |  | ||||||
|  |         cls.TestRelModel = get_fake_model( | ||||||
|  |             { | ||||||
|  |                 'description': LocalizedField() | ||||||
|  |             } | ||||||
|  |         ) | ||||||
|  |         cls.TestModel = get_fake_model( | ||||||
|  |             { | ||||||
|  |                 'title': LocalizedField(), | ||||||
|  |                 'rel': models.ForeignKey(cls.TestRelModel, | ||||||
|  |                                          on_delete=models.CASCADE) | ||||||
|  |             } | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |     def tearDown(self): | ||||||
|  |         if admin.site.is_registered(self.TestModel): | ||||||
|  |             admin.site.unregister(self.TestModel) | ||||||
|  |         if admin.site.is_registered(self.TestRelModel): | ||||||
|  |             admin.site.unregister(self.TestRelModel) | ||||||
|  |  | ||||||
|  |     @classmethod | ||||||
|  |     def test_model_admin(cls): | ||||||
|  |         """Tests whether :see:LocalizedFieldsAdminMixin | ||||||
|  |         mixin are works with admin.ModelAdmin""" | ||||||
|  |  | ||||||
|  |         @admin.register(cls.TestModel) | ||||||
|  |         class TestModelAdmin(LocalizedFieldsAdminMixin, admin.ModelAdmin): | ||||||
|  |             pass | ||||||
|  |  | ||||||
|  |         assert len(check_admin_app(apps.get_app_configs())) == 0 | ||||||
|  |  | ||||||
|  |     @classmethod | ||||||
|  |     def test_stackedmodel_admin(cls): | ||||||
|  |         """Tests whether :see:LocalizedFieldsAdminMixin mixin are works | ||||||
|  |         with admin.StackedInline""" | ||||||
|  |  | ||||||
|  |         class TestModelStackedInline(LocalizedFieldsAdminMixin, | ||||||
|  |                                      admin.StackedInline): | ||||||
|  |             model = cls.TestModel | ||||||
|  |  | ||||||
|  |         @admin.register(cls.TestRelModel) | ||||||
|  |         class TestRelModelAdmin(admin.ModelAdmin): | ||||||
|  |             inlines = [ | ||||||
|  |                 TestModelStackedInline, | ||||||
|  |             ] | ||||||
|  |  | ||||||
|  |         assert len(check_admin_app(apps.get_app_configs())) == 0 | ||||||
|  |  | ||||||
|  |     @classmethod | ||||||
|  |     def test_tabularmodel_admin(cls): | ||||||
|  |         """Tests whether :see:LocalizedFieldsAdminMixin mixin are works | ||||||
|  |         with admin.TabularInline""" | ||||||
|  |  | ||||||
|  |         class TestModelTabularInline(LocalizedFieldsAdminMixin, | ||||||
|  |                                      admin.TabularInline): | ||||||
|  |             model = cls.TestModel | ||||||
|  |  | ||||||
|  |         @admin.register(cls.TestRelModel) | ||||||
|  |         class TestRelModelAdmin(admin.ModelAdmin): | ||||||
|  |             inlines = [ | ||||||
|  |                 TestModelTabularInline, | ||||||
|  |             ] | ||||||
|  |  | ||||||
|  |         assert len(check_admin_app(apps.get_app_configs())) == 0 | ||||||
| @@ -1,6 +1,7 @@ | |||||||
| import json | import json | ||||||
|  |  | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
|  | from django.db import models | ||||||
| from django.db.utils import IntegrityError | from django.db.utils import IntegrityError | ||||||
| from django.test import TestCase | from django.test import TestCase | ||||||
|  |  | ||||||
| @@ -33,7 +34,6 @@ class LocalizedFieldTestCase(TestCase): | |||||||
|         field = LocalizedField(required=False) |         field = LocalizedField(required=False) | ||||||
|         assert field.required == [] |         assert field.required == [] | ||||||
|  |  | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def test_from_db_value(): |     def test_from_db_value(): | ||||||
|         """Tests whether the :see:from_db_value function |         """Tests whether the :see:from_db_value function | ||||||
| @@ -205,6 +205,16 @@ class LocalizedFieldTestCase(TestCase): | |||||||
|         for field in field.formfield().fields: |         for field in field.formfield().fields: | ||||||
|             assert field.required |             assert field.required | ||||||
|  |  | ||||||
|  |     def test_descriptor_user_defined_primary_key(self): | ||||||
|  |         """Tests that descriptor works even when primary key is user defined.""" | ||||||
|  |         model = get_fake_model(dict( | ||||||
|  |             slug=models.SlugField(primary_key=True), | ||||||
|  |             title=LocalizedField() | ||||||
|  |         )) | ||||||
|  |  | ||||||
|  |         obj = model.objects.create(slug='test', title='test') | ||||||
|  |         assert obj.title == 'test' | ||||||
|  |  | ||||||
|     def test_required_all(self): |     def test_required_all(self): | ||||||
|         """Tests whether passing required=True properly validates |         """Tests whether passing required=True properly validates | ||||||
|         that all languages are filled in.""" |         that all languages are filled in.""" | ||||||
|   | |||||||
| @@ -40,7 +40,6 @@ class LocalizedFieldFormTestCase(TestCase): | |||||||
|         for field in form.fields: |         for field in form.fields: | ||||||
|             assert not field.required |             assert not field.required | ||||||
|  |  | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def test_compress(): |     def test_compress(): | ||||||
|         """Tests whether the :see:compress function |         """Tests whether the :see:compress function | ||||||
|   | |||||||
| @@ -148,7 +148,7 @@ class LocalizedIntegerFieldTestCase(TestCase): | |||||||
|  |  | ||||||
|         with connection.cursor() as cursor: |         with connection.cursor() as cursor: | ||||||
|             table_name = self.TestModel._meta.db_table |             table_name = self.TestModel._meta.db_table | ||||||
|             cursor.execute("update %s set score = 'en=>haha'" % table_name); |             cursor.execute("update %s set score = 'en=>haha'" % table_name) | ||||||
|  |  | ||||||
|         obj.refresh_from_db() |         obj.refresh_from_db() | ||||||
|         assert obj.score.get(settings.LANGUAGE_CODE) is None |         assert obj.score.get(settings.LANGUAGE_CODE) is None | ||||||
|   | |||||||
							
								
								
									
										51
									
								
								tests/test_lookups.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								tests/test_lookups.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | |||||||
|  | from django.apps import apps | ||||||
|  | from django.conf import settings | ||||||
|  | from django.test import TestCase, override_settings | ||||||
|  | from django.utils import translation | ||||||
|  |  | ||||||
|  | from localized_fields.fields import LocalizedField | ||||||
|  | from localized_fields.value import LocalizedValue | ||||||
|  |  | ||||||
|  | from .fake_model import get_fake_model | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @override_settings(LOCALIZED_FIELDS_EXPERIMENTAL=True) | ||||||
|  | class LocalizedLookupsTestCase(TestCase): | ||||||
|  |     """Tests whether localized lookups properly work with.""" | ||||||
|  |     TestModel1 = None | ||||||
|  |  | ||||||
|  |     @classmethod | ||||||
|  |     def setUpClass(cls): | ||||||
|  |         """Creates the test model in the database.""" | ||||||
|  |  | ||||||
|  |         super(LocalizedLookupsTestCase, cls).setUpClass() | ||||||
|  |  | ||||||
|  |         # reload app as setting has changed | ||||||
|  |         config = apps.get_app_config('localized_fields') | ||||||
|  |         config.ready() | ||||||
|  |  | ||||||
|  |         cls.TestModel = get_fake_model( | ||||||
|  |             { | ||||||
|  |                 'text': LocalizedField(), | ||||||
|  |             } | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |     def test_localized_lookup(self): | ||||||
|  |         """Tests whether localized lookup properly works.""" | ||||||
|  |  | ||||||
|  |         self.TestModel.objects.create( | ||||||
|  |             text=LocalizedValue(dict(en='text_en', ro='text_ro', nl='text_nl')), | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         # assert that it properly lookups the currently active language | ||||||
|  |         for lang_code, _ in settings.LANGUAGES: | ||||||
|  |             translation.activate(lang_code) | ||||||
|  |             assert self.TestModel.objects.filter(text='text_' + lang_code).exists() | ||||||
|  |  | ||||||
|  |         # ensure that the default language is used in case no | ||||||
|  |         # language is active at all | ||||||
|  |         translation.deactivate_all() | ||||||
|  |         assert self.TestModel.objects.filter(text='text_en').exists() | ||||||
|  |  | ||||||
|  |         # ensure that hstore lookups still work | ||||||
|  |         assert self.TestModel.objects.filter(text__ro='text_ro').exists() | ||||||
| @@ -1,6 +1,4 @@ | |||||||
| from django.conf import settings |  | ||||||
| from django.test import TestCase | from django.test import TestCase | ||||||
| from django.utils import translation |  | ||||||
|  |  | ||||||
| from localized_fields.fields import LocalizedField | from localized_fields.fields import LocalizedField | ||||||
|  |  | ||||||
|   | |||||||
| @@ -152,7 +152,7 @@ class LocalizedValueTestCase(TestCase): | |||||||
|  |  | ||||||
|         # with no value, we always expect it to return None |         # with no value, we always expect it to return None | ||||||
|         localized_value = LocalizedValue() |         localized_value = LocalizedValue() | ||||||
|         assert localized_value.translate() == None |         assert localized_value.translate() is None | ||||||
|         assert str(localized_value) == '' |         assert str(localized_value) == '' | ||||||
|  |  | ||||||
|         # with no value for the default language, the default |         # with no value for the default language, the default | ||||||
| @@ -164,7 +164,7 @@ class LocalizedValueTestCase(TestCase): | |||||||
|         }) |         }) | ||||||
|  |  | ||||||
|         translation.activate(settings.LANGUAGE_CODE) |         translation.activate(settings.LANGUAGE_CODE) | ||||||
|         assert localized_value.translate() == None |         assert localized_value.translate() is None | ||||||
|         assert str(localized_value) == '' |         assert str(localized_value) == '' | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								tox.ini
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								tox.ini
									
									
									
									
									
								
							| @@ -1,10 +1,12 @@ | |||||||
| [tox] | [tox] | ||||||
| envlist = py35-dj{111,20} | envlist = py35-dj{111,20,21,22}, py36-dj{111,20,21,22}, py37-dj{111,20,21,22} | ||||||
|  |  | ||||||
| [testenv] | [testenv] | ||||||
| deps = | deps = | ||||||
|     dj111: Django>=1.11,<1.12 |     dj111: Django>=1.11,<1.12 | ||||||
|     dj20: Django>=2.0,<2.1 |     dj20: Django>=2.0,<2.1 | ||||||
|  |     dj21: Django>=2.1,<2.2 | ||||||
|  |     dj22: Django>=2.2,<2.3 | ||||||
|     -rrequirements/test.txt |     -rrequirements/test.txt | ||||||
| setenv = | setenv = | ||||||
|     DJANGO_SETTINGS_MODULE=settings |     DJANGO_SETTINGS_MODULE=settings | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user