New Upstream Release - django-model-utils

Ready changes

Summary

Merged new upstream version: 4.3.1 (was: 4.2.0).

Resulting package

Built on 2022-12-14T19:35 (took 15m39s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-releases python-django-model-utils-docapt install -t fresh-releases python3-django-model-utils

Lintian Result

Diff

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 7d08edc..6a01614 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
new file mode 100644
index 0000000..621e9fa
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,20 @@
+repos:
+  - repo: https://github.com/PyCQA/isort
+    rev: 5.10.1
+    hooks:
+      - id: isort
+        args: ['--profile', 'black', '--check-only', '--diff']
+        files: ^((model_utils|tests)/)|setup.py
+
+  - repo: https://github.com/PyCQA/flake8
+    rev: 5.0.4
+    hooks:
+      - id: flake8
+        args: ['--ignore=E402,E501,E731,W503']
+        files: ^(model_utils|tests)/
+
+  - repo: https://github.com/asottile/pyupgrade
+    rev: v3.1.0
+    hooks:
+    -   id: pyupgrade
+        args: [--py37-plus]
diff --git a/AUTHORS.rst b/AUTHORS.rst
index f50fc91..21c822d 100644
--- a/AUTHORS.rst
+++ b/AUTHORS.rst
@@ -25,10 +25,12 @@
 | Donald Stufft <donald.stufft@gmail.com>
 | Douglas Meehan <dmeehan@gmail.com>
 | Emin Bugra Saral <eminbugrasaral@me.com>
+| Enrique Matías Sánchez <cronopios@gmail.com>
 | Eran Rundstein <eranrund@gmail.com>
 | Eugene Kuznetsov <atorich@gmail.com>
 | Felipe Prenholato <felipe.rafael@pdg.com.br>
 | Filipe Ximenes <filipeximenes@gmail.com>
+| Florian Alu <fla@grapps.fr>
 | Germano Massullo <github.com/Germano0>
 | Gregor Müllegger <gregor@muellegger.de>
 | Guilherme Devincenzi <github.com/gdevincenzi>
diff --git a/CHANGES.rst b/CHANGES.rst
index 9151dee..09d39ca 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,9 +1,21 @@
 Changelog
 =========
 
-Unreleased
-----------
+4.3.1 (2022-11-15)
+------------------
+
+- Confirm support for `Django 4.0`
+- 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.3.0
+-----
 
+- Never released due to packaging issues.
 
 4.2.0 (2021-10-11)
 ------------------
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..e0d5efa
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,46 @@
+# Code of Conduct
+
+As contributors and maintainers of the Jazzband projects, and in the interest of
+fostering an open and welcoming community, we pledge to respect all people who
+contribute through reporting issues, posting feature requests, updating documentation,
+submitting pull requests or patches, and other activities.
+
+We are committed to making participation in the Jazzband a harassment-free experience
+for everyone, regardless of the level of experience, gender, gender identity and
+expression, sexual orientation, disability, personal appearance, body size, race,
+ethnicity, age, religion, or nationality.
+
+Examples of unacceptable behavior by participants include:
+
+- The use of sexualized language or imagery
+- Personal attacks
+- Trolling or insulting/derogatory comments
+- Public or private harassment
+- Publishing other's private information, such as physical or electronic addresses,
+  without explicit permission
+- Other unethical or unprofessional conduct
+
+The Jazzband roadies have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are not
+aligned to this Code of Conduct, or to ban temporarily or permanently any contributor
+for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
+
+By adopting this Code of Conduct, the roadies commit themselves to fairly and
+consistently applying these principles to every aspect of managing the jazzband
+projects. Roadies who do not follow or enforce the Code of Conduct may be permanently
+removed from the Jazzband roadies.
+
+This code of conduct applies both within project spaces and in public spaces when an
+individual is representing the project or its community.
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
+contacting the roadies at `roadies@jazzband.co`. All complaints will be reviewed and
+investigated and will result in a response that is deemed necessary and appropriate to
+the circumstances. Roadies are obligated to maintain confidentiality with regard to the
+reporter of an incident.
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version
+1.3.0, available at [https://contributor-covenant.org/version/1/3/0/][version]
+
+[homepage]: https://contributor-covenant.org
+[version]: https://contributor-covenant.org/version/1/3/0/
diff --git a/README.rst b/README.rst
index c07f109..9a9e031 100644
--- a/README.rst
+++ b/README.rst
@@ -20,9 +20,9 @@ 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/
+.. _Django: https://www.djangoproject.com/
 
 This app is available on `PyPI`_.
 
diff --git a/debian/changelog b/debian/changelog
index b7d9f52..332beb6 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+django-model-utils (4.3.1-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Wed, 14 Dec 2022 19:24:47 -0000
+
 django-model-utils (4.2.0-2) unstable; urgency=medium
 
   [ Debian Janitor ]
diff --git a/debian/patches/Don-t-rely-on-setuptools_scm.patch b/debian/patches/Don-t-rely-on-setuptools_scm.patch
index 9497a1c..71e3178 100644
--- a/debian/patches/Don-t-rely-on-setuptools_scm.patch
+++ b/debian/patches/Don-t-rely-on-setuptools_scm.patch
@@ -10,11 +10,11 @@ Forward: not-needed
  setup.py | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)
 
-diff --git a/setup.py b/setup.py
-index 48aef8f..f9211bc 100644
---- a/setup.py
-+++ b/setup.py
-@@ -19,7 +19,7 @@ long_description = "\n\n".join(long_desc(HERE))
+Index: django-model-utils.git/setup.py
+===================================================================
+--- django-model-utils.git.orig/setup.py
++++ django-model-utils.git/setup.py
+@@ -19,7 +19,7 @@ long_description = "\n\n".join(long_desc
  setup(
      name='django-model-utils',
      use_scm_version={"version_scheme": "post-release"},
diff --git a/docs/managers.rst b/docs/managers.rst
index b90f3d4..0d07a96 100644
--- a/docs/managers.rst
+++ b/docs/managers.rst
@@ -6,7 +6,7 @@ InheritanceManager
 
 This manager (`contributed by Jeff Elmore`_) should be attached to a base model
 class in a model-inheritance tree.  It allows queries on that base model to
-return heterogenous results of the actual proper subtypes, without any
+return heterogeneous results of the actual proper subtypes, without any
 additional queries.
 
 For instance, if you have a ``Place`` model with subclasses ``Restaurant`` and
diff --git a/docs/setup.rst b/docs/setup.rst
index 06d274c..69c9840 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/locale/es/LC_MESSAGES/django.mo b/model_utils/locale/es/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..6cf203a
Binary files /dev/null and b/model_utils/locale/es/LC_MESSAGES/django.mo differ
diff --git a/model_utils/locale/es/LC_MESSAGES/django.po b/model_utils/locale/es/LC_MESSAGES/django.po
new file mode 100644
index 0000000..99e0024
--- /dev/null
+++ b/model_utils/locale/es/LC_MESSAGES/django.po
@@ -0,0 +1,43 @@
+# This file is distributed under the same license as the django-model-utils package.
+#
+# Translators:
+# Enrique Matías Sánchez <cronopios@gmail.com>, 2020.
+msgid ""
+msgstr ""
+"Project-Id-Version: django-model-utils\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-03-29 10:59+0200\n"
+"PO-Revision-Date: 2020-03-29 11:14+0100\n"
+"Last-Translator: Enrique Matías Sánchez <cronopios@gmail.com>\n"
+"Language-Team: \n"
+"Language: es\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Lokalize 2.0\n"
+
+#: models.py:28
+msgid "created"
+msgstr "creado"
+
+#: models.py:29
+msgid "modified"
+msgstr "modificado"
+
+#: models.py:51
+msgid "start"
+msgstr "inicio"
+
+#: models.py:52
+msgid "end"
+msgstr "fin"
+
+#: models.py:67
+msgid "status"
+msgstr "estado"
+
+#: models.py:68
+msgid "status changed"
+msgstr "estado modificado"
+
diff --git a/model_utils/locale/fr/LC_MESSAGES/django.mo b/model_utils/locale/fr/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..e91c79b
Binary files /dev/null and b/model_utils/locale/fr/LC_MESSAGES/django.mo differ
diff --git a/model_utils/locale/fr/LC_MESSAGES/django.po b/model_utils/locale/fr/LC_MESSAGES/django.po
new file mode 100644
index 0000000..e542066
--- /dev/null
+++ b/model_utils/locale/fr/LC_MESSAGES/django.po
@@ -0,0 +1,46 @@
+# French translations of django-model-utils
+#
+# This file is distributed under the same license as the django-model-utils package.
+#
+# Translators:
+# ------------
+# Florian Alu <fla@grapps.fr>, 2021.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: django-model-utils\n"
+"Report-Msgid-Bugs-To: https://github.com/jazzband/django-model-utils/issues\n"
+"POT-Creation-Date: 2021-01-11 11:39+0100\n"
+"PO-Revision-Date: 2021-01-11 11:45+0100\n"
+"Last-Translator: Florian Alu <fla@grapps.fr>\n"
+"Language-Team: \n"
+"Language: fr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"X-Generator: Poedit 2.4.2\n"
+
+#: .\models.py:25
+msgid "created"
+msgstr "créé"
+
+#: .\models.py:26
+msgid "modified"
+msgstr "modifié"
+
+#: .\models.py:50
+msgid "start"
+msgstr "début"
+
+#: .\models.py:51
+msgid "end"
+msgstr "fin"
+
+#: .\models.py:66
+msgid "status"
+msgstr "statut"
+
+#: .\models.py:67
+msgid "status changed"
+msgstr "statut modifié"
diff --git a/model_utils/managers.py b/model_utils/managers.py
index ba8ea63..75d3d98 100644
--- a/model_utils/managers.py
+++ b/model_utils/managers.py
@@ -72,16 +72,10 @@ class InheritanceQuerySetMixin:
                     )
             subclasses = verified_subclasses
 
-        # workaround https://code.djangoproject.com/ticket/16855
-        previous_select_related = self.query.select_related
         if subclasses:
             new_qs = self.select_related(*subclasses)
         else:
             new_qs = self
-        previous_is_dict = isinstance(previous_select_related, dict)
-        new_is_dict = isinstance(new_qs.query.select_related, dict)
-        if previous_is_dict and new_is_dict:
-            new_qs.query.select_related.update(previous_select_related)
         new_qs.subclasses = subclasses
         return new_qs
 
@@ -145,7 +139,7 @@ class InheritanceQuerySetMixin:
         """
         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
@@ -185,7 +179,7 @@ class InheritanceQuerySet(InheritanceQuerySetMixin, QuerySet):
         """
         Fetch only objects that are instances of the provided model(s).
         """
-        # If we aren't already selecting the subclasess, we need
+        # If we aren't already selecting the subclasses, we need
         # to in order to get this to work.
 
         # How can we tell if we are not selecting subclasses?
@@ -314,7 +308,7 @@ class JoinQueryset(models.QuerySet):
 
         # Put additional quotes around string.
         params = [
-            '\'{}\''.format(p)
+            f'\'{p}\''
             if isinstance(p, str) else p
             for p in params
         ]
@@ -331,7 +325,7 @@ class JoinQueryset(models.QuerySet):
         to itself.
 
         `Join` either uses the current queryset and effectively does a self-join to
-        create a new limited queryset OR it uses a querset given by the user.
+        create a new limited queryset OR it uses a queryset given by the user.
 
         The model of a given queryset needs to contain a valid foreign key to
         the current queryset to perform a join. A new queryset is then created.
@@ -344,7 +338,7 @@ class JoinQueryset(models.QuerySet):
                 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 15cc212..2c6f855 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 @@ setup(
     maintainer='JazzBand',
     url='https://github.com/jazzband/django-model-utils',
     packages=find_packages(exclude=['tests*']),
-    install_requires=['Django>=2.0.1'],
+    python_requires=">=3.7",
+    install_requires=['Django>=3.2'],
     classifiers=[
         'Development Status :: 5 - Production/Stable',
         'Environment :: Web Environment',
@@ -38,18 +39,16 @@ setup(
         '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,
-    tests_require=['Django>=2.2'],
     package_data={
         'model_utils': [
             'locale/*/LC_MESSAGES/django.po', 'locale/*/LC_MESSAGES/django.mo'
diff --git a/tests/models.py b/tests/models.py
index c789267..d785e88 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 @@ class StringyDescriptor:
             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/settings.py b/tests/settings.py
index ec42daf..4ead542 100644
--- a/tests/settings.py
+++ b/tests/settings.py
@@ -23,3 +23,5 @@ CACHES = {
 }
 
 DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
+
+USE_TZ = False
diff --git a/tests/test_managers/test_join_manager.py b/tests/test_managers/test_join_manager.py
index 3b8a4c2..57c1c34 100644
--- a/tests/test_managers/test_join_manager.py
+++ b/tests/test_managers/test_join_manager.py
@@ -6,7 +6,7 @@ from tests.models import BoxJoinModel, JoinItemForeignKey
 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/tests/test_models/test_timestamped_model.py b/tests/test_models/test_timestamped_model.py
index 0d13914..9e61f63 100644
--- a/tests/test_models/test_timestamped_model.py
+++ b/tests/test_models/test_timestamped_model.py
@@ -14,7 +14,7 @@ class TimeStampedModelTests(TestCase):
 
     def test_created_sets_modified(self):
         '''
-        Ensure that on creation that modifed is set exactly equal to created.
+        Ensure that on creation that modified is set exactly equal to created.
         '''
         t1 = TimeStamp.objects.create()
         self.assertEqual(t1.created, t1.modified)
@@ -31,7 +31,7 @@ class TimeStampedModelTests(TestCase):
     def test_overriding_created_via_object_creation_also_uses_creation_date_for_modified(self):
         """
         Setting the created date when first creating an object
-        should be permissable.
+        should be permissible.
         """
         different_date = datetime.today() - timedelta(weeks=52)
         t1 = TimeStamp.objects.create(created=different_date)
diff --git a/tox.ini b/tox.ini
index 6f7c855..5a3ab0f 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,14 +1,12 @@
 [tox]
 envlist =
-    py{36,37,38,39}-dj{22,31,32}
-    py310-dj{31,32}
-    py{38,39,310}-djmain
+    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,9 +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

Debdiff

[The following lists of changes regard files as different if they have different names, permissions or owners.]

Files in second set of .debs but not in first

-rw-r--r--  root/root   /usr/lib/python3/dist-packages/model_utils/locale/es/LC_MESSAGES/django.mo
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/model_utils/locale/es/LC_MESSAGES/django.po
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/model_utils/locale/fr/LC_MESSAGES/django.mo
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/model_utils/locale/fr/LC_MESSAGES/django.po

Files in first set of .debs but not in second

lrwxrwxrwx  root/root   /usr/share/doc/python-django-model-utils-doc/html/_static/sphinx_highlight.js -> ../../../../javascript/sphinxdoc/1.0/sphinx_highlight.js

Control files of package python-django-model-utils-doc: lines which differ (wdiff format)

  • Depends: libjs-sphinxdoc (>= 5.2) 5.0)

No differences were encountered between the control files of package python3-django-model-utils

More details

Full run details