diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 0a95d6a90d..563d973e21 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -14,6 +14,7 @@ Changelog * Fix: Wagtail's middleware classes are now compatible with Django 1.10's new-style middleware (Karl Hobley) * Fix: The `Pages.can_create_at` method is now checked in the create page view (Mikalai Radchuk) + * Fix: Fixed regression on Django 1.10.1 causing Page subclasses to fail to use PageManager (Matt Westcott) 1.6 (15.08.2016) diff --git a/docs/releases/1.6.1.rst b/docs/releases/1.6.1.rst index 29d2673de4..f81b1b048e 100644 --- a/docs/releases/1.6.1.rst +++ b/docs/releases/1.6.1.rst @@ -15,3 +15,24 @@ Bug fixes * Wagtail's middleware classes are now compatible with Django 1.10's `new-style middleware `_ (Karl Hobley) * The :meth:`~wagtail.wagtailcore.models.Page.can_create_at` method is now checked in the create page view (Mikalai Radchuk) +* Fixed regression on Django 1.10.1 causing Page subclasses to fail to use PageManager (Matt Westcott) + + +Upgrade considerations +====================== + +Multi-level inheritance and custom managers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The inheritance rules for :ref:`custom_page_managers` have been updated to match Django's standard behaviour. In the vast majority of scenarios there will be no change. However, in the specific case where a page model with a custom ``objects`` manager is subclassed further, the subclass will be assigned a plain ``Manager`` instead of a ``PageManager``, and will now need to explicitly override this with a ``PageManager`` to function correctly: + +.. code-block:: python + + class EventPage(Page): + objects = EventManager() + + + class SpecialEventPage(EventPage): + # Previously SpecialEventPage.objects would be set to a PageManager automatically; + # this now needs to be set explicitly + objects = PageManager() diff --git a/docs/topics/pages.rst b/docs/topics/pages.rst index 7b72d3f3f8..eefe5cd483 100644 --- a/docs/topics/pages.rst +++ b/docs/topics/pages.rst @@ -440,6 +440,8 @@ This is because ``Page`` enforces ordering QuerySets by path. Instead, you must news_items = NewsItemPage.objects.live().order_by('-publication_date') +.. _custom_page_managers: + Custom Page managers -------------------- diff --git a/wagtail/wagtailcore/models.py b/wagtail/wagtailcore/models.py index 9143913902..2a42b4de6f 100644 --- a/wagtail/wagtailcore/models.py +++ b/wagtail/wagtailcore/models.py @@ -261,14 +261,6 @@ class PageBase(models.base.ModelBase): # don't proceed with all this page type registration stuff return - # Override the default `objects` attribute with a `PageManager`. - # Managers are not inherited by MTI child models, so `Page` subclasses - # will get a plain `Manager` instead of a `PageManager`. - # If the developer has set their own custom `Manager` subclass, do not - # clobber it. - if not cls._meta.abstract and type(cls.objects) is models.Manager: - PageManager().contribute_to_class(cls, 'objects') - if 'template' not in dct: # Define a default template path derived from the app name and model name cls.template = "%s/%s.html" % (cls._meta.app_label, camelcase_to_underscore(name)) @@ -289,8 +281,21 @@ class PageBase(models.base.ModelBase): PAGE_MODEL_CLASSES.append(cls) +class AbstractPage(MP_Node): + """ + Abstract superclass for Page. According to Django's inheritance rules, managers set on + abstract models are inherited by subclasses, but managers set on concrete models that are extended + via multi-table inheritance are not. We therefore need to attach PageManager to an abstract + superclass to ensure that it is retained by subclasses of Page. + """ + objects = PageManager() + + class Meta: + abstract = True + + @python_2_unicode_compatible -class Page(six.with_metaclass(PageBase, MP_Node, index.Indexed, ClusterableModel)): +class Page(six.with_metaclass(PageBase, AbstractPage, index.Indexed, ClusterableModel)): title = models.CharField( verbose_name=_('title'), max_length=255, @@ -391,8 +396,6 @@ class Page(six.with_metaclass(PageBase, MP_Node, index.Indexed, ClusterableModel # Do not allow plain Page instances to be created through the Wagtail admin is_creatable = False - objects = PageManager() - def __init__(self, *args, **kwargs): super(Page, self).__init__(*args, **kwargs) if not self.id and not self.content_type_id: