[Python-modules-commits] [django-polymorphic] 02/13: New upstream version 2.0

Michael Fladischer fladi at moszumanska.debian.org
Sun Feb 4 13:19:39 UTC 2018


This is an automated email from the git hooks/post-receive script.

fladi pushed a commit to branch debian/master
in repository django-polymorphic.

commit c040057e3ba7d054fd301941d57e1422c6acba63
Author: Michael Fladischer <FladischerMichael at fladi.at>
Date:   Sat Jan 27 13:51:57 2018 +0100

    New upstream version 2.0
---
 .travis.yml                                        |   76 +-
 README.rst                                         |    4 +-
 docs/admin.rst                                     |    8 +-
 docs/changelog.rst                                 |   25 +
 docs/conf.py                                       |    4 +-
 docs/managers.rst                                  |    5 +-
 docs/quickstart.rst                                |    4 +-
 docs/third-party.rst                               |    4 +-
 example/example/settings.py                        |   28 +-
 example/pexp/admin.py                              |   10 +-
 polymorphic/__init__.py                            |    9 +-
 polymorphic/admin/childadmin.py                    |   50 +-
 polymorphic/admin/generic.py                       |    2 +-
 polymorphic/admin/helpers.py                       |    7 +-
 polymorphic/admin/inlines.py                       |    5 +-
 polymorphic/admin/parentadmin.py                   |  128 +--
 polymorphic/base.py                                |  107 +-
 polymorphic/formsets/__init__.py                   |    2 +
 polymorphic/formsets/generic.py                    |    9 +-
 polymorphic/formsets/models.py                     |   61 +-
 polymorphic/formsets/utils.py                      |   12 +-
 polymorphic/locale/en/LC_MESSAGES/django.po        |    5 -
 polymorphic/manager.py                             |    2 -
 polymorphic/managers.py                            |   15 +-
 polymorphic/models.py                              |   35 +-
 polymorphic/query.py                               |   92 +-
 polymorphic/query_translate.py                     |   13 +-
 polymorphic/showfields.py                          |    6 -
 .../static/polymorphic/js/polymorphic_inlines.js   |    4 +
 .../templatetags/polymorphic_formset_tags.py       |    2 +-
 polymorphic/tests/__init__.py                      |  408 -------
 polymorphic/tests/admintestcase.py                 |  214 ++++
 polymorphic/tests/migrations/0001_initial.py       | 1133 ++++++++++++++++++++
 polymorphic/tests/migrations/__init__.py           |    0
 polymorphic/tests/{__init__.py => models.py}       |   84 +-
 polymorphic/tests/test_admin.py                    |  119 +-
 polymorphic/tests/test_contrib.py                  |   31 +
 polymorphic/tests/test_multidb.py                  |   76 +-
 polymorphic/tests/test_orm.py                      |  564 ++++++----
 polymorphic/tests/test_regression.py               |    2 +-
 polymorphic/tests/test_utils.py                    |   76 ++
 polymorphic/utils.py                               |   49 +-
 runtests.py                                        |   22 +-
 setup.cfg                                          |   45 +-
 setup.py                                           |   57 +-
 tox.ini                                            |   29 +-
 46 files changed, 2495 insertions(+), 1148 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index a6f737b..9a3175a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,45 +1,71 @@
+# https://travis-ci.org/django-polymorphic/django-polymorphic
 sudo: false
 language: python
-cache: pip
-python:
-  - "2.7"
-  - "3.4"
-  - "3.5"
-  - "3.6"
+
+python: "3.6"
+
 env:
-  - TOXENV="django18"
-  - TOXENV="django110"
-  - TOXENV="django111"
-  - TOXENV="djangodev"
+  - 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:
+  - postgres
 
 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="django18"
+      env: TOXENV=py27-django111
     - python: "3.6"
-      env: TOXENV="django110"
-
-    - python: "3.4"
-      env: TOXENV="django110"
-    - python: "3.4"
-      env: TOXENV="django111"
-
-    - python: "2.7"
-      env: TOXENV="djangodev"
-
+      env: TOXENV=py35-django111
+    - python: "3.6"
+      env: TOXENV=py35-django200
+    - python: "3.6"
+      env: TOXENV=py35-djangomaster
   allow_failures:
-    - env: TOXENV="djangodev"
+    - env: TOXENV=py35-djangomaster
+    - env: TOXENV=py36-djangomaster
+    - env: TOXENV=py36-djangomaster-postgres DB=postgres
+
+cache:
+  directories:
+    - $HOME/.cache/pip
+    - $TRAVIS_BUILD_DIR/.tox
 
 before_install:
