Support for Django 4 (#101)

* Support for Django 4

* Change pytest-django version

* Change psycopg2 version

* Change pytest version

* Change tox version

* Change pytest-cov version

* Update circle ci jobs

* Fix ci jobs

* Update ci to postgres 12

* Allow Django 5.0

* Bump Django min version to 3.2

* Fix CI to account for >= 3.2 requirement

* Fix quote

* Remove python 3.12 for now due to distutils removal

* Remove Django upper bound

* Add back python3.6,3.7 wfs

* Downgrade dj-database-url

* Fix isnull issue from Django 4
This commit is contained in:
Tudor Văran 2024-06-04 12:49:13 +03:00 committed by GitHub
parent a55986d28c
commit 5bb16af6a4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 161 additions and 46 deletions

View File

@ -3,7 +3,7 @@ jobs:
test-python36: test-python36:
docker: docker:
- image: python:3.6-alpine - image: python:3.6-alpine
- image: postgres:11.0 - image: postgres:12.0
environment: environment:
POSTGRES_DB: 'localizedfields' POSTGRES_DB: 'localizedfields'
POSTGRES_USER: 'localizedfields' POSTGRES_USER: 'localizedfields'
@ -11,26 +11,25 @@ jobs:
steps: steps:
- checkout - checkout
- run: - run:
name: Install packages name: Install packages
command: apk add postgresql-libs gcc musl-dev postgresql-dev git command: apk add postgresql-libs gcc musl-dev postgresql-dev git
- run: - run:
name: Install Python packages name: Install Python packages
command: pip install --progress-bar off .[test] command: pip install --progress-bar off .[test]
- run: - run:
name: Run tests name: Run tests
command: tox -e 'py36-dj{20,21,22,30,31}' command: tox -e 'py36-dj{20,21,22,30,31,32}'
environment: environment:
DATABASE_URL: 'postgres://localizedfields:localizedfields@localhost:5432/localizedfields' DATABASE_URL: 'postgres://localizedfields:localizedfields@localhost:5432/localizedfields'
- store_test_results: - store_test_results:
path: reports path: reports
test-python37: test-python37:
docker: docker:
- image: python:3.7-alpine - image: python:3.7-alpine
- image: postgres:11.0 - image: postgres:12.0
environment: environment:
POSTGRES_DB: 'localizedfields' POSTGRES_DB: 'localizedfields'
POSTGRES_USER: 'localizedfields' POSTGRES_USER: 'localizedfields'
@ -38,26 +37,25 @@ jobs:
steps: steps:
- checkout - checkout
- run: - run:
name: Install packages name: Install packages
command: apk add postgresql-libs gcc musl-dev postgresql-dev git command: apk add postgresql-libs gcc musl-dev postgresql-dev git
- run: - run:
name: Install Python packages name: Install Python packages
command: pip install --progress-bar off .[test] command: pip install --progress-bar off .[test]
- run: - run:
name: Run tests name: Run tests
command: tox -e 'py37-dj{20,21,22,30,31}' command: tox -e 'py37-dj{20,21,22,30,31,32}'
environment: environment:
DATABASE_URL: 'postgres://localizedfields:localizedfields@localhost:5432/localizedfields' DATABASE_URL: 'postgres://localizedfields:localizedfields@localhost:5432/localizedfields'
- store_test_results: - store_test_results:
path: reports path: reports
test-python38: test-python38:
docker: docker:
- image: python:3.8-alpine - image: python:3.8-alpine
- image: postgres:11.0 - image: postgres:12.0
environment: environment:
POSTGRES_DB: 'localizedfields' POSTGRES_DB: 'localizedfields'
POSTGRES_USER: 'localizedfields' POSTGRES_USER: 'localizedfields'
@ -74,7 +72,7 @@ jobs:
- run: - run:
name: Run tests 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: environment:
DATABASE_URL: 'postgres://localizedfields:localizedfields@localhost:5432/localizedfields' DATABASE_URL: 'postgres://localizedfields:localizedfields@localhost:5432/localizedfields'
@ -84,7 +82,7 @@ jobs:
test-python39: test-python39:
docker: docker:
- image: python:3.9-alpine - image: python:3.9-alpine
- image: postgres:11.0 - image: postgres:12.0
environment: environment:
POSTGRES_DB: 'localizedfields' POSTGRES_DB: 'localizedfields'
POSTGRES_USER: 'localizedfields' POSTGRES_USER: 'localizedfields'
@ -101,16 +99,69 @@ jobs:
- run: - run:
name: Run tests name: Run tests
command: tox -e 'py39-dj{21,22,30,31}' command: tox -e 'py39-dj{30,31,32,40,41,42}'
environment: environment:
DATABASE_URL: 'postgres://localizedfields:localizedfields@localhost:5432/localizedfields' DATABASE_URL: 'postgres://localizedfields:localizedfields@localhost:5432/localizedfields'
- store_test_results: - store_test_results:
path: reports 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: analysis:
docker: docker:
- image: python:3.7-alpine - image: python:3.8-alpine
steps: steps:
- checkout - checkout
- run: - run:
@ -134,4 +185,6 @@ workflows:
- test-python37 - test-python37
- test-python38 - test-python38
- test-python39 - test-python39
- test-python310
- test-python311
- analysis - analysis

View File

@ -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) | | :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) | | :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) | | :package: | **PyPi** | [![PyPi](https://badge.fury.io/py/django-localized-fields.svg)](https://pypi.python.org/pypi/django-localized-fields) |
| <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="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, 4.0, 4.1, 4.2, 5.0 |
| <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, 3.10, 3.11 |
| :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)
| :checkered_flag: | **Installation** | [Installation Guide](https://django-localized-fields.readthedocs.io/en/latest/installation.html) | | :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. `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 ## Working with the code
### Prerequisites ### Prerequisites
* PostgreSQL 10 or newer. * PostgreSQL 12 or newer.
* Django 2.0 or newer. * Django 2.0 or newer.
* Python 3.6 or newer. * Python 3.6 or newer.

View File

@ -43,6 +43,10 @@ class LocalizedLookupMixin:
return super().process_lhs(qn, connection) return super().process_lhs(qn, connection)
def get_prep_lookup(self): 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) return str(self.rhs)

View File

@ -36,7 +36,7 @@ with open(
setup( setup(
name="django-localized-fields", name="django-localized-fields",
version="6.8b3", version="6.8b4",
packages=find_packages(exclude=["tests"]), packages=find_packages(exclude=["tests"]),
include_package_data=True, include_package_data=True,
license="MIT License", license="MIT License",
@ -67,6 +67,8 @@ setup(
"Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP",
"Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Internet :: WWW/HTTP :: Dynamic Content",
], ],
@ -80,14 +82,14 @@ setup(
':python_version <= "3.6"': ["dataclasses"], ':python_version <= "3.6"': ["dataclasses"],
"docs": ["Sphinx==2.2.0", "sphinx-rtd-theme==0.4.3"], "docs": ["Sphinx==2.2.0", "sphinx-rtd-theme==0.4.3"],
"test": [ "test": [
"tox==3.14.3", "tox==3.28.0",
"pytest==5.3.2", "pytest==7.0.1",
"pytest-django==3.7.0", "pytest-django==4.5.2",
"pytest-cov==2.8.1", "pytest-cov==2.12.1",
"dj-database-url==0.5.0", "dj-database-url==0.5.0",
"django-autoslug==1.9.6", "django-autoslug==1.9.9",
"django-bleach==0.6.1", "django-bleach==0.9.0",
"psycopg2==2.8.4", "psycopg2==2.9.8",
], ],
"analysis": [ "analysis": [
"black==22.3.0", "black==22.3.0",

51
tests/test_isnull.py Normal file
View File

@ -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()

View File

@ -1,5 +1,5 @@
[tox] [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] [testenv]
deps = deps =
@ -8,6 +8,11 @@ deps =
dj22: Django>=2.2,<2.3 dj22: Django>=2.2,<2.3
dj30: Django>=3.0,<3.0.2 dj30: Django>=3.0,<3.0.2
dj31: Django>=3.1,<3.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] .[test]
setenv = setenv =
DJANGO_SETTINGS_MODULE=settings DJANGO_SETTINGS_MODULE=settings