Merge pull request #318 from meshy/django-2.0

Add django 2.0 support
fix_request_path_info
Diederik van der Boor 2018-01-18 15:43:59 +01:00 committed by GitHub
commit e164026b43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 69 additions and 87 deletions

View File

@ -5,17 +5,18 @@ language: python
python: "3.6" python: "3.6"
env: env:
- TOXENV=py27-django110
- TOXENV=py27-django111 - TOXENV=py27-django111
- TOXENV=py34-django110
- TOXENV=py34-django111 - TOXENV=py34-django111
- TOXENV=py35-django110 - TOXENV=py34-django200
- TOXENV=py35-django111 - TOXENV=py35-django111
- TOXENV=py35-django200
- TOXENV=py35-djangomaster - TOXENV=py35-djangomaster
- TOXENV=py36-django111 - TOXENV=py36-django111
- TOXENV=py36-django200
- TOXENV=py36-djangomaster - TOXENV=py36-djangomaster
# XXX: Use a matrix to build these? # XXX: Use a matrix to build these?
- TOXENV=py36-django111-postgres DB=postgres - TOXENV=py36-django111-postgres DB=postgres
- TOXENV=py36-django200-postgres DB=postgres
- TOXENV=py36-djangomaster-postgres DB=postgres - TOXENV=py36-djangomaster-postgres DB=postgres
services: services:
@ -24,17 +25,21 @@ services:
matrix: matrix:
fast_finish: true fast_finish: true
include: include:
- python: "3.5" - python: "2.7"
env: TOXENV=py35-django110 env: TOXENV=py27-django111
- python: "3.5" - python: "3.5"
env: TOXENV=py35-django111 env: TOXENV=py35-django111
- python: "3.5"
env: TOXENV=py35-django200
- python: "3.5" - python: "3.5"
env: TOXENV=py35-djangomaster env: TOXENV=py35-djangomaster
exclude: exclude:
- python: "3.6" - python: "3.6"
env: TOXENV=py35-django110 env: TOXENV=py27-django111
- python: "3.6" - python: "3.6"
env: TOXENV=py35-django111 env: TOXENV=py35-django111
- python: "3.6"
env: TOXENV=py35-django200
- python: "3.6" - python: "3.6"
env: TOXENV=py35-djangomaster env: TOXENV=py35-djangomaster
allow_failures: allow_failures:

View File

@ -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. While taking this in mind, there are valid reasons for using subclassed models.
That's what this library is designed for! 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. 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.2*. For older Django versions, install *django-polymorphic==1.3*.
For more information, see the `documentation at Read the Docs <https://django-polymorphic.readthedocs.io/>`_. For more information, see the `documentation at Read the Docs <https://django-polymorphic.readthedocs.io/>`_.

View File

@ -4,13 +4,14 @@ Changelog
Changes in git 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: * **BACKWARDS INCOMPATIBILITY:** Removed old deprecated code from 1.0, thus:
* Import managers from ``polymorphic.managers`` (plural), not ``polymorphic.manager``. * Import managers from ``polymorphic.managers`` (plural), not ``polymorphic.manager``.
* Register child models to the admin as well using ``@admin.register()`` or ``admin.site.register()``, * Register child models to the admin as well using ``@admin.register()`` or ``admin.site.register()``,
as this is no longer done automatically. as this is no longer done automatically.
* Django 2.0 support.
* Added ``PolymorphicTypeUndefined`` exception for incomplete imported models. * Added ``PolymorphicTypeUndefined`` exception for incomplete imported models.
When a data migration or import creates an polymorphic model, When a data migration or import creates an polymorphic model,
the ``polymorphic_ctype_id`` field should be filled in manually too. the ``polymorphic_ctype_id`` field should be filled in manually too.

View File

@ -12,8 +12,8 @@ Update the settings file::
'django.contrib.contenttypes', '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. 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.2*. For older Django versions, use *django-polymorphic==1.3*.
Making Your Models Polymorphic Making Your Models Polymorphic
------------------------------ ------------------------------

View File

@ -11,16 +11,22 @@ from django.core.exceptions import PermissionDenied, ImproperlyConfigured
from django.db import models from django.db import models
from django.http import Http404, HttpResponseRedirect from django.http import Http404, HttpResponseRedirect
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.urls import RegexURLResolver
from django.utils.encoding import force_text from django.utils.encoding import force_text
from django.utils.http import urlencode from django.utils.http import urlencode
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from polymorphic.utils import get_base_polymorphic_model from polymorphic.utils import get_base_polymorphic_model
from .forms import PolymorphicModelChoiceForm 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: if sys.version_info[0] >= 3:
long = int 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) 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) 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 resolvermatch = resolver.resolve(path) # May raise Resolver404
if not resolvermatch: if not resolvermatch:
raise Http404("No match for path '{0}' in admin subclass.".format(path)) raise Http404("No match for path '{0}' in admin subclass.".format(path))

