From 10f65638528208af5e9b8207faad3eb406f61d0c Mon Sep 17 00:00:00 2001 From: Bastien Vallet Date: Tue, 4 Aug 2020 14:33:08 +0200 Subject: [PATCH 1/5] [compat] Cleanup compat matrix with Django/Python supported versions --- .travis.yml | 29 +++++++++++++++-------------- README.rst | 2 +- setup.cfg | 6 +++--- tox.ini | 11 +++++------ 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/.travis.yml b/.travis.yml index c320375..9edc4d6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,32 +11,33 @@ addons: matrix: fast_finish: true include: - # Django 1.11: Python 2.7, 3.5, or 3.6 - - { env: TOXENV=py27-django111, python: 2.7 } - - { env: TOXENV=py35-django111, python: 3.5 } - - { env: TOXENV=py36-django111, python: 3.6 } - - { env: TOXENV=py36-django111-postgres DB=postgres, python: 3.6 } - # Django 2.0: Python 3.5, or 3.6 - - { env: TOXENV=py35-django20, python: 3.5 } - - { env: TOXENV=py36-django20, python: 3.6 } - - { env: TOXENV=py36-django20-postgres DB=postgres, python: 3.6 } - # Django 2.1: Python 3.6, or 3.7 + # Django 2.1: Python 3.5, 3.6, or 3.7 + - { env: TOXENV=py35-django21, python: 3.5 } - { env: TOXENV=py36-django21, python: 3.6 } - { env: TOXENV=py37-django21, python: 3.7 } - { env: TOXENV=py37-django21-postgres DB=postgres, python: 3.7 } - # Django 2.2: Python 3.6, or 3.7 + # Django 2.2: Python 3.5, 3.6, 3.7 or 3.8 + - { env: TOXENV=py35-django22, python: 3.5 } - { env: TOXENV=py36-django22, python: 3.6 } - { env: TOXENV=py37-django22, python: 3.7 } - - { env: TOXENV=py37-django22-postgres DB=postgres, python: 3.7 } + - { env: TOXENV=py38-django22, python: 3.8 } + - { env: TOXENV=py38-django22-postgres DB=postgres, python: 3.8 } + # Django 3.0: Python 3.6, 3.7 or 3.8 + - { env: TOXENV=py36-django30, python: 3.6 } + - { env: TOXENV=py37-django30, python: 3.7 } + - { env: TOXENV=py38-django30, python: 3.8 } + - { env: TOXENV=py38-django30-postgres DB=postgres, python: 3.8 } # Django development master (direct from GitHub source): - { env: TOXENV=py36-djangomaster, python: 3.6 } - { env: TOXENV=py37-djangomaster, python: 3.7 } - - { env: TOXENV=py37-djangomaster-postgres DB=postgres, python: 3.7 } + - { env: TOXENV=py38-djangomaster, python: 3.8 } + - { env: TOXENV=py38-djangomaster-postgres DB=postgres, python: 3.8 } allow_failures: - env: TOXENV=py36-djangomaster - env: TOXENV=py37-djangomaster - - env: TOXENV=py37-djangomaster-postgres DB=postgres + - env: TOXENV=py38-djangomaster + - env: TOXENV=py38-djangomaster-postgres DB=postgres cache: directories: diff --git a/README.rst b/README.rst index 76a95b9..2905e12 100644 --- a/README.rst +++ b/README.rst @@ -62,7 +62,7 @@ Django to perform an ``INNER JOIN`` to fetch the model fields from the database. While taking this in mind, there are valid reasons for using subclassed models. That's what this library is designed for! -The current release of *django-polymorphic* supports Django 1.11, 2.0, 2.1, 2.2 and Python 2.7 and 3.5+ is supported. +The current release of *django-polymorphic* supports Django 2.1, 2.2, 3.0 and Python 3.5+ is supported. For older Django versions, install *django-polymorphic==1.3*. For more information, see the `documentation at Read the Docs `_. diff --git a/setup.cfg b/setup.cfg index a805f90..74740de 100644 --- a/setup.cfg +++ b/setup.cfg @@ -14,18 +14,18 @@ classifiers = Development Status :: 5 - Production/Stable Environment :: Web Environment Framework :: Django - Framework :: Django :: 1.11 - Framework :: Django :: 2.0 Framework :: Django :: 2.1 Framework :: Django :: 2.2 + Framework :: Django :: 3.0 Intended Audience :: Developers License :: OSI Approved :: BSD License Operating System :: OS Independent - Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 + Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 Topic :: Database [options] diff --git a/tox.ini b/tox.ini index 8ccc8fe..abe33ca 100644 --- a/tox.ini +++ b/tox.ini @@ -1,9 +1,9 @@ [tox] envlist = - py27-django{111} - py35-django{111,20} - py36-django{111,20,21,22,master} - py37-django{21,22,master} + py35-django{21,22,master} + py36-django{21,22,30,master} + py37-django{21,22,30,master} + py38-django{21,22,30,master} docs [testenv] @@ -14,10 +14,9 @@ setenv = deps = coverage dj-database-url - django111: Django >= 1.11, < 2.0 - django20: Django ~= 2.0 django21: Django ~= 2.1 django22: Django ~= 2.2 + django30: Django ~= 3.0 djangomaster: https://github.com/django/django/archive/master.tar.gz postgres: psycopg2 commands = From 85469082d0dcfac04ee92d205d8bee1a4c3aef4d Mon Sep 17 00:00:00 2001 From: Bastien Vallet Date: Tue, 4 Aug 2020 14:38:15 +0200 Subject: [PATCH 2/5] Remove python_2_unicode_compatible method --- example/orders/models.py | 3 --- polymorphic/compat.py | 19 ------------------- polymorphic/managers.py | 2 -- polymorphic/showfields.py | 2 -- 4 files changed, 26 deletions(-) diff --git a/example/orders/models.py b/example/orders/models.py index cf12286..97ec22e 100644 --- a/example/orders/models.py +++ b/example/orders/models.py @@ -1,12 +1,10 @@ from django.db import models from django.utils.dates import MONTHS_3 -from django.utils.six import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ from polymorphic.models import PolymorphicModel -@python_2_unicode_compatible class Order(models.Model): """ An example order that has polymorphic relations @@ -23,7 +21,6 @@ class Order(models.Model): return self.title -@python_2_unicode_compatible class Payment(PolymorphicModel): """ A generic payment model. diff --git a/polymorphic/compat.py b/polymorphic/compat.py index e52a3a9..094b161 100644 --- a/polymorphic/compat.py +++ b/polymorphic/compat.py @@ -24,22 +24,3 @@ def with_metaclass(meta, *bases): return meta(name, bases, d) return type.__new__(metaclass, "temporary_class", (), {}) - - -def python_2_unicode_compatible(klass): - """ - A decorator that defines __unicode__ and __str__ methods under Python 2. - Under Python 3 it does nothing. - - To support Python 2 and 3 with a single code base, define a __str__ method - returning text and apply this decorator to the class. - """ - if PY2: - if "__str__" not in klass.__dict__: - raise ValueError( - "@python_2_unicode_compatible cannot be applied " - "to %s because it doesn't define __str__()." % klass.__name__ - ) - klass.__unicode__ = klass.__str__ - klass.__str__ = lambda self: self.__unicode__().encode("utf-8") - return klass diff --git a/polymorphic/managers.py b/polymorphic/managers.py index a43840b..901f086 100644 --- a/polymorphic/managers.py +++ b/polymorphic/managers.py @@ -6,13 +6,11 @@ from __future__ import unicode_literals from django.db import models -from polymorphic.compat import python_2_unicode_compatible from polymorphic.query import PolymorphicQuerySet __all__ = ("PolymorphicManager", "PolymorphicQuerySet") -@python_2_unicode_compatible class PolymorphicManager(models.Manager): """ Manager for PolymorphicModel diff --git a/polymorphic/showfields.py b/polymorphic/showfields.py index ffa14f5..0d62d88 100644 --- a/polymorphic/showfields.py +++ b/polymorphic/showfields.py @@ -4,12 +4,10 @@ import re from django.db import models from . import compat -from .compat import python_2_unicode_compatible RE_DEFERRED = re.compile("_Deferred_.*") -@python_2_unicode_compatible class ShowFieldBase(object): """ base class for the ShowField... model mixins, does the work """ From 68605ba2baee69367824e4b30aaa24b52e2c501b Mon Sep 17 00:00:00 2001 From: Bastien Vallet Date: Tue, 4 Aug 2020 14:43:07 +0200 Subject: [PATCH 3/5] [compat] Remove PY2 compat artefacts - No need to specific types - Remove __future__ imports --- docs/migrating.rst | 1 - polymorphic/admin/parentadmin.py | 18 +++--------------- polymorphic/base.py | 2 -- polymorphic/compat.py | 18 ------------------ polymorphic/contrib/extra_views.py | 2 -- polymorphic/managers.py | 1 - polymorphic/models.py | 2 -- polymorphic/query.py | 4 +--- polymorphic/query_translate.py | 4 +--- polymorphic/showfields.py | 2 +- .../templatetags/polymorphic_admin_tags.py | 2 +- polymorphic/tests/test_multidb.py | 2 -- polymorphic/utils.py | 9 ++------- 13 files changed, 9 insertions(+), 58 deletions(-) diff --git a/docs/migrating.rst b/docs/migrating.rst index 866da3b..2db2355 100644 --- a/docs/migrating.rst +++ b/docs/migrating.rst @@ -29,7 +29,6 @@ can be included in a single Django migration. For example: .. code-block:: python # -*- coding: utf-8 -*- - from __future__ import unicode_literals from django.db import migrations, models diff --git a/polymorphic/admin/parentadmin.py b/polymorphic/admin/parentadmin.py index c9f7598..7addfde 100644 --- a/polymorphic/admin/parentadmin.py +++ b/polymorphic/admin/parentadmin.py @@ -1,8 +1,6 @@ """ The parent admin displays the list view of the base model. """ -import sys - from django.contrib import admin from django.contrib.admin.helpers import AdminErrorList, AdminForm from django.contrib.admin.templatetags.admin_urls import add_preserved_filters @@ -11,6 +9,7 @@ from django.core.exceptions import ImproperlyConfigured, PermissionDenied from django.db import models from django.http import Http404, HttpResponseRedirect from django.template.response import TemplateResponse +from django.urls import URLResolver from django.utils.encoding import force_text from django.utils.http import urlencode from django.utils.safestring import mark_safe @@ -20,17 +19,6 @@ from polymorphic.utils import get_base_polymorphic_model from .forms import PolymorphicModelChoiceForm -try: - # Django 2.0+ - from django.urls import URLResolver -except ImportError: - # Django < 2.0 - from django.urls import RegexURLResolver as URLResolver - - -if sys.version_info[0] >= 3: - long = int - class RegistrationClosed(RuntimeError): "The admin model can't be registered anymore at this point." @@ -293,9 +281,9 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin): try: pos = path.find("/") if pos == -1: - object_id = long(path) + object_id = int(path) else: - object_id = long(path[0:pos]) + object_id = int(path[0:pos]) except ValueError: raise Http404( "No ct_id parameter, unable to find admin subclass for path '{0}'.".format( diff --git a/polymorphic/base.py b/polymorphic/base.py index 81d988a..1bf6059 100644 --- a/polymorphic/base.py +++ b/polymorphic/base.py @@ -2,8 +2,6 @@ """ PolymorphicModel Meta Class """ -from __future__ import absolute_import - import inspect import os import sys diff --git a/polymorphic/compat.py b/polymorphic/compat.py index 094b161..1589eb2 100644 --- a/polymorphic/compat.py +++ b/polymorphic/compat.py @@ -1,22 +1,4 @@ """Compatibility with Python 2 (taken from 'django.utils.six')""" -import sys - -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 - - -if PY3: - string_types = (str,) - integer_types = (int,) - class_types = (type,) - text_type = str - binary_type = bytes - - MAXSIZE = sys.maxsize -else: - string_types = (basestring,) - integer_types = (int, long) - def with_metaclass(meta, *bases): class metaclass(type): diff --git a/polymorphic/contrib/extra_views.py b/polymorphic/contrib/extra_views.py index f3e050d..9822c47 100644 --- a/polymorphic/contrib/extra_views.py +++ b/polymorphic/contrib/extra_views.py @@ -4,8 +4,6 @@ The ``extra_views.advanced`` provides a method to combine that with a create/upd This package provides classes that support both options for polymorphic formsets. """ -from __future__ import absolute_import - import extra_views from django.core.exceptions import ImproperlyConfigured diff --git a/polymorphic/managers.py b/polymorphic/managers.py index 901f086..5e894d4 100644 --- a/polymorphic/managers.py +++ b/polymorphic/managers.py @@ -2,7 +2,6 @@ """ The manager class for use in the models. """ -from __future__ import unicode_literals from django.db import models diff --git a/polymorphic/models.py b/polymorphic/models.py index 63731e8..5c34ff7 100644 --- a/polymorphic/models.py +++ b/polymorphic/models.py @@ -2,8 +2,6 @@ """ Seamless Polymorphic Inheritance for Django Models """ -from __future__ import absolute_import - from django.contrib.contenttypes.models import ContentType from django.db import models from django.db.models.fields.related import ( diff --git a/polymorphic/query.py b/polymorphic/query.py index 4dc4d30..7277f6c 100644 --- a/polymorphic/query.py +++ b/polymorphic/query.py @@ -2,8 +2,6 @@ """ QuerySet for PolymorphicModel """ -from __future__ import absolute_import - import copy from collections import defaultdict @@ -177,7 +175,7 @@ class PolymorphicQuerySet(QuerySet): """translate the field paths in the args, then call vanilla order_by.""" field_names = [ translate_polymorphic_field_path(self.model, a) - if isinstance(a, compat.string_types) + if isinstance(a, str) else a # allow expressions to pass unchanged for a in field_names ] diff --git a/polymorphic/query_translate.py b/polymorphic/query_translate.py index cbecf10..08798bc 100644 --- a/polymorphic/query_translate.py +++ b/polymorphic/query_translate.py @@ -2,8 +2,6 @@ """ PolymorphicQuerySet support functions """ -from __future__ import absolute_import - import copy from collections import deque @@ -144,7 +142,7 @@ def translate_polymorphic_field_path(queryset_model, field_path): into modela__modelb__modelc__field3. Returns: translated path (unchanged, if no translation needed) """ - if not isinstance(field_path, compat.string_types): + if not isinstance(field_path, str): raise ValueError("Expected field name as string: {0}".format(field_path)) classname, sep, pure_field_path = field_path.partition("___") diff --git a/polymorphic/showfields.py b/polymorphic/showfields.py index 0d62d88..d4c21b5 100644 --- a/polymorphic/showfields.py +++ b/polymorphic/showfields.py @@ -40,7 +40,7 @@ class ShowFieldBase(object): out += content.__class__.__name__ elif issubclass(field_type, models.ManyToManyField): out += "%d" % content.count() - elif isinstance(content, compat.integer_types): + elif isinstance(content, int): out += str(content) elif content is None: out += "None" diff --git a/polymorphic/templatetags/polymorphic_admin_tags.py b/polymorphic/templatetags/polymorphic_admin_tags.py index 9914b84..87057d6 100644 --- a/polymorphic/templatetags/polymorphic_admin_tags.py +++ b/polymorphic/templatetags/polymorphic_admin_tags.py @@ -31,7 +31,7 @@ class BreadcrumbScope(Node): # Instead, have an assignment tag that inserts that in the template. base_opts = self.base_opts.resolve(context) new_vars = {} - if base_opts and not isinstance(base_opts, compat.string_types): + if base_opts and not isinstance(base_opts, str): new_vars = { "app_label": base_opts.app_label, # What this is all about "opts": base_opts, diff --git a/polymorphic/tests/test_multidb.py b/polymorphic/tests/test_multidb.py index eec9e5a..b70a8d5 100644 --- a/polymorphic/tests/test_multidb.py +++ b/polymorphic/tests/test_multidb.py @@ -1,5 +1,3 @@ -from __future__ import print_function - from django.contrib.contenttypes.models import ContentType from django.db.models import Q from django.test import TestCase diff --git a/polymorphic/utils.py b/polymorphic/utils.py index 14c9a9f..8c00758 100644 --- a/polymorphic/utils.py +++ b/polymorphic/utils.py @@ -1,5 +1,3 @@ -import sys - from django.contrib.contenttypes.models import ContentType from django.db import DEFAULT_DB_ALIAS @@ -59,12 +57,9 @@ def sort_by_subclass(*classes): """ Sort a series of models by their inheritance order. """ - if sys.version_info[0] == 2: - return sorted(classes, cmp=_compare_mro) - else: - from functools import cmp_to_key + from functools import cmp_to_key - return sorted(classes, key=cmp_to_key(_compare_mro)) + return sorted(classes, key=cmp_to_key(_compare_mro)) def get_base_polymorphic_model(ChildModel, allow_abstract=False): From b6921baedee140ba3a5622080e6b8c5860f527f1 Mon Sep 17 00:00:00 2001 From: Bastien Vallet Date: Wed, 5 Aug 2020 10:39:48 +0200 Subject: [PATCH 4/5] Add support for Django 3.1 --- .travis.yml | 5 +++++ polymorphic/query.py | 2 +- polymorphic/query_translate.py | 4 ++-- setup.cfg | 1 + tox.ini | 7 ++++--- 5 files changed, 13 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9edc4d6..76a7e2d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,6 +27,11 @@ matrix: - { env: TOXENV=py37-django30, python: 3.7 } - { env: TOXENV=py38-django30, python: 3.8 } - { env: TOXENV=py38-django30-postgres DB=postgres, python: 3.8 } + # Django 3.1: Python 3.6, 3.7 or 3.8 + - { env: TOXENV=py36-django31, python: 3.6 } + - { env: TOXENV=py37-django31, python: 3.7 } + - { env: TOXENV=py38-django31, python: 3.8 } + - { env: TOXENV=py38-django31-postgres DB=postgres, python: 3.8 } # Django development master (direct from GitHub source): - { env: TOXENV=py36-djangomaster, python: 3.6 } - { env: TOXENV=py37-djangomaster, python: 3.7 } diff --git a/polymorphic/query.py b/polymorphic/query.py index 7277f6c..197fc0c 100644 --- a/polymorphic/query.py +++ b/polymorphic/query.py @@ -6,7 +6,7 @@ import copy from collections import defaultdict from django.contrib.contenttypes.models import ContentType -from django.db.models import FieldDoesNotExist +from django.core.exceptions import FieldDoesNotExist from django.db.models.query import ModelIterable, Q, QuerySet from . import compat diff --git a/polymorphic/query_translate.py b/polymorphic/query_translate.py index 08798bc..45328a9 100644 --- a/polymorphic/query_translate.py +++ b/polymorphic/query_translate.py @@ -7,7 +7,7 @@ from collections import deque from django.apps import apps from django.contrib.contenttypes.models import ContentType -from django.core.exceptions import FieldError +from django.core.exceptions import FieldError, FieldDoesNotExist from django.db import models from django.db.models import Q from django.db.models.fields.related import ForeignObjectRel, RelatedField @@ -187,7 +187,7 @@ def translate_polymorphic_field_path(queryset_model, field_path): # Can also test whether the field exists in the related object to avoid ambiguity between # class names and field names, but that never happens when your class names are in CamelCase. return field_path # No exception raised, field does exist. - except models.FieldDoesNotExist: + except FieldDoesNotExist: pass submodels = _get_all_sub_models(queryset_model) diff --git a/setup.cfg b/setup.cfg index 74740de..c6755cb 100644 --- a/setup.cfg +++ b/setup.cfg @@ -17,6 +17,7 @@ classifiers = Framework :: Django :: 2.1 Framework :: Django :: 2.2 Framework :: Django :: 3.0 + Framework :: Django :: 3.1 Intended Audience :: Developers License :: OSI Approved :: BSD License Operating System :: OS Independent diff --git a/tox.ini b/tox.ini index abe33ca..e507416 100644 --- a/tox.ini +++ b/tox.ini @@ -1,9 +1,9 @@ [tox] envlist = py35-django{21,22,master} - py36-django{21,22,30,master} - py37-django{21,22,30,master} - py38-django{21,22,30,master} + py36-django{21,22,30,31,master} + py37-django{21,22,30,31,master} + py38-django{21,22,30,31,master} docs [testenv] @@ -17,6 +17,7 @@ deps = django21: Django ~= 2.1 django22: Django ~= 2.2 django30: Django ~= 3.0 + django31: Django ~= 3.1 djangomaster: https://github.com/django/django/archive/master.tar.gz postgres: psycopg2 commands = From 7f248bd4bd1268b3491c6efcb37ac1dcf7e748b6 Mon Sep 17 00:00:00 2001 From: Bastien Vallet Date: Mon, 10 Aug 2020 22:48:31 +0200 Subject: [PATCH 5/5] Update README for Django compatibility list --- README.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 2905e12..15d49be 100644 --- a/README.rst +++ b/README.rst @@ -62,7 +62,8 @@ Django to perform an ``INNER JOIN`` to fetch the model fields from the database. While taking this in mind, there are valid reasons for using subclassed models. That's what this library is designed for! -The current release of *django-polymorphic* supports Django 2.1, 2.2, 3.0 and Python 3.5+ is supported. +The current release of *django-polymorphic* supports Django 2.1, 2.2, 3.0, 3.1 +and Python 3.5+ is supported. For older Django versions, install *django-polymorphic==1.3*. For more information, see the `documentation at Read the Docs `_.