diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7d08edcf..6a016146 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,11 +9,11 @@ jobs: fail-fast: false max-parallel: 5 matrix: - python-version: ['3.6', '3.7', '3.8', '3.9', '3.10'] + python-version: ['3.7', '3.8', '3.9', '3.10'] services: postgres: - image: postgres:10 + image: postgres:12 env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 36db6a6d..0295ad11 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,3 +12,9 @@ repos: - id: flake8 args: ['--ignore=E402,E501,E731,W503'] files: ^(model_utils|tests)/ + + - repo: https://github.com/asottile/pyupgrade + rev: v2.37.3 + hooks: + - id: pyupgrade + args: [--py37-plus] diff --git a/CHANGES.rst b/CHANGES.rst index ae2be54e..8f1cbaf8 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -8,6 +8,9 @@ Unreleased - Add Spanish translation - Add French translation - Drop Django 1.7 workaround from `select_subclasses()` +- Drop support for `Django < 3.2` +- Drop support for `Python 3.6` +- Confirm support for `Django 4.1` 4.2.0 (2021-10-11) ------------------ diff --git a/README.rst b/README.rst index c07f1098..7516a000 100644 --- a/README.rst +++ b/README.rst @@ -20,7 +20,7 @@ django-model-utils Django model mixins and utilities. -``django-model-utils`` supports `Django`_ 2.2+. +``django-model-utils`` supports `Django`_ 3.2+. .. _Django: http://www.djangoproject.com/ diff --git a/docs/setup.rst b/docs/setup.rst index 06d274c8..69c98407 100644 --- a/docs/setup.rst +++ b/docs/setup.rst @@ -17,7 +17,7 @@ modify your ``INSTALLED_APPS`` setting. Dependencies ============ -``django-model-utils`` supports `Django`_ 2.2, 3.1 and 3.2 (latest bugfix -release in each series only) on Python 3.6+. +``django-model-utils`` supports `Django`_ 3.2+ (latest bugfix +release in each series only) on Python 3.7+. .. _Django: http://www.djangoproject.com/ diff --git a/model_utils/managers.py b/model_utils/managers.py index 19b8d71d..75d3d98a 100644 --- a/model_utils/managers.py +++ b/model_utils/managers.py @@ -139,7 +139,7 @@ def _get_ancestors_path(self, model, levels=None): """ if not issubclass(model, self.model): raise ValueError( - "{!r} is not a subclass of {!r}".format(model, self.model)) + f"{model!r} is not a subclass of {self.model!r}") ancestry = [] # should be a OneToOneField or None @@ -308,7 +308,7 @@ def get_quoted_query(self, query): # Put additional quotes around string. params = [ - '\'{}\''.format(p) + f'\'{p}\'' if isinstance(p, str) else p for p in params ] @@ -338,7 +338,7 @@ def join(self, qs=None): if getattr(fk, 'related_model', None) == self.model ] fk = fk[0] if fk else None - model_set = '{}_set'.format(self.model.__name__.lower()) + model_set = f'{self.model.__name__.lower()}_set' key = fk or getattr(qs.model, model_set, None) if not key: diff --git a/setup.py b/setup.py index 3798bdaf..2c6f855f 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ def long_desc(root_path): for filename in FILES: filepath = os.path.realpath(os.path.join(root_path, filename)) if os.path.isfile(filepath): - with open(filepath, mode='r') as f: + with open(filepath) as f: yield f.read() @@ -29,7 +29,8 @@ def long_desc(root_path): maintainer='JazzBand', url='https://github.com/jazzband/django-model-utils', packages=find_packages(exclude=['tests*']), - install_requires=['Django>=2.2'], + python_requires=">=3.7", + install_requires=['Django>=3.2'], classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', @@ -38,16 +39,14 @@ def long_desc(root_path): 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Framework :: Django', - 'Framework :: Django :: 2.2', - 'Framework :: Django :: 3.1', 'Framework :: Django :: 3.2', 'Framework :: Django :: 4.0', + 'Framework :: Django :: 4.1', ], zip_safe=False, package_data={ diff --git a/tests/models.py b/tests/models.py index c789267e..d785e880 100644 --- a/tests/models.py +++ b/tests/models.py @@ -1,4 +1,3 @@ -import django from django.db import models from django.db.models import Manager from django.db.models.query_utils import DeferredAttribute @@ -387,13 +386,9 @@ def __get__(self, obj, cls=None): return self if self.name in obj.get_deferred_fields(): # This queries the database, and sets the value on the instance. - if django.VERSION < (3, 0): - DeferredAttribute(field_name=self.name).__get__(obj, cls) - else: - # Since Django 3.0, DeferredAttribute wants a field argument. - fields_map = {f.name: f for f in cls._meta.fields} - field = fields_map[self.name] - DeferredAttribute(field=field).__get__(obj, cls) + fields_map = {f.name: f for f in cls._meta.fields} + field = fields_map[self.name] + DeferredAttribute(field=field).__get__(obj, cls) return str(obj.__dict__[self.name]) def __set__(self, obj, value): diff --git a/tests/test_managers/test_join_manager.py b/tests/test_managers/test_join_manager.py index 3b8a4c27..57c1c344 100644 --- a/tests/test_managers/test_join_manager.py +++ b/tests/test_managers/test_join_manager.py @@ -6,7 +6,7 @@ class JoinManagerTest(TestCase): def setUp(self): for i in range(20): - BoxJoinModel.objects.create(name='name_{i}'.format(i=i)) + BoxJoinModel.objects.create(name=f'name_{i}') JoinItemForeignKey.objects.create( weight=10, belonging=BoxJoinModel.objects.get(name='name_1') diff --git a/tox.ini b/tox.ini index 40a05566..5a3ab0fb 100644 --- a/tox.ini +++ b/tox.ini @@ -1,14 +1,12 @@ [tox] envlist = - py{36,37,38,39}-dj{22,31} - py{36,37,38,39,310}-dj32 - py{38,39,310}-dj{40,main} + py{37,38,39,310}-dj32 + py{38,39,310}-dj{40,41,main} flake8 isort [gh-actions] python = - 3.6: py36 3.7: py37 3.8: py38, flake8, isort 3.9: py39 @@ -18,10 +16,9 @@ python = deps = freezegun==0.3.12 -rrequirements-test.txt - dj22: Django==2.2.* - dj31: Django==3.1.* dj32: Django==3.2.* dj40: Django==4.0.* + dj41: Django==4.1.* djmain: https://github.com/django/django/archive/main.tar.gz ignore_outcome = djmain: True