diff --git a/.circleci/config.yml b/.circleci/config.yml index 8e7a6f5..75a342a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,7 @@ jobs: test-python36: docker: - image: python:3.6-alpine - - image: postgres:11.0 + - image: postgres:12.0 environment: POSTGRES_DB: 'localizedfields' POSTGRES_USER: 'localizedfields' @@ -11,26 +11,25 @@ jobs: steps: - checkout - run: - name: Install packages - command: apk add postgresql-libs gcc musl-dev postgresql-dev git + name: Install packages + command: apk add postgresql-libs gcc musl-dev postgresql-dev git - run: - name: Install Python packages - command: pip install --progress-bar off .[test] + name: Install Python packages + command: pip install --progress-bar off .[test] - run: - name: Run tests - command: tox -e 'py36-dj{20,21,22,30,31}' - environment: - DATABASE_URL: 'postgres://localizedfields:localizedfields@localhost:5432/localizedfields' + name: Run tests + command: tox -e 'py36-dj{20,21,22,30,31,32}' + environment: + DATABASE_URL: 'postgres://localizedfields:localizedfields@localhost:5432/localizedfields' - store_test_results: - path: reports - + path: reports test-python37: docker: - image: python:3.7-alpine - - image: postgres:11.0 + - image: postgres:12.0 environment: POSTGRES_DB: 'localizedfields' POSTGRES_USER: 'localizedfields' @@ -38,26 +37,25 @@ jobs: steps: - checkout - run: - name: Install packages - command: apk add postgresql-libs gcc musl-dev postgresql-dev git + name: Install packages + command: apk add postgresql-libs gcc musl-dev postgresql-dev git - run: - name: Install Python packages - command: pip install --progress-bar off .[test] + name: Install Python packages + command: pip install --progress-bar off .[test] - run: - name: Run tests - command: tox -e 'py37-dj{20,21,22,30,31}' - environment: - DATABASE_URL: 'postgres://localizedfields:localizedfields@localhost:5432/localizedfields' + name: Run tests + command: tox -e 'py37-dj{20,21,22,30,31,32}' + environment: + DATABASE_URL: 'postgres://localizedfields:localizedfields@localhost:5432/localizedfields' - store_test_results: - path: reports - + path: reports test-python38: docker: - image: python:3.8-alpine - - image: postgres:11.0 + - image: postgres:12.0 environment: POSTGRES_DB: 'localizedfields' POSTGRES_USER: 'localizedfields' @@ -74,7 +72,7 @@ jobs: - run: name: Run tests - command: tox -e 'py38-dj{20,21,22,30,31}' + command: tox -e 'py38-dj{20,21,22,30,31,32,40,41,42}' environment: DATABASE_URL: 'postgres://localizedfields:localizedfields@localhost:5432/localizedfields' @@ -84,7 +82,7 @@ jobs: test-python39: docker: - image: python:3.9-alpine - - image: postgres:11.0 + - image: postgres:12.0 environment: POSTGRES_DB: 'localizedfields' POSTGRES_USER: 'localizedfields' @@ -101,16 +99,69 @@ jobs: - run: name: Run tests - command: tox -e 'py39-dj{21,22,30,31}' + command: tox -e 'py39-dj{30,31,32,40,41,42}' environment: DATABASE_URL: 'postgres://localizedfields:localizedfields@localhost:5432/localizedfields' - store_test_results: path: reports + test-python310: + docker: + - image: python:3.10-alpine + - image: postgres:12.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 --progress-bar off .[test] + + - run: + name: Run tests + command: tox -e 'py310-dj{32,40,41,42,50}' + environment: + DATABASE_URL: 'postgres://localizedfields:localizedfields@localhost:5432/localizedfields' + + - store_test_results: + path: reports + test-python311: + docker: + - image: python:3.11-alpine + - image: postgres:12.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 --progress-bar off .[test] + + - run: + name: Run tests + command: tox -e 'py311-dj{42,50}' + environment: + DATABASE_URL: 'postgres://localizedfields:localizedfields@localhost:5432/localizedfields' + + - store_test_results: + path: reports + analysis: docker: - - image: python:3.7-alpine + - image: python:3.8-alpine steps: - checkout - run: @@ -134,4 +185,6 @@ workflows: - test-python37 - test-python38 - test-python39 + - test-python310 + - test-python311 - analysis diff --git a/README.md b/README.md index adc20c3..49a9d19 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ -| | | | -|--------------------|---------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| | | | +|--------------------|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | :white_check_mark: | **Tests** | [![CircleCI](https://circleci.com/gh/SectorLabs/django-localized-fields/tree/master.svg?style=svg)](https://circleci.com/gh/SectorLabs/django-localized-fields/tree/master) | -| :memo: | **License** | [![License](https://img.shields.io/:license-mit-blue.svg)](http://doge.mit-license.org) | -| :package: | **PyPi** | [![PyPi](https://badge.fury.io/py/django-localized-fields.svg)](https://pypi.python.org/pypi/django-localized-fields) | -| | **Django Versions** | 2.0, 2.1, 2.2, 3.0, 3.1, 3.2 | -| | **Python Versions** | 3.6, 3.7, 3.8, 3.9 | -| :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) -| :checkered_flag: | **Installation** | [Installation Guide](https://django-localized-fields.readthedocs.io/en/latest/installation.html) | +| :memo: | **License** | [![License](https://img.shields.io/:license-mit-blue.svg)](http://doge.mit-license.org) | +| :package: | **PyPi** | [![PyPi](https://badge.fury.io/py/django-localized-fields.svg)](https://pypi.python.org/pypi/django-localized-fields) | +| | **Django Versions** | 2.0, 2.1, 2.2, 3.0, 3.1, 3.2, 4.0, 4.1, 4.2, 5.0 | +| | **Python Versions** | 3.6, 3.7, 3.8, 3.9, 3.10, 3.11 | +| :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) +| :checkered_flag: | **Installation** | [Installation Guide](https://django-localized-fields.readthedocs.io/en/latest/installation.html) | `django-localized-fields` is an implementation of a field class for Django models that allows the field's value to be set in multiple languages. It does this by utilizing the ``hstore`` type (PostgreSQL specific), which is available as `models.HStoreField` since Django 1.10. @@ -20,7 +20,7 @@ ## Working with the code ### Prerequisites -* PostgreSQL 10 or newer. +* PostgreSQL 12 or newer. * Django 2.0 or newer. * Python 3.6 or newer. diff --git a/localized_fields/lookups.py b/localized_fields/lookups.py index 1536450..ba38980 100644 --- a/localized_fields/lookups.py +++ b/localized_fields/lookups.py @@ -43,6 +43,10 @@ def process_lhs(self, qn, connection): return super().process_lhs(qn, connection) def get_prep_lookup(self): + # Django 4.0 removed the ability for isnull fields to be something other than a bool + # We should NOT convert them to strings + if isinstance(self.rhs, bool): + return self.rhs return str(self.rhs) diff --git a/setup.py b/setup.py index 52d8292..b076389 100644 --- a/setup.py +++ b/setup.py @@ -36,7 +36,7 @@ def run(self): setup( name="django-localized-fields", - version="6.8b3", + version="6.8b4", packages=find_packages(exclude=["tests"]), include_package_data=True, license="MIT License", @@ -67,6 +67,8 @@ def run(self): "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", ], @@ -80,14 +82,14 @@ def run(self): ':python_version <= "3.6"': ["dataclasses"], "docs": ["Sphinx==2.2.0", "sphinx-rtd-theme==0.4.3"], "test": [ - "tox==3.14.3", - "pytest==5.3.2", - "pytest-django==3.7.0", - "pytest-cov==2.8.1", + "tox==3.28.0", + "pytest==7.0.1", + "pytest-django==4.5.2", + "pytest-cov==2.12.1", "dj-database-url==0.5.0", - "django-autoslug==1.9.6", - "django-bleach==0.6.1", - "psycopg2==2.8.4", + "django-autoslug==1.9.9", + "django-bleach==0.9.0", + "psycopg2==2.9.8", ], "analysis": [ "black==22.3.0", diff --git a/tests/test_isnull.py b/tests/test_isnull.py new file mode 100644 index 0000000..fb54e52 --- /dev/null +++ b/tests/test_isnull.py @@ -0,0 +1,51 @@ +import django +import pytest + +from django.test import TestCase + +from localized_fields.fields import LocalizedField +from localized_fields.value import LocalizedValue + +from .fake_model import get_fake_model + + +class LocalizedIsNullLookupsTestCase(TestCase): + """Tests whether ref lookups properly work with.""" + + TestModel1 = None + + @classmethod + def setUpClass(cls): + """Creates the test model in the database.""" + super(LocalizedIsNullLookupsTestCase, cls).setUpClass() + cls.TestModel = get_fake_model( + {"text": LocalizedField(null=True, required=[])} + ) + cls.TestModel.objects.create( + text=LocalizedValue(dict(en="text_en", ro="text_ro", nl="text_nl")) + ) + cls.TestModel.objects.create( + text=None, + ) + + def test_isnull_lookup_valid_values(self): + """Test whether isnull properly works with valid values.""" + assert self.TestModel.objects.filter(text__isnull=True).exists() + assert self.TestModel.objects.filter(text__isnull=False).exists() + + def test_isnull_lookup_null(self): + """Test whether isnull crashes with None as value.""" + + with pytest.raises(ValueError): + assert self.TestModel.objects.filter(text__isnull=None).exists() + + def test_isnull_lookup_string(self): + """Test whether isnull properly works with string values on the + corresponding Django version.""" + if django.VERSION < (4, 0): + assert self.TestModel.objects.filter(text__isnull="True").exists() + else: + with pytest.raises(ValueError): + assert self.TestModel.objects.filter( + text__isnull="True" + ).exists() diff --git a/tox.ini b/tox.ini index 15dfab6..d931abe 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py36-dj{20,21,22,30,31}, py37-dj{20,21,22,30,31}, py38-dj{20,21,22,30,31}, py39-dj{21,22,30,31} +envlist = py36-dj{20,21,22,30,31,32}, py37-dj{20,21,22,30,31,32}, py38-dj{20,21,22,30,31,32,40,41,42}, py39-dj{30,31,32,40,41,42}, py310-dj{32,40,41,42,50}, py311-dj{42,50} [testenv] deps = @@ -8,6 +8,11 @@ deps = dj22: Django>=2.2,<2.3 dj30: Django>=3.0,<3.0.2 dj31: Django>=3.1,<3.2 + dj32: Django>=3.2,<4.0 + dj40: Django>=4.0,<4.1 + dj41: Django>=4.1,<4.2 + dj42: Django>=4.2,<5.0 + dj50: Django>=5.0,<5.1 .[test] setenv = DJANGO_SETTINGS_MODULE=settings