-- pip install codecov coverage==3.6 tox
+- psql -c 'CREATE DATABASE default;' -U postgres || true
+- psql -c 'CREATE DATABASE secondary;' -U postgres || true
+
+install:
+  - pip install --upgrade pip wheel setuptools
+  - pip install codecov coverage tox
 
 script:
   - tox
 
 after_success:
-- coverage xml -i
-- codecov
+  - coverage xml -i
+  - codecov
 
 branches:
   only:
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 <https://django-polymorphic.readthedocs.io/>`_.
 
diff --git a/docs/admin.rst b/docs/admin.rst
index 39fc68f..caea29b 100644
--- a/docs/admin.rst
+++ b/docs/admin.rst
@@ -63,7 +63,7 @@ The models are taken from :ref:`advanced-features`.
 
     class ModelAChildAdmin(PolymorphicChildModelAdmin):
         """ Base admin class for all child models """
-        base_model = ModelA
+        base_model = ModelA  # Optional, explicitly set here.
 
         # By using these `base_...` attributes instead of the regular ModelAdmin `form` and `fieldsets`,
         # the additional fields of the child models are automatically added to the admin form.
@@ -75,13 +75,13 @@ The models are taken from :ref:`advanced-features`.
 
     @admin.register(ModelB)
     class ModelBAdmin(ModelAChildAdmin):
-        base_model = ModelB
+        base_model = ModelB  # Explicitly set here!
         # define custom features here
 
 
     @admin.register(ModelC)
     class ModelCAdmin(ModelBAdmin):
-        base_model = ModelC
+        base_model = ModelC  # Explicitly set here!
         show_in_index = True  # makes child model admin visible in main admin site
         # define custom features here
 
@@ -89,7 +89,7 @@ The models are taken from :ref:`advanced-features`.
     @admin.register(ModelA)
     class ModelAParentAdmin(PolymorphicParentModelAdmin):
         """ The parent model admin """
-        base_model = ModelA
+        base_model = ModelA  # Optional, explicitly set here.
         child_models = (ModelB, ModelC)
         list_filter = (PolymorphicChildModelFilter,)  # This is optional.
 
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 0236d66..f843587 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -1,6 +1,31 @@
 Changelog
 =========
 
+Changes in 2.0 (2018-01-22)
+---------------------------
+
+* **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``.
+ * Register child models to the admin as well using ``@admin.register()`` or ``admin.site.register()``,
+   as this is no longer done automatically.
+
+* Added 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.
+  The ``polymorphic.utils.reset_polymorphic_ctype`` function can be used for that.
+* Added ``PolymorphicTypeInvalid`` exception when database was incorrectly imported.
+* Added ``polymorphic.utils.get_base_polymorphic_model()`` to find the base model for types.
+* Using ``base_model`` on the polymorphic admins is no longer required, as this can be autodetected.
+* Fixed manager errors for swappable models.
+* Fixed ``deleteText`` of ``|as_script_options`` template filter.
+* Fixed ``.filter(applabel__ModelName___field=...)`` lookups.
+* Improved ``polymorphic.utils.reset_polymorphic_ctype()`` to accept models in random ordering.
+* Fix fieldsets handling in the admin (``declared_fieldsets`` is removed since Django 1.9)
+
+
 Version 1.3 (2017-08-01)
 ------------------------
 
diff --git a/docs/conf.py b/docs/conf.py
index b9b053f..20da1fb 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -61,9 +61,9 @@ copyright = u'2013, Bert Constantin, Chris Glass, Diederik van der Boor'
 # built documents.
 #
 # The short X.Y version.
-version = '1.3'
+version = '2.0'
 # The full version, including alpha/beta/rc tags.
-release = '1.3'
+release = '2.0'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff --git a/docs/managers.rst b/docs/managers.rst
index 8790593..f0a0163 100644
--- a/docs/managers.rst
+++ b/docs/managers.rst
@@ -65,7 +65,6 @@ ArtProject inherited the managers ``objects`` and ``objects_ordered`` from Proje
 ``ArtProject.objects_ordered.all()`` will return all art projects ordered
 regarding their start time and ``ArtProject.objects_ordered.most_recent()``
 will return the ten most recent art projects.
-.
 
 Using a Custom Queryset Class
 -----------------------------
@@ -80,9 +79,9 @@ instead of Django's QuerySet as the base class::
         from polymorphic.query import PolymorphicQuerySet
 
         class MyQuerySet(PolymorphicQuerySet):
-            def my_queryset_method(...):
+            def my_queryset_method(self):
                 ...
 
         class MyModel(PolymorphicModel):
-            my_objects=PolymorphicManager(MyQuerySet)
+            my_objects = PolymorphicManager.from_queryset(MyQuerySet)
             ...
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
 ------------------------------
