diff --git a/polymorphic/admin/childadmin.py b/polymorphic/admin/childadmin.py index 2695d1a..ec5a8ba 100644 --- a/polymorphic/admin/childadmin.py +++ b/polymorphic/admin/childadmin.py @@ -5,7 +5,6 @@ import inspect from django.contrib import admin from django.urls import resolve -from django.utils import six from django.utils.translation import ugettext_lazy as _ from polymorphic.utils import get_base_polymorphic_model @@ -216,7 +215,7 @@ class PolymorphicChildModelAdmin(admin.ModelAdmin): # By not declaring the fields/form in the base class, # get_form() will populate the form with all available fields. form = self.get_form(request, obj, exclude=exclude) - subclass_fields = list(six.iterkeys(form.base_fields)) + list(self.get_readonly_fields(request, obj)) + subclass_fields = list(form.base_fields.keys()) + list(self.get_readonly_fields(request, obj)) # Find which fields are not part of the common fields. for fieldset in self.get_base_fieldsets(request, obj): diff --git a/polymorphic/compat.py b/polymorphic/compat.py new file mode 100644 index 0000000..ede0d5e --- /dev/null +++ b/polymorphic/compat.py @@ -0,0 +1,44 @@ +"""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): + def __new__(cls, name, this_bases, d): + 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 4c1a8fb..863e0a7 100644 --- a/polymorphic/managers.py +++ b/polymorphic/managers.py @@ -5,7 +5,7 @@ The manager class for use in the models. from __future__ import unicode_literals from django.db import models -from django.utils.six import python_2_unicode_compatible +from polymorphic.compat import python_2_unicode_compatible from polymorphic.query import PolymorphicQuerySet diff --git a/polymorphic/models.py b/polymorphic/models.py index 40f9ce3..a0bac80 100644 --- a/polymorphic/models.py +++ b/polymorphic/models.py @@ -8,8 +8,8 @@ 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 +from polymorphic.compat import with_metaclass from .base import PolymorphicModelBase from .managers import PolymorphicManager from .query_translate import translate_polymorphic_Q_object @@ -26,7 +26,7 @@ class PolymorphicTypeInvalid(RuntimeError): pass -class PolymorphicModel(six.with_metaclass(PolymorphicModelBase, models.Model)): +class PolymorphicModel(with_metaclass(PolymorphicModelBase, models.Model)): """ Abstract base class that provides polymorphic behaviour for any model directly or indirectly derived from it. diff --git a/polymorphic/query.py b/polymorphic/query.py index 795f95f..75d28b3 100644 --- a/polymorphic/query.py +++ b/polymorphic/query.py @@ -10,8 +10,8 @@ from collections import defaultdict from django.contrib.contenttypes.models import ContentType from django.db.models import FieldDoesNotExist from django.db.models.query import ModelIterable, Q, QuerySet -from django.utils import six +from . import compat from .query_translate import translate_polymorphic_filter_definitions_in_kwargs, translate_polymorphic_filter_definitions_in_args from .query_translate import translate_polymorphic_field_path, translate_polymorphic_Q_object @@ -162,7 +162,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, six.string_types) else a # allow expressions to pass unchanged + if isinstance(a, compat.string_types) else a # allow expressions to pass unchanged for a in field_names ] return super(PolymorphicQuerySet, self).order_by(*field_names) @@ -267,7 +267,7 @@ class PolymorphicQuerySet(QuerySet): for a in args: test___lookup(a) - for a in six.itervalues(kwargs): + for a in kwargs.values(): patch_lookup(a) def annotate(self, *args, **kwargs): @@ -430,12 +430,12 @@ class PolymorphicQuerySet(QuerySet): real_object = transmogrify(real_class, real_object) if self.query.annotations: - for anno_field_name in six.iterkeys(self.query.annotations): + for anno_field_name in self.query.annotations.keys(): attr = getattr(base_object, anno_field_name) setattr(real_object, anno_field_name, attr) if self.query.extra_select: - for select_field_name in six.iterkeys(self.query.extra_select): + for select_field_name in self.query.extra_select.keys(): attr = getattr(base_object, select_field_name) setattr(real_object, select_field_name, attr) @@ -445,13 +445,13 @@ class PolymorphicQuerySet(QuerySet): # set polymorphic_annotate_names in all objects (currently just used for debugging/printing) if self.query.annotations: - annotate_names = list(six.iterkeys(self.query.annotations)) # get annotate field list + annotate_names = list(self.query.annotations.keys()) # get annotate field list for real_object in resultlist: real_object.polymorphic_annotate_names = annotate_names # set polymorphic_extra_select_names in all objects (currently just used for debugging/printing) if self.query.extra_select: - extra_select_names = list(six.iterkeys(self.query.extra_select)) # get extra select field list + extra_select_names = list(self.query.extra_select.keys()) # get extra select field list for real_object in resultlist: real_object.polymorphic_extra_select_names = extra_select_names diff --git a/polymorphic/query_translate.py b/polymorphic/query_translate.py index 51646b6..e486eeb 100644 --- a/polymorphic/query_translate.py +++ b/polymorphic/query_translate.py @@ -14,7 +14,6 @@ from django.db import models from django.db.models import Q from django.db.models.fields.related import ForeignObjectRel, RelatedField from django.db.utils import DEFAULT_DB_ALIAS -from django.utils import six ################################################################################### @@ -24,6 +23,8 @@ from django.utils import six # They form a kind of small framework for easily adding more # functionality to filters and Q objects. # Probably a more general queryset enhancement class could be made out of them. +from polymorphic import compat + def translate_polymorphic_filter_definitions_in_kwargs(queryset_model, kwargs, using=DEFAULT_DB_ALIAS): """ @@ -129,7 +130,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, six.string_types): + if not isinstance(field_path, compat.string_types): 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 5e7b079..0b193c4 100644 --- a/polymorphic/showfields.py +++ b/polymorphic/showfields.py @@ -2,8 +2,9 @@ import re from django.db import models -from django.utils import six -from django.utils.six import python_2_unicode_compatible + +from . import compat +from .compat import python_2_unicode_compatible RE_DEFERRED = re.compile('_Deferred_.*') @@ -41,7 +42,7 @@ class ShowFieldBase(object): out += content.__class__.__name__ elif issubclass(field_type, models.ManyToManyField): out += '%d' % content.count() - elif isinstance(content, six.integer_types): + elif isinstance(content, compat.integer_types): 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 b51793a..d0afb5f 100644 --- a/polymorphic/templatetags/polymorphic_admin_tags.py +++ b/polymorphic/templatetags/polymorphic_admin_tags.py @@ -1,5 +1,5 @@ from django.template import Library, Node, TemplateSyntaxError -from django.utils import six +from polymorphic import compat register = Library() @@ -32,7 +32,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, six.string_types): + if base_opts and not isinstance(base_opts, compat.string_types): new_vars = { 'app_label': base_opts.app_label, # What this is all about 'opts': base_opts, diff --git a/polymorphic/tests/models.py b/polymorphic/tests/models.py index a77f684..f866502 100644 --- a/polymorphic/tests/models.py +++ b/polymorphic/tests/models.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- - +import django import uuid from django.contrib.contenttypes.models import ContentType @@ -201,8 +201,9 @@ class MROBase3(models.Model): class MRODerived(MROBase2, MROBase3): - class Meta: - manager_inheritance_from_future = True + if django.VERSION < (3, 0): + class Meta: + manager_inheritance_from_future = True class ParentModelWithManager(PolymorphicModel): @@ -477,4 +478,4 @@ class SubclassSelectorProxyModel(SubclassSelectorProxyBaseModel): class SubclassSelectorProxyConcreteModel(SubclassSelectorProxyModel): - concrete_field = models.CharField(max_length=10, default='test_cf') \ No newline at end of file + concrete_field = models.CharField(max_length=10, default='test_cf') diff --git a/polymorphic/tests/test_orm.py b/polymorphic/tests/test_orm.py index fecdcd7..f0ee4fc 100644 --- a/polymorphic/tests/test_orm.py +++ b/polymorphic/tests/test_orm.py @@ -5,9 +5,8 @@ from django.contrib.contenttypes.models import ContentType from django.db import models from django.db.models import Case, Count, Q, When from django.test import TransactionTestCase -from django.utils import six -from polymorphic import query_translate +from polymorphic import compat, query_translate from polymorphic.managers import PolymorphicManager from polymorphic.models import PolymorphicTypeInvalid, PolymorphicTypeUndefined from polymorphic.tests.models import ( @@ -566,7 +565,7 @@ class PolymorphicTests(TransactionTestCase): select={"topic": "tests_modelextraexternal.topic"}, where=["tests_modelextraa.id = tests_modelextraexternal.id"], ) - if six.PY3: + if compat.PY3: self.assertEqual(repr(objects[0]), '') self.assertEqual(repr(objects[1]), '') self.assertEqual(repr(objects[2]), '')