#27337: Convoluted MTI with abstract model mixin fails on PY2, works on PY3
----------------------------------------------+--------------------------
     Reporter:  Tim Heap                      |      Owner:  nobody
         Type:  Bug                           |     Status:  new
    Component:  Database layer (models, ORM)  |    Version:  1.10
     Severity:  Normal                        |   Keywords:  python2, mti
 Triage Stage:  Unreviewed                    |  Has patch:  0
Easy pickings:  0                             |      UI/UX:  0
----------------------------------------------+--------------------------
 This is a rather convoluted situation, but with the set up below, PY2
 fails but PY3 works. For context, this is roughly the same model set up
 used in Wagtail:

 * A Page class with custom Metaclass,
 * Developers extend Page using MTI to make custom page types
 * Mixins can be used for adding common functionality from e.g. plugins.

 {{{#!python
 from django.db import models
 from django.utils import six
 from django.test import TestCase

 class ConvolutedMTIWithAbtractMixinTests(TestCase):
     def test_convoluted_mti(self):

         class PageBase(type(models.Model)):
             """
             Wagtail's metaclass does some things, but nothing related.
             This empty metaclass still spoils things for some reason?
             """

         class Page(six.with_metaclass(PageBase, models.Model)):
             """The base page model. Every page should inherit from
 this."""
             url = models.CharField(max_length=255)

             class Meta:
                 app_label = 'tests'

         class FancyMixin(models.Model):
             """A model mixin with extra functionality, including extra
 model fields."""
             fancy_field = models.CharField(max_length=10)

             class Meta:
                 abstract = True

         class MyPage(FancyMixin, Page):
             """My custom page type. Extends Page using MTI, and mixes in
 FancyMixin"""
             content = models.CharField(max_length=255)

             class Meta:
                 app_label = 'tests'

         # The page_ptr from MyPage to Page is created.
         self.assertTrue(hasattr(MyPage, 'page_ptr'))

         # The other fields are all there
         self.assertTrue(MyPage._meta.get_field('url'))
         self.assertTrue(MyPage._meta.get_field('fancy_field'))
         self.assertTrue(MyPage._meta.get_field('content'))

         # The page_ptr field exists as a parent pointer to page.
         self.assertIn(Page, MyPage._meta.parents)
         self.assertIsInstance(MyPage._meta.parents[Page],
 models.OneToOneField)

         # But Django on py27 gets confused, and 'page_ptr' is not
 'registered'
         # (There is no nice _meta.has_field, so try: catch will do.)
         try:
             MyPage._meta.get_field('page_ptr')
         except FieldDoesNotExist:
             self.fail("MyPage.page_ptr field is missing!")
 }}}

 I stuck this test in `tests/model_inheritance/tests.py` when testing this
 out, but the tests can probably go anywhere. For some tests failing in the
 wild, this Travis CI build (https://travis-
 ci.org/takeflight/wagtail/builds/167013485) of some patches to Wagtail
 shows this happening on Django 1.8, 1.9 and 1.10 at least, but only on
 Python 2.7.

 I've poked around a little bit to see if I could work out what is going
 on, but I am not familiar enough with the model internals to make much
 headway.

--
Ticket URL: <https://code.djangoproject.com/ticket/27337>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/050.61257da253f07b79837013a87f34d933%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to