﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
27337	Convoluted MTI with abstract model mixin fails on PY2, works on PY3	Tim Heap	nobody	"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."	Bug	closed	Database layer (models, ORM)	1.10	Normal	invalid	python2, mti py2		Accepted	0	0	0	0	0	0