View File

@ -1,11 +1,17 @@
""" """
Internal utils Internal utils
""" """
import django
def add_media(dest, media): def add_media(dest, media):
""" """
Optimized version of django.forms.Media.__add__() that doesn't create new objects. Optimized version of django.forms.Media.__add__() that doesn't create new objects.
Only required for Django < 2.0
""" """
dest.add_css(media._css) if django.VERSION >= (2, 0):
dest.add_js(media._js) dest += media
else:
dest.add_css(media._css)
dest.add_js(media._js)

View File

@ -6,6 +6,7 @@ from __future__ import absolute_import
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db import models from django.db import models
from django.db.models.fields.related import ReverseOneToOneDescriptor, ForwardManyToOneDescriptor
from django.db.utils import DEFAULT_DB_ALIAS from django.db.utils import DEFAULT_DB_ALIAS
from django.utils import six 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() 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(): for name, model in subclasses_and_superclasses_accessors.items():
# Here be dragons. # Here be dragons.
orig_accessor = getattr(self.__class__, name, None) orig_accessor = getattr(self.__class__, name, None)

View File

@ -49,7 +49,7 @@ class AdminTestCase(TestCase):
# Make sure the URLs are reachable by reverse() # Make sure the URLs are reachable by reverse()
clear_url_caches() clear_url_caches()
set_urlconf(tuple([ set_urlconf(tuple([
url('^tmp-admin/', include(self.admin_site.urls)) url('^tmp-admin/', self.admin_site.urls)
])) ]))
def get_admin_instance(self, model): def get_admin_instance(self, model):

View File

@ -237,21 +237,6 @@ class PlainChildModelWithManager(models.Model):
objects = PlainMyManager() 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): class BlogBase(ShowFieldTypeAndContent, PolymorphicModel):
name = models.CharField(max_length=10) name = models.CharField(max_length=10)

View File

