﻿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
33313	Inheriting from multiple abstract models with same field causes name collision when overriding field is direct parent	Ben Nace	nobody	"Given the following example models:

{{{
class ModelActivation(models.Model):
    start_date = models.DateField(null=True, blank=True)
    end_date = models.DateField(null=True, blank=True)
    active = models.BooleanField()

    class Meta:
        abstract = True

class BaseData(ModelActivation):
    entity_state = models.CharField(max_length=100)

    class Meta:
        abstract = True

class RequiredStart(models.Model):
    start_date = models.DateField()

    class Meta:
        abstract = True

class RequiredEnd(models.Model):
    end_date = models.DateField()

    class Meta:
        abstract = True

class RequiredStartEnd(RequiredStart, RequiredEnd):
    class Meta:
        abstract = True
}}}
\\

Any of the following when the override for start_date is defined on a direct parent model, results in the error ""(models.E006) The field 'start_date' clashes with the field 'start_date' from model 'app.testmodel' (or 'app.testmodel2')""
{{{
class TestModel(RequiredStart, BaseData):
    pass

class TestModel2(RequiredStart, ModelActivation):
    pass
}}}
However, if the overriding field is pushed up to a grandparent model, rather than a direct parent, it works fine.
{{{
class TestModel3(RequiredStartEnd, BaseData):
    pass

class TestModel4(RequiredStartEnd, ModelActivation):
    pass
}}}

In my limited debugging, it appears to me that this is because of the way inherited_attributes is tracked in the __new__ method of the ModelBase model metaclass (in django.db.models.base.py). For a grandparent model, not being a direct parent, all items in the __dict__ will be added to inherited_attributes, which includes the fields:

{{{
            if base not in parents or not hasattr(base, '_meta'):
                # Things without _meta aren't functional models, so they're
                # uninteresting parents.
                inherited_attributes.update(base.__dict__)
                continue
}}}

However, when a field is inherited from a direct parent, it is not added to inherited_attributes, it is not added to field_names, and it does not appear in new_class.__dict__, so the field from the ancestor higher up in the mro is also added via

{{{
                for field in parent_fields:
                    if (field.name not in field_names and
                            field.name not in new_class.__dict__ and
                            field.name not in inherited_attributes):
                        new_field = copy.deepcopy(field)
                        new_class.add_to_class(field.name, new_field)
}}}

"	Bug	closed	Database layer (models, ORM)	3.2	Normal	fixed		Jarek Glowacki Carlton Gibson	Unreviewed	0	0	0	0	0	0