diff --git a/docs/third-party.rst b/docs/third-party.rst
index f1d412a..50105f9 100644
--- a/docs/third-party.rst
+++ b/docs/third-party.rst
@@ -69,7 +69,7 @@ The admin :ref:`admin example <admin-example>` becomes:
 
 
     class ModelAChildAdmin(PolymorphicChildModelAdmin, VersionAdmin):
-        base_model = ModelA
+        base_model = ModelA  # optional, explicitly set here.
         base_form = ...
         base_fieldsets = (
             ...
@@ -83,7 +83,7 @@ The admin :ref:`admin example <admin-example>` becomes:
 
 
     class ModelAParentAdmin(VersionAdmin, PolymorphicParentModelAdmin):
-        base_model = ModelA
+        base_model = ModelA  # optional, explicitly set here.
         child_models = (
             (ModelB, ModelBAdmin),
             (ModelC, ModelCAdmin),
diff --git a/example/example/settings.py b/example/example/settings.py
index abb389b..1514227 100644
--- a/example/example/settings.py
+++ b/example/example/settings.py
@@ -1,7 +1,6 @@
 import os
 
 DEBUG = True
-TEMPLATE_DEBUG = DEBUG
 
 ADMINS = (
     # ('Your Name', 'your_email at example.com'),
@@ -42,11 +41,6 @@ STATICFILES_FINDERS = (
     'django.contrib.staticfiles.finders.AppDirectoriesFinder',
 )
 
-TEMPLATE_LOADERS = (
-    'django.template.loaders.filesystem.Loader',
-    'django.template.loaders.app_directories.Loader',
-)
-
 MIDDLEWARE_CLASSES = (
     'django.middleware.common.CommonMiddleware',
     'django.contrib.sessions.middleware.SessionMiddleware',
@@ -55,12 +49,30 @@ MIDDLEWARE_CLASSES = (
     'django.contrib.messages.middleware.MessageMiddleware',
 )
 
+TEMPLATES=[{
+    "BACKEND": "django.template.backends.django.DjangoTemplates",
+    "DIRS": (),
+    "OPTIONS": {
+        "loaders": (
+            "django.template.loaders.filesystem.Loader",
+            "django.template.loaders.app_directories.Loader",
+        ),
+        "context_processors": (
+            "django.template.context_processors.debug",
+            "django.template.context_processors.i18n",
+            "django.template.context_processors.media",
+            "django.template.context_processors.request",
+            "django.template.context_processors.static",
+            "django.contrib.messages.context_processors.messages",
+            "django.contrib.auth.context_processors.auth",
+        ),
+    },
+}]
+
 ROOT_URLCONF = 'example.urls'
 
 WSGI_APPLICATION = 'example.wsgi.application'
 
-TEMPLATE_DIRS = ()
-
 INSTALLED_APPS = (
     'django.contrib.auth',
     'django.contrib.admin',
diff --git a/example/pexp/admin.py b/example/pexp/admin.py
index d359c31..a04ec8b 100644
--- a/example/pexp/admin.py
+++ b/example/pexp/admin.py
@@ -4,13 +4,13 @@ from pexp.models import *
 
 
 class ProjectAdmin(PolymorphicParentModelAdmin):
-    base_model = Project
+    base_model = Project  # Can be set explicitly.
     list_filter = (PolymorphicChildModelFilter,)
     child_models = (Project, ArtProject, ResearchProject)
 
 
 class ProjectChildAdmin(PolymorphicChildModelAdmin):
-    base_model = Project
+    base_model = Project  # Can be set explicitly.
 
     # On purpose, only have the shared fields here.
     # The fields of the derived model should still be displayed.
@@ -27,13 +27,12 @@ admin.site.register(ResearchProject, ProjectChildAdmin)
 
 
 class UUIDModelAAdmin(PolymorphicParentModelAdmin):
-    base_model = UUIDModelA
     list_filter = (PolymorphicChildModelFilter,)
     child_models = (UUIDModelA, UUIDModelB)
 
 
 class UUIDModelAChildAdmin(PolymorphicChildModelAdmin):
-    base_model = UUIDModelA
+    pass
 
 
 admin.site.register(UUIDModelA, UUIDModelAAdmin)
@@ -42,13 +41,12 @@ admin.site.register(UUIDModelC, UUIDModelAChildAdmin)
 
 
 class ProxyAdmin(PolymorphicParentModelAdmin):
-    base_model = ProxyBase
     list_filter = (PolymorphicChildModelFilter,)
     child_models = (ProxyA, ProxyB)
 
 
 class ProxyChildAdmin(PolymorphicChildModelAdmin):
-    base_model = ProxyBase
+    pass
 
 
 admin.site.register(ProxyBase, ProxyAdmin)
diff --git a/polymorphic/__init__.py b/polymorphic/__init__.py
index 77d7fe2..07ee3fa 100644
--- a/polymorphic/__init__.py
+++ b/polymorphic/__init__.py
@@ -7,5 +7,10 @@ This code and affiliated files are (C) by Bert Constantin and individual contrib
 Please see LICENSE and AUTHORS for more information.
 """
 
-# See PEP 440 (https://www.python.org/dev/peps/pep-0440/)
-__version__ = "1.3"
+import pkg_resources
+
+try:
+    __version__ = pkg_resources.require("django-polymorphic")[0].version
+except pkg_resources.DistributionNotFound:
+    __version__ = None  # for RTD among others
+
diff --git a/polymorphic/admin/childadmin.py b/polymorphic/admin/childadmin.py
index e492fed..2695d1a 100644
--- a/polymorphic/admin/childadmin.py
+++ b/polymorphic/admin/childadmin.py
@@ -4,10 +4,11 @@ The child admin displays the change/delete view of the subclass model.
 import inspect
 
 from django.contrib import admin
-from django.core.urlresolvers import resolve
+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
 from ..admin import PolymorphicParentModelAdmin
 
 
@@ -25,15 +26,32 @@ class PolymorphicChildModelAdmin(admin.ModelAdmin):
     * It adds the base model to the template lookup paths.
     * It allows to set ``base_form`` so the derived class will automatically include other fields in the form.
     * It allows to set ``base_fieldsets`` so the derived class will automatically display any extra fields.
-
-    The ``base_model`` attribute must be set.
     """
+
+    #: The base model that the class uses (auto-detected if not set explicitly)
     base_model = None
+
+    #: By setting ``base_form`` instead of ``form``, any subclass fields are automatically added to the form.
+    #: This is useful when your model admin class is inherited by others.
     base_form = None
+
+    #: By setting ``base_fieldsets`` instead of ``fieldsets``,
+    #: any subclass fields can be automatically added.
+    #: This is useful when your model admin class is inherited by others.
     base_fieldsets = None
-    extra_fieldset_title = _("Contents")  # Default title for extra fieldset
+
+    #: Default title for extra fieldset
+    extra_fieldset_title = _("Contents")
+
+    #: Whether the child admin model should be visible in the admin index page.
     show_in_index = False
 
+    def __init__(self, model, admin_site, *args, **kwargs):
+        super(PolymorphicChildModelAdmin, self).__init__(model, admin_site, *args, **kwargs)
+
+        if self.base_model is None:
+            self.base_model = get_base_polymorphic_model(model)
+
     def get_form(self, request, obj=None, **kwargs):
         # The django admin validation requires the form to have a 'class Meta: model = ..'
         # attribute, or it will complain that the fields are missing.
@@ -44,9 +62,9 @@ class PolymorphicChildModelAdmin(admin.ModelAdmin):
         # If the derived class sets the model explicitly, respect that setting.
         kwargs.setdefault('form', self.base_form or self.form)
 
-        # prevent infinite recursion in django 1.6+
-        if not getattr(self, 'declared_fieldsets', None):
-            kwargs.setdefault('fields', None)
+        # prevent infinite recursion when this is called from get_subclass_fields
+        if not self.fieldsets and not self.fields:
+            kwargs.setdefault('fields', '__all__')
 
         return super(PolymorphicChildModelAdmin, self).get_form(request, obj, **kwargs)
 
@@ -167,10 +185,14 @@ class PolymorphicChildModelAdmin(admin.ModelAdmin):
 
     # ---- Extra: improving the form/fieldset default display ----
 
+    def get_base_fieldsets(self, request, obj=None):
+        return self.base_fieldsets
+
     def get_fieldsets(self, request, obj=None):
-        # If subclass declares fieldsets, this is respected
-        if (hasattr(self, 'declared_fieldset') and self.declared_fieldsets) \
-           or not self.base_fieldsets:
+        base_fieldsets = self.get_base_fieldsets(request, obj)
+
+        # If subclass declares fieldsets or fields, this is respected
+        if self.fieldsets or self.fields or not self.base_fieldsets:
             return super(PolymorphicChildModelAdmin, self).get_fieldsets(request, obj)
 
         # Have a reasonable default fieldsets,
@@ -179,11 +201,11 @@ class PolymorphicChildModelAdmin(admin.ModelAdmin):
 
         if other_fields:
             return (
-                self.base_fieldsets[0],
+                base_fieldsets[0],
                 (self.extra_fieldset_title, {'fields': other_fields}),
-            ) + self.base_fieldsets[1:]
+            ) + base_fieldsets[1:]
         else:
-            return self.base_fieldsets
+            return base_fieldsets
 
     def get_subclass_fields(self, request, obj=None):
         # Find out how many fields would really be on the form,
@@ -197,7 +219,7 @@ class PolymorphicChildModelAdmin(admin.ModelAdmin):
         subclass_fields = list(six.iterkeys(form.base_fields)) + list(self.get_readonly_fields(request, obj))
 
         # Find which fields are not part of the common fields.
-        for fieldset in self.base_fieldsets:
+        for fieldset in self.get_base_fieldsets(request, obj):
             for field in fieldset[1]['fields']:
                 try:
                     subclass_fields.remove(field)
diff --git a/polymorphic/admin/generic.py b/polymorphic/admin/generic.py
index bd3453f..91fc082 100644
--- a/polymorphic/admin/generic.py
+++ b/polymorphic/admin/generic.py
@@ -42,7 +42,7 @@ class GenericPolymorphicInlineModelAdmin(PolymorphicInlineModelAdmin, GenericInl
             Expose the ContentType that the child relates to.
             This can be used for the ``polymorphic_ctype`` field.
             """
-            return ContentType.objects.get_for_model(self.model)
+            return ContentType.objects.get_for_model(self.model, for_concrete_model=False)
 
         def get_formset_child(self, request, obj=None, **kwargs):
             # Similar to GenericInlineModelAdmin.get_formset(),
diff --git a/polymorphic/admin/helpers.py b/polymorphic/admin/helpers.py
index d7fdef6..f935ed1 100644
--- a/polymorphic/admin/helpers.py
+++ b/polymorphic/admin/helpers.py
@@ -42,7 +42,7 @@ class PolymorphicInlineAdminFormSet(InlineAdminFormSet):
         """
         for form, original in zip(self.formset.initial_forms, self.formset.get_queryset()):
             # Output the form
-            model = original.get_real_concrete_instance_class()
+            model = original.get_real_instance_class()
             child_inline = self.opts.get_child_inline_instance(model)
             view_on_site_url = self.opts.get_view_on_site_url(original)
 
@@ -82,11 +82,10 @@ class PolymorphicInlineAdminFormSet(InlineAdminFormSet):
         fields.update(child_inline.get_prepopulated_fields(self.request, self.obj))
         return fields
 
-    # The polymorphic template follows the same method like all other inlines do in Django 1.10.
-    # This method is added for compatibility with older Django versions.
     def inline_formset_data(self):
         """
         A JavaScript data structure for the JavaScript code
+        This overrides the default Django version to add the ``childTypes`` data.
         """
         verbose_name = self.opts.verbose_name
         return json.dumps({
@@ -120,7 +119,7 @@ class PolymorphicInlineSupportMixin(object):
     :class:`~django.contrib.admin.helpers.InlineAdminFormSet` for the polymorphic formsets.
     """
 
-    def get_inline_formsets(self, request, formsets, inline_instances, obj=None):
+    def get_inline_formsets(self, request, formsets, inline_instances, obj=None, *args, **kwargs):
         """
         Overwritten version to produce the proper admin wrapping for the
         polymorphic inline formset. This fixes the media and form appearance
diff --git a/polymorphic/admin/inlines.py b/polymorphic/admin/inlines.py
index deee7bb..bb25350 100644
--- a/polymorphic/admin/inlines.py
+++ b/polymorphic/admin/inlines.py
@@ -10,7 +10,8 @@ from django.contrib.admin.utils import flatten_fieldsets
 from django.core.exceptions import ImproperlyConfigured
 from django.forms import Media
 
-from polymorphic.formsets import polymorphic_child_forms_factory, BasePolymorphicInlineFormSet, PolymorphicFormSetChild
+from polymorphic.formsets import polymorphic_child_forms_factory, BasePolymorphicInlineFormSet, \
+    PolymorphicFormSetChild, UnsupportedChildType
 from polymorphic.formsets.utils import add_media
 from .helpers import PolymorphicInlineSupportMixin
 
@@ -89,7 +90,7 @@ class PolymorphicInlineModelAdmin(InlineModelAdmin):
         try:
             return self._child_inlines_lookup[model]
         except KeyError:
-            raise ValueError("Model '{0}' not found in child_inlines".format(model.__name__))
+            raise UnsupportedChildType("Model '{0}' not found in child_inlines".format(model.__name__))
 
     def get_formset(self, request, obj=None, **kwargs):
         """
diff --git a/polymorphic/admin/parentadmin.py b/polymorphic/admin/parentadmin.py
index da7dffc..32fb3a9 100644
--- a/polymorphic/admin/parentadmin.py
+++ b/polymorphic/admin/parentadmin.py
@@ -2,26 +2,31 @@
 The parent admin displays the list view of the base model.
 """
 import sys
-import warnings
 
-import django
-from django.conf.urls import url
 from django.contrib import admin
 from django.contrib.admin.helpers import AdminErrorList, AdminForm
 from django.contrib.admin.templatetags.admin_urls import add_preserved_filters
 from django.contrib.contenttypes.models import ContentType
-from django.core.exceptions import PermissionDenied
-from django.core.urlresolvers import RegexURLResolver
+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.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
 
@@ -37,21 +42,20 @@ class ChildAdminNotRegistered(RuntimeError):
 class PolymorphicParentModelAdmin(admin.ModelAdmin):
     """
     A admin interface that can displays different change/delete pages, depending on the polymorphic model.
-    To use this class, two variables need to be defined:
+    To use this class, one attribute need to be defined:
 
-    * :attr:`base_model` should
-    * :attr:`child_models` should be a list of (Model, Admin) tuples
+    * :attr:`child_models` should be a list models.
 
     Alternatively, the following methods can be implemented:
 
-    * :func:`get_child_models` should return a list of (Model, ModelAdmin) tuples
+    * :func:`get_child_models` should return a list of models.
     * optionally, :func:`get_child_type_choices` can be overwritten to refine the choices for the add dialog.
 
     This class needs to be inherited by the model admin base class that is registered in the site.
     The derived models should *not* register the ModelAdmin, but instead it should be returned by :func:`get_child_models`.
     """
 
-    #: The base model that the class uses
+    #: The base model that the class uses (auto-detected if not set explicitly)
     base_model = None
 
     #: The child models that should be displayed
@@ -72,42 +76,26 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin):
         super(PolymorphicParentModelAdmin, self).__init__(model, admin_site, *args, **kwargs)
         self._is_setup = False
 
+        if self.base_model is None:
+            self.base_model = get_base_polymorphic_model(model)
+
     def _lazy_setup(self):
         if self._is_setup:
             return
 
-        # By not having this in __init__() there is less stress on import dependencies as well,
-        # considering an advanced use cases where a plugin system scans for the child models.
-        child_models = self.get_child_models()
-        # Check if get_child_models() returns an iterable of models (new format) or an iterable
-        # of (Model, Admin) (legacy format). When iterable is empty, assume the new format.
-        self._compat_mode = len(child_models) and isinstance(child_models[0], (list, tuple))
-        if not self._compat_mode:
-            self._child_models = child_models
-            self._child_admin_site = self.admin_site
-            self._is_setup = True
-            return
+        self._child_models = self.get_child_models()
+
+        # Make absolutely sure that the child models don't use the old 0.9 format,
+        # as of polymorphic 1.4 this deprecated configuration is no longer supported.
+        # Instead, register the child models in the admin too.
+        if self._child_models and not issubclass(self._child_models[0], models.Model):
+            raise ImproperlyConfigured(
+                "Since django-polymorphic 1.4, the `child_models` attribute "
+                "and `get_child_models()` method should be a list of models only.\n"
+                "The model-admin class should be registered in the regular Django admin."
+            )
 
-        # Continue only if in compatibility mode
-        warnings.warn("Using tuples of (Model, ModelAdmin) in PolymorphicParentModelAdmin.child_models is "
-                      "deprecated; instead child_models should be iterable of child models eg. "
-                      "(Model1, Model2, ..) and child admins should be registered to default admin site",
-                      DeprecationWarning)
-        self._child_admin_site = self.admin_site.__class__(name=self.admin_site.name)
-        self._child_admin_site.get_app_list = lambda request: ()  # HACK: workaround for Django 1.9
-
-        for Model, Admin in child_models:
-            self.register_child(Model, Admin)
-        self._child_models = dict(child_models)
-
-        # This is needed to deal with the improved ForeignKeyRawIdWidget in Django 1.4 and perhaps other widgets too.
-        # The ForeignKeyRawIdWidget checks whether the referenced model is registered in the admin, otherwise it displays itself as a textfield.
-        # As simple solution, just make sure all parent admin models are also know in the child admin site.
-        # This should be done after all parent models are registered off course.
-        complete_registry = self.admin_site._registry.copy()
-        complete_registry.update(self._child_admin_site._registry)
-
-        self._child_admin_site._registry = complete_registry
+        self._child_admin_site = self.admin_site
         self._is_setup = True
 
     def register_child(self, model, model_admin):
@@ -145,11 +133,7 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin):
         """
         self._lazy_setup()
         choices = []
-        for child_model_desc in self.get_child_models():
-            if self._compat_mode:
-                model = child_model_desc[0]
-            else:
-                model = child_model_desc
+        for model in self.get_child_models():
             perm_function_name = 'has_{0}_permission'.format(action)
             model_admin = self._get_real_admin_by_model(model)
             perm_function = getattr(model_admin, perm_function_name)
@@ -266,49 +250,7 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin):
         # At this point. all admin code needs to be known.
         self._lazy_setup()
 
-        # Continue only if in compatibility mode
-        if not self._compat_mode:
-            return urls
-
-        info = _get_opt(self.model)
-
-        # Patch the change view URL so it's not a big catch-all; allowing all
-        # custom URLs to be added to the end. This is done by adding '/$' to the
-        # end of the regex.  The url needs to be recreated, patching url.regex
-        # is not an option Django 1.4's LocaleRegexProvider changed it.
-        if django.VERSION < (1, 9):
-            # On Django 1.9, the change view URL has been changed from
-            # /<app>/<model>/<pk>/ to /<app>/<model>/<pk>/change/, which is
-            # why we can skip this workaround for Django >= 1.9.
-            new_change_url = url(
-                r'^{0}/$'.format(self.pk_regex),
-                self.admin_site.admin_view(self.change_view),
-                name='{0}_{1}_change'.format(*info)
-            )
-
-            redirect_urls = []
-            for i, oldurl in enumerate(urls):
-                if oldurl.name == new_change_url.name:
-                    urls[i] = new_change_url
-        else:
-            # For Django 1.9, the redirect at the end acts as catch all.
-            # The custom urls need to be inserted before that.
-            redirect_urls = [pat for pat in urls if not pat.name]  # redirect URL has no name.
-            urls = [pat for pat in urls if pat.name]
-
-        # Define the catch-all for custom views
-        custom_urls = [
-            url(r'^(?P<path>.+)$', self.admin_site.admin_view(self.subclass_view))
-        ]
-
-        # Add reverse names for all polymorphic models, so the delete button and "save and add" just work.
-        # These definitions are masked by the definition above, since it needs special handling (and a ct_id parameter).
-        dummy_urls = []
-        for model, _ in self.get_child_models():
-            admin = self._get_real_admin_by_model(model)
-            dummy_urls += admin.get_urls()
-
-        return urls + custom_urls + dummy_urls + redirect_urls
+        return urls
 
     def subclass_view(self, request, path):
         """
@@ -329,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))
@@ -421,7 +363,3 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin):
             "admin/%s/change_list.html" % base_app_label,
             "admin/change_list.html"
         ]
-
-
-def _get_opt(model):
-    return model._meta.app_label, model._meta.model_name
diff --git a/polymorphic/base.py b/polymorphic/base.py
index b6a5a74..cd6e225 100644
--- a/polymorphic/base.py
+++ b/polymorphic/base.py
@@ -4,11 +4,10 @@ PolymorphicModel Meta Class
 """
 from __future__ import absolute_import
 
+import inspect
 import os
 import sys
-import inspect
 
-import django
 from django.db import models
 from django.db.models.base import ModelBase
 from django.db.models.manager import ManagerDescriptor
@@ -22,11 +21,6 @@ POLYMORPHIC_SPECIAL_Q_KWORDS = ['instance_of', 'not_instance_of']
 
 DUMPDATA_COMMAND = os.path.join('django', 'core', 'management', 'commands', 'dumpdata.py')
 
-try:
-    from django.db.models.manager import AbstractManagerDescriptor  # Django 1.5
-except ImportError:
-    AbstractManagerDescriptor = None
-
 
 ###################################################################################
 # PolymorphicModel meta class
@@ -60,11 +54,6 @@ class PolymorphicModelBase(ModelBase):
 
         # Workaround compatibility issue with six.with_metaclass() and custom Django model metaclasses:
         if not attrs and model_name == 'NewBase':
-            if django.VERSION < (1, 5):
-                # Let Django fully ignore the class which is inserted in between.
-                # Django 1.5 fixed this, see https://code.djangoproject.com/ticket/19688
-                attrs['__module__'] = 'django.utils.six'
-                attrs['Meta'] = type('Meta', (), {'abstract': True})
             return super(PolymorphicModelBase, self).__new__(self, model_name, bases, attrs)
 
         # create new model
@@ -73,32 +62,9 @@ class PolymorphicModelBase(ModelBase):
         # check if the model fields are all allowed
         self.validate_model_fields(new_class)
 
-        # create list of all managers to be inherited from the base classes
-        if django.VERSION < (1, 10):
-            inherited_managers = new_class.get_inherited_managers(attrs)
-
-            # add the managers to the new model
-            for source_name, mgr_name, manager in inherited_managers:
-                # print '** add inherited manager from model %s, manager %s, %s' % (source_name, mgr_name, manager.__class__.__name__)
-                new_manager = manager._copy_to_model(new_class)
-                if mgr_name == '_default_manager':
-                    new_class._default_manager = new_manager
-                else:
-                    new_class.add_to_class(mgr_name, new_manager)
-
-            # get first user defined manager; if there is one, make it the _default_manager
-            # this value is used by the related objects, restoring access to custom queryset methods on related objects.
-            user_manager = self.get_first_user_defined_manager(new_class)
-            if user_manager:
-                # print '## add default manager', type(def_mgr)
-                new_class._default_manager = user_manager._copy_to_model(new_class)
-                new_class._default_manager._inherited = False   # the default mgr was defined by the user, not inherited
-
         # validate resulting default manager
-        if django.VERSION >= (1, 10) and not new_class._meta.abstract:
-            self.validate_model_manager(new_class.objects, model_name, 'objects')
-        else:
-            self.validate_model_manager(new_class._default_manager, model_name, '_default_manager')
+        if not new_class._meta.abstract and not new_class._meta.swapped:
+            self.validate_model_manager(new_class.objects, model_name, "objects")
 
         # for __init__ function of this class (monkeypatching inheritance accessors)
         new_class.polymorphic_super_sub_accessors_replaced = False
@@ -112,73 +78,6 @@ class PolymorphicModelBase(ModelBase):
 
         return new_class
 
-    if django.VERSION < (1, 10):
-        def get_inherited_managers(self, attrs):
-            """
-            Return list of all managers to be inherited/propagated from the base classes;
-            use correct mro, only use managers with _inherited==False (they are of no use),
-            skip managers that are overwritten by the user with same-named class attributes (in attrs)
-            """
-            # print "** ", self.__name__
-            add_managers = []
-            add_managers_keys = set()
-            for base in self.__mro__[1:]:
-                if not issubclass(base, models.Model):
-                    continue
-                if not getattr(base, 'polymorphic_model_marker', None):
-                    continue  # leave managers of non-polym. models alone
-
-                for key, manager in base.__dict__.items():
-                    if type(manager) == models.manager.ManagerDescriptor:
-                        manager = manager.manager
-
-                    # As of Django 1.5, the abstract models don't get any managers, only a
-                    # AbstractManagerDescriptor as substitute.
-                    if type(manager) == AbstractManagerDescriptor and base.__name__ == 'PolymorphicModel':
-                        model = manager.model
-                        if key == 'objects':
-                            manager = PolymorphicManager()
-                            manager.model = model
-                        elif key == 'base_objects':
-                            manager = models.Manager()
-                            manager.model = model
-
-                    if AbstractManagerDescriptor is not None:
-                        # Django 1.4 unconditionally assigned managers to a model. As of Django 1.5 however,
-                        # the abstract models don't get any managers, only a AbstractManagerDescriptor as substitute.
-                        # Pretend that the manager is still there, so all code works like it used to.
-                        if type(manager) == AbstractManagerDescriptor and base.__name__ == 'PolymorphicModel':
-                            model = manager.model
-                            if key == 'objects':
-                                manager = PolymorphicManager()
-                                manager.model = model
-                            elif key == 'base_objects':
-                                manager = models.Manager()
-                                manager.model = model
-
-                    if not isinstance(manager, models.Manager):
-                        continue
-                    if key == '_base_manager':
-                        continue       # let Django handle _base_manager
-                    if key in attrs:
-                        continue
-                    if key in add_managers_keys:
-                        continue       # manager with that name already added, skip
-                    if manager._inherited:
-                        continue             # inherited managers (on the bases) have no significance, they are just copies
-                    # print '## {0} {1}'.format(self.__name__, key)
-
-                    if isinstance(manager, PolymorphicManager):  # validate any inherited polymorphic managers
-                        self.validate_model_manager(manager, self.__name__, key)
-                    add_managers.append((base.__name__, key, manager))
-                    add_managers_keys.add(key)
-
-            # The ordering in the base.__dict__ may randomly change depending on which method is added.
-            # Make sure base_objects is on top, and 'objects' and '_default_manager' follow afterwards.
-            # This makes sure that the _base_manager is also assigned properly.
-            add_managers = sorted(add_managers, key=lambda item: (item[1].startswith('_'), item[1]))
-            return add_managers
-
         @classmethod
         def get_first_user_defined_manager(mcs, new_class):
             # See if there is a manager attribute directly stored at this inheritance level.
diff --git a/polymorphic/formsets/__init__.py b/polymorphic/formsets/__init__.py
index 02c47ae..1f3b335 100644
--- a/polymorphic/formsets/__init__.py
+++ b/polymorphic/formsets/__init__.py
@@ -12,6 +12,7 @@ from .models import (
     BasePolymorphicModelFormSet,
     BasePolymorphicInlineFormSet,
     PolymorphicFormSetChild,
+    UnsupportedChildType,
     polymorphic_modelformset_factory,
     polymorphic_inlineformset_factory,
     polymorphic_child_forms_factory,
@@ -27,6 +28,7 @@ __all__ = (
     'BasePolymorphicModelFormSet',
     'BasePolymorphicInlineFormSet',
     'PolymorphicFormSetChild',
+    'UnsupportedChildType',
     'polymorphic_modelformset_factory',
     'polymorphic_inlineformset_factory',
... 3924 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/django-polymorphic.git



More information about the Python-modules-commits mailing list