From 840bfe8d22700ef21faee9c6bdf09701952fc495 Mon Sep 17 00:00:00 2001 From: Charlie Denton Date: Sun, 29 Oct 2017 00:02:58 +0100 Subject: [PATCH 01/12] Fix import in Django 2.0 Because the URL mechanism has changed in Django 2.0, the RegexURLResolver has been renamed. --- polymorphic/admin/parentadmin.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/polymorphic/admin/parentadmin.py b/polymorphic/admin/parentadmin.py index 00dcb34..32fb3a9 100644 --- a/polymorphic/admin/parentadmin.py +++ b/polymorphic/admin/parentadmin.py @@ -11,16 +11,22 @@ from django.core.exceptions import PermissionDenied, ImproperlyConfigured from django.db import models from django.http import Http404, HttpResponseRedirect from django.template.response import TemplateResponse -from django.urls import RegexURLResolver from django.utils.encoding import force_text from django.utils.http import urlencode from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _ - 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 @@ -265,7 +271,7 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin): ct_id = self.model.objects.values_list('polymorphic_ctype_id', flat=True).get(pk=object_id) real_admin = self._get_real_admin_by_ct(ct_id) - resolver = RegexURLResolver('^', real_admin.urls) + resolver = URLResolver('^', real_admin.urls) resolvermatch = resolver.resolve(path) # May raise Resolver404 if not resolvermatch: raise Http404("No match for path '{0}' in admin subclass.".format(path)) From 1a4595f5ceb29854360be559fbb43b92fb1af157 Mon Sep 17 00:00:00 2001 From: Charlie Denton Date: Sun, 29 Oct 2017 00:05:11 +0100 Subject: [PATCH 02/12] Fix deprecated admin site URL in tests The old format was deprecated in Django 1.9, and removed in Django 2.0. Ref: https://docs.djangoproject.com/en/stable/releases/1.9/#passing-a-3-tuple-or-an-app-name-to-include --- polymorphic/tests/admintestcase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polymorphic/tests/admintestcase.py b/polymorphic/tests/admintestcase.py index fb2e560..618d8fc 100644 --- a/polymorphic/tests/admintestcase.py +++ b/polymorphic/tests/admintestcase.py @@ -49,7 +49,7 @@ class AdminTestCase(TestCase): # Make sure the URLs are reachable by reverse() clear_url_caches() set_urlconf(tuple([ - url('^tmp-admin/', include(self.admin_site.urls)) + url('^tmp-admin/', self.admin_site.urls) ])) def get_admin_instance(self, model): From 96b11b50f94138ade6505b25748babeb5cff8e75 Mon Sep 17 00:00:00 2001 From: Charlie Denton Date: Sun, 29 Oct 2017 00:17:47 +0100 Subject: [PATCH 03/12] Remove django 1.10 from tests --- .travis.yml | 7 ------- docs/changelog.rst | 2 +- tox.ini | 7 +++---- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index a621d1d..f59dac9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,11 +5,8 @@ language: python python: "3.6" env: - - TOXENV=py27-django110 - TOXENV=py27-django111 - - TOXENV=py34-django110 - TOXENV=py34-django111 - - TOXENV=py35-django110 - TOXENV=py35-django111 - TOXENV=py35-djangomaster - TOXENV=py36-django111 @@ -24,15 +21,11 @@ services: matrix: fast_finish: true include: - - python: "3.5" - env: TOXENV=py35-django110 - python: "3.5" env: TOXENV=py35-django111 - python: "3.5" env: TOXENV=py35-djangomaster exclude: - - python: "3.6" - env: TOXENV=py35-django110 - python: "3.6" env: TOXENV=py35-django111 - python: "3.6" diff --git a/docs/changelog.rst b/docs/changelog.rst index bc242ab..9e4c46e 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,7 +4,7 @@ Changelog Changes in git -------------- -* **BACKWARDS INCOMPATIBILITY:** Dropped Django 1.8 support. +* **BACKWARDS INCOMPATIBILITY:** Dropped Django 1.8 and 1.10 support. * **BACKWARDS INCOMPATIBILITY:** Removed old deprecated code from 1.0, thus: * Import managers from ``polymorphic.managers`` (plural), not ``polymorphic.manager``. diff --git a/tox.ini b/tox.ini index f8e95a8..4d4cb8a 100644 --- a/tox.ini +++ b/tox.ini @@ -1,8 +1,8 @@ [tox] envlist = - py27-django{110,111} - py34-django{110,111} - py35-django{110,111,master} + py27-django{111} + py34-django{111} + py35-django{111,master} py36-django{111,master} docs @@ -14,7 +14,6 @@ setenv = deps = coverage dj-database-url - django110: Django >= 1.10, < 1.11 django111: Django >= 1.11, < 2.0 djangomaster: https://github.com/django/django/archive/master.tar.gz postgres: psycopg2 From 60e56ed47282577d80bb98710c5898ca7f1f31e2 Mon Sep 17 00:00:00 2001 From: Charlie Denton Date: Sun, 29 Oct 2017 00:18:05 +0100 Subject: [PATCH 04/12] Add django 2.0 to tests --- .travis.yml | 12 ++++++++++++ tox.ini | 7 ++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index f59dac9..9a3175a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,12 +7,16 @@ python: "3.6" env: - TOXENV=py27-django111 - TOXENV=py34-django111 + - TOXENV=py34-django200 - TOXENV=py35-django111 + - TOXENV=py35-django200 - TOXENV=py35-djangomaster - TOXENV=py36-django111 + - TOXENV=py36-django200 - TOXENV=py36-djangomaster # XXX: Use a matrix to build these? - TOXENV=py36-django111-postgres DB=postgres + - TOXENV=py36-django200-postgres DB=postgres - TOXENV=py36-djangomaster-postgres DB=postgres services: @@ -21,13 +25,21 @@ services: matrix: fast_finish: true include: + - python: "2.7" + env: TOXENV=py27-django111 - python: "3.5" env: TOXENV=py35-django111 + - python: "3.5" + env: TOXENV=py35-django200 - python: "3.5" env: TOXENV=py35-djangomaster exclude: + - python: "3.6" + env: TOXENV=py27-django111 - python: "3.6" env: TOXENV=py35-django111 + - python: "3.6" + env: TOXENV=py35-django200 - python: "3.6" env: TOXENV=py35-djangomaster allow_failures: diff --git a/tox.ini b/tox.ini index 4d4cb8a..6c96640 100644 --- a/tox.ini +++ b/tox.ini @@ -1,9 +1,9 @@ [tox] envlist = py27-django{111} - py34-django{111} - py35-django{111,master} - py36-django{111,master} + py34-django{111,200} + py35-django{111,200,master} + py36-django{111,200,master} docs [testenv] @@ -15,6 +15,7 @@ deps = coverage dj-database-url django111: Django >= 1.11, < 2.0 + django200: Django ~= 2.0.0 djangomaster: https://github.com/django/django/archive/master.tar.gz postgres: psycopg2 commands = From 120520e44d7dfcf3079bfdc9a118d28b5620cb14 Mon Sep 17 00:00:00 2001 From: Charlie Denton Date: Sun, 29 Oct 2017 00:44:48 +0100 Subject: [PATCH 05/12] Fix `add_media` util for Django 2.0 Neither `add_css` nor `add_js` exist in Django 2.0 because the method for adding `Media` classes together has changed. Ref: https://github.com/django/django/commit/c19b56f633e172b3c02094cbe12d28865ee57772 --- polymorphic/formsets/utils.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/polymorphic/formsets/utils.py b/polymorphic/formsets/utils.py index 3d55f8b..5dac6a7 100644 --- a/polymorphic/formsets/utils.py +++ b/polymorphic/formsets/utils.py @@ -1,11 +1,17 @@ """ Internal utils """ +import django def add_media(dest, media): """ Optimized version of django.forms.Media.__add__() that doesn't create new objects. + + Only required for Django < 2.0 """ - dest.add_css(media._css) - dest.add_js(media._js) + if django.VERSION >= (2, 0): + dest += media + else: + dest.add_css(media._css) + dest.add_js(media._js) From e2bf741d5db88cfe7fbc67beba4c14c200cd09e0 Mon Sep 17 00:00:00 2001 From: Charlie Denton Date: Sun, 29 Oct 2017 01:04:17 +0100 Subject: [PATCH 06/12] Remove models that are not referenced in tests This silences one instance of the warning that's being printed in tests for versions of Django before 2.0: > RemovedInDjango20Warning: Managers from concrete parents will soon > qualify as default managers if they appear before any other managers > in the MRO. --- polymorphic/tests/models.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/polymorphic/tests/models.py b/polymorphic/tests/models.py index 47f21e4..60fc619 100644 --- a/polymorphic/tests/models.py +++ b/polymorphic/tests/models.py @@ -237,21 +237,6 @@ class PlainChildModelWithManager(models.Model): objects = PlainMyManager() -class MgrInheritA(models.Model): - mgrA = models.Manager() - mgrA2 = models.Manager() - field1 = models.CharField(max_length=10) - - -class MgrInheritB(MgrInheritA): - mgrB = models.Manager() - field2 = models.CharField(max_length=10) - - -class MgrInheritC(ShowFieldTypeAndContent, MgrInheritB): - pass - - class BlogBase(ShowFieldTypeAndContent, PolymorphicModel): name = models.CharField(max_length=10) From 6fb34a0a4490ea484efd10e461bff2820544baa3 Mon Sep 17 00:00:00 2001 From: Charlie Denton Date: Sun, 29 Oct 2017 01:32:56 +0100 Subject: [PATCH 07/12] Mention django 2.0 support in changelog --- docs/changelog.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 9e4c46e..ded13c1 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -11,6 +11,7 @@ Changes in git * Register child models to the admin as well using ``@admin.register()`` or ``admin.site.register()``, as this is no longer done automatically. +* Django 2.0 support. * Added ``PolymorphicTypeUndefined`` exception for incomplete imported models. When a data migration or import creates an polymorphic model, the ``polymorphic_ctype_id`` field should be filled in manually too. From 3f6d94139c95d36685cd3d19d86d27db3143f907 Mon Sep 17 00:00:00 2001 From: Charlie Denton Date: Fri, 3 Nov 2017 23:28:07 +0000 Subject: [PATCH 08/12] Remove unused function in tests --- polymorphic/tests/test_orm.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/polymorphic/tests/test_orm.py b/polymorphic/tests/test_orm.py index 441877e..9500606 100644 --- a/polymorphic/tests/test_orm.py +++ b/polymorphic/tests/test_orm.py @@ -997,17 +997,3 @@ class PolymorphicTests(TransactionTestCase): MultiTableDerived.objects.bulk_create([ MultiTableDerived(field1='field1', field2='field2') ]) - - -def qrepr(data): - """ - Ensure consistent repr() output for the QuerySet object. - """ - if isinstance(data, QuerySet): - if django.VERSION < (1, 11): - # Django 1.10 still shows " Date: Fri, 3 Nov 2017 23:45:44 +0000 Subject: [PATCH 09/12] Clean up unrequired compat import --- polymorphic/models.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/polymorphic/models.py b/polymorphic/models.py index 6c7a9b4..893dddd 100644 --- a/polymorphic/models.py +++ b/polymorphic/models.py @@ -6,6 +6,7 @@ from __future__ import absolute_import from django.contrib.contenttypes.models import ContentType from django.db import models +from django.db.models.fields.related import ReverseOneToOneDescriptor, ForwardManyToOneDescriptor from django.db.utils import DEFAULT_DB_ALIAS from django.utils import six @@ -178,14 +179,6 @@ class PolymorphicModel(six.with_metaclass(PolymorphicModelBase, models.Model)): subclasses_and_superclasses_accessors = self._get_inheritance_relation_fields_and_models() - try: - from django.db.models.fields.related import ReverseOneToOneDescriptor, ForwardManyToOneDescriptor - except ImportError: - # django < 1.9 - from django.db.models.fields.related import ( - SingleRelatedObjectDescriptor as ReverseOneToOneDescriptor, - ReverseSingleRelatedObjectDescriptor as ForwardManyToOneDescriptor, - ) for name, model in subclasses_and_superclasses_accessors.items(): # Here be dragons. orig_accessor = getattr(self.__class__, name, None) From 388d0e12c04f29578e4492ddb3507b1b7eee8431 Mon Sep 17 00:00:00 2001 From: Charlie Denton Date: Fri, 3 Nov 2017 23:46:33 +0000 Subject: [PATCH 10/12] Update docs to reflect supported django versions --- README.rst | 4 ++-- docs/quickstart.rst | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 5b67d04..3d96553 100644 --- a/README.rst +++ b/README.rst @@ -60,8 +60,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 1.8, 1.10, 1.11 and Python 2.7 and 3.4+ is supported. -For older Django versions, install *django-polymorphic==1.2*. +The current release of *django-polymorphic* supports Django 1.11, 2.0 and Python 2.7 and 3.4+ is supported. +For older Django versions, install *django-polymorphic==1.3*. For more information, see the `documentation at Read the Docs `_. diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 3df803f..91ca7b3 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -12,8 +12,8 @@ Update the settings file:: 'django.contrib.contenttypes', ) -The current release of *django-polymorphic* supports Django 1.8, 1.10, 1.11 and Python 2.7 and 3.4+ is supported. -For older Django versions, use *django-polymorphic==1.2*. +The current release of *django-polymorphic* supports Django 1.11, 2.0 and Python 2.7 and 3.4+ is supported. +For older Django versions, use *django-polymorphic==1.3*. Making Your Models Polymorphic ------------------------------ From c3179058d38fac0c425c54a30f19b6e574091b0b Mon Sep 17 00:00:00 2001 From: Charlie Denton Date: Fri, 3 Nov 2017 23:47:10 +0000 Subject: [PATCH 11/12] Update trove classifiers and django requirement --- setup.cfg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 3fda5e8..3046638 100644 --- a/setup.cfg +++ b/setup.cfg @@ -14,8 +14,8 @@ classifiers = Development Status :: 5 - Production/Stable Environment :: Web Environment Framework :: Django - Framework :: Django :: 1.10 Framework :: Django :: 1.11 + Framework :: Django :: 2.0 Intended Audience :: Developers License :: OSI Approved :: BSD License Operating System :: OS Independent @@ -30,7 +30,7 @@ classifiers = packages = find: include_package_data = True install_requires = - Django >= 1.10 + Django >= 1.11 [options.packages.find] exclude = From ed40b9e3e292911ef839c758a53ce8bd09082516 Mon Sep 17 00:00:00 2001 From: Charlie Denton Date: Wed, 17 Jan 2018 22:01:02 +0000 Subject: [PATCH 12/12] Use assertRegex to ignore object IDs Sometimes the tests failed because these objects had IDs that differed from expectations. As the IDs are not relevant to this test, I have replaced the exact string match with a regex match that accepts any ID. --- polymorphic/tests/test_orm.py | 52 +++++++++++++++++------------------ 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/polymorphic/tests/test_orm.py b/polymorphic/tests/test_orm.py index 9500606..335d450 100644 --- a/polymorphic/tests/test_orm.py +++ b/polymorphic/tests/test_orm.py @@ -228,14 +228,14 @@ class PolymorphicTests(TransactionTestCase): objects_deferred = Model2A.objects.defer('field1') self.assertNotIn('field1', objects_deferred[0].__dict__, 'field1 was not deferred (using defer())') - self.assertEqual(repr(objects_deferred[0]), - '') - self.assertEqual(repr(objects_deferred[1]), - '') - self.assertEqual(repr(objects_deferred[2]), - '') - self.assertEqual(repr(objects_deferred[3]), - '') + self.assertRegex(repr(objects_deferred[0]), + '') + self.assertRegex(repr(objects_deferred[1]), + '') + self.assertRegex(repr(objects_deferred[2]), + '') + self.assertRegex(repr(objects_deferred[3]), + '') objects_only = Model2A.objects.only('pk', 'polymorphic_ctype', 'field1') @@ -246,33 +246,33 @@ class PolymorphicTests(TransactionTestCase): ' on a child model') self.assertNotIn('field4', objects_only[3].__dict__, 'field4 was not deferred (using only())') - self.assertEqual(repr(objects_only[0]), - '') - self.assertEqual(repr(objects_only[1]), - '') - self.assertEqual(repr(objects_only[2]), - '') - self.assertEqual(repr(objects_only[3]), - '') + self.assertRegex(repr(objects_only[0]), + '') + self.assertRegex(repr(objects_only[1]), + '') + self.assertRegex(repr(objects_only[2]), + '') + self.assertRegex(repr(objects_only[3]), + '') ModelX.objects.create(field_b="A1", field_x="A2") ModelY.objects.create(field_b="B1", field_y="B2") objects_deferred = Base.objects.defer('ModelY___field_y') - self.assertEqual(repr(objects_deferred[0]), - '') - self.assertEqual(repr(objects_deferred[1]), - '') + self.assertRegex(repr(objects_deferred[0]), + '') + self.assertRegex(repr(objects_deferred[1]), + '') objects_only = Base.objects.only( 'polymorphic_ctype', 'ModelY___field_y', 'ModelX___field_x', ) - self.assertEqual(repr(objects_only[0]), - '') - self.assertEqual(repr(objects_only[1]), - '') + self.assertRegex(repr(objects_only[0]), + '') + self.assertRegex(repr(objects_only[1]), + '') def test_defer_related_fields(self): self.create_model2abcd()