@ -228,14 +228,14 @@ class PolymorphicTests(TransactionTestCase):
objects_deferred = Model2A.objects.defer('field1') objects_deferred = Model2A.objects.defer('field1')
self.assertNotIn('field1', objects_deferred[0].__dict__, 'field1 was not deferred (using defer())') self.assertNotIn('field1', objects_deferred[0].__dict__, 'field1 was not deferred (using defer())')
self.assertEqual(repr(objects_deferred[0]), self.assertRegex(repr(objects_deferred[0]),
'<Model2A: id 1, field1 (CharField), deferred[field1]>') '<Model2A: id \d+, field1 \(CharField\), deferred\[field1\]>')
self.assertEqual(repr(objects_deferred[1]), self.assertRegex(repr(objects_deferred[1]),
'<Model2B: id 2, field1 (CharField), field2 (CharField), deferred[field1]>') '<Model2B: id \d+, field1 \(CharField\), field2 \(CharField\), deferred\[field1\]>')
self.assertEqual(repr(objects_deferred[2]), self.assertRegex(repr(objects_deferred[2]),
'<Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField), deferred[field1]>') '<Model2C: id \d+, field1 \(CharField\), field2 \(CharField\), field3 \(CharField\), deferred\[field1\]>')
self.assertEqual(repr(objects_deferred[3]), self.assertRegex(repr(objects_deferred[3]),
'<Model2D: id 4, field1 (CharField), field2 (CharField), field3 (CharField), field4 (CharField), deferred[field1]>') '<Model2D: id \d+, field1 \(CharField\), field2 \(CharField\), field3 \(CharField\), field4 \(CharField\), deferred\[field1\]>')
objects_only = Model2A.objects.only('pk', 'polymorphic_ctype', 'field1') objects_only = Model2A.objects.only('pk', 'polymorphic_ctype', 'field1')
@ -246,33 +246,33 @@ class PolymorphicTests(TransactionTestCase):
' on a child model') ' on a child model')
self.assertNotIn('field4', objects_only[3].__dict__, self.assertNotIn('field4', objects_only[3].__dict__,
'field4 was not deferred (using only())') 'field4 was not deferred (using only())')
self.assertEqual(repr(objects_only[0]), self.assertRegex(repr(objects_only[0]),
'<Model2A: id 1, field1 (CharField)>') '<Model2A: id \d+, field1 \(CharField\)>')
self.assertEqual(repr(objects_only[1]), self.assertRegex(repr(objects_only[1]),
'<Model2B: id 2, field1 (CharField), field2 (CharField), deferred[field2]>') '<Model2B: id \d+, field1 \(CharField\), field2 \(CharField\), deferred\[field2\]>')
self.assertEqual(repr(objects_only[2]), self.assertRegex(repr(objects_only[2]),
'<Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField), ' '<Model2C: id \d+, field1 \(CharField\), field2 \(CharField\), field3 \(CharField\), '
'deferred[field2,field3,model2a_ptr_id]>') 'deferred\[field2,field3,model2a_ptr_id\]>')
self.assertEqual(repr(objects_only[3]), self.assertRegex(repr(objects_only[3]),
'<Model2D: id 4, field1 (CharField), field2 (CharField), field3 (CharField), field4 (CharField), ' '<Model2D: id \d+, field1 \(CharField\), field2 \(CharField\), field3 \(CharField\), field4 \(CharField\), '
'deferred[field2,field3,field4,model2a_ptr_id,model2b_ptr_id]>') 'deferred\[field2,field3,field4,model2a_ptr_id,model2b_ptr_id\]>')
ModelX.objects.create(field_b="A1", field_x="A2") ModelX.objects.create(field_b="A1", field_x="A2")
ModelY.objects.create(field_b="B1", field_y="B2") ModelY.objects.create(field_b="B1", field_y="B2")
objects_deferred = Base.objects.defer('ModelY___field_y') objects_deferred = Base.objects.defer('ModelY___field_y')
self.assertEqual(repr(objects_deferred[0]), self.assertRegex(repr(objects_deferred[0]),
'<ModelX: id 3, field_b (CharField), field_x (CharField)>') '<ModelX: id \d+, field_b \(CharField\), field_x \(CharField\)>')
self.assertEqual(repr(objects_deferred[1]), self.assertRegex(repr(objects_deferred[1]),
'<ModelY: id 4, field_b (CharField), field_y (CharField), deferred[field_y]>') '<ModelY: id \d+, field_b \(CharField\), field_y \(CharField\), deferred\[field_y\]>')
objects_only = Base.objects.only( objects_only = Base.objects.only(
'polymorphic_ctype', 'ModelY___field_y', 'ModelX___field_x', 'polymorphic_ctype', 'ModelY___field_y', 'ModelX___field_x',
) )
self.assertEqual(repr(objects_only[0]), self.assertRegex(repr(objects_only[0]),
'<ModelX: id 3, field_b (CharField), field_x (CharField), deferred[field_b]>') '<ModelX: id \d+, field_b \(CharField\), field_x \(CharField\), deferred\[field_b\]>')
self.assertEqual(repr(objects_only[1]), self.assertRegex(repr(objects_only[1]),
'<ModelY: id 4, field_b (CharField), field_y (CharField), deferred[field_b]>') '<ModelY: id \d+, field_b \(CharField\), field_y \(CharField\), deferred\[field_b\]>')
def test_defer_related_fields(self): def test_defer_related_fields(self):
self.create_model2abcd() self.create_model2abcd()
@ -997,17 +997,3 @@ class PolymorphicTests(TransactionTestCase):
MultiTableDerived.objects.bulk_create([ MultiTableDerived.objects.bulk_create([
MultiTableDerived(field1='field1', field2='field2') 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 "<QuerySet [", not taking the actual type into account.
return '<{0} {1}'.format(data.__class__.__name__, repr(data)[10:])
else:
return repr(data)
return repr(data)

View File

@ -14,8 +14,8 @@ classifiers =
Development Status :: 5 - Production/Stable Development Status :: 5 - Production/Stable
Environment :: Web Environment Environment :: Web Environment
Framework :: Django Framework :: Django
Framework :: Django :: 1.10
Framework :: Django :: 1.11 Framework :: Django :: 1.11
Framework :: Django :: 2.0
Intended Audience :: Developers Intended Audience :: Developers
License :: OSI Approved :: BSD License License :: OSI Approved :: BSD License
Operating System :: OS Independent Operating System :: OS Independent
@ -30,7 +30,7 @@ classifiers =
packages = find: packages = find:
include_package_data = True include_package_data = True
install_requires = install_requires =
Django >= 1.10 Django >= 1.11
[options.packages.find] [options.packages.find]
exclude = exclude =

10
tox.ini
View File

@ -1,9 +1,9 @@
[tox] [tox]
envlist = envlist =
py27-django{110,111} py27-django{111}
py34-django{110,111} py34-django{111,200}
py35-django{110,111,master} py35-django{111,200,master}
py36-django{111,master} py36-django{111,200,master}
docs docs
[testenv] [testenv]
@ -14,8 +14,8 @@ setenv =
deps = deps =
coverage coverage
dj-database-url dj-database-url
django110: Django >= 1.10, < 1.11
django111: Django >= 1.11, < 2.0 django111: Django >= 1.11, < 2.0
django200: Django ~= 2.0.0
djangomaster: https://github.com/django/django/archive/master.tar.gz djangomaster: https://github.com/django/django/archive/master.tar.gz
postgres: psycopg2 postgres: psycopg2
commands = commands =