#33313: Inheriting from multiple abstract models with same field causes name collision when overriding field is direct parent -------------------------------------+------------------------------------- Reporter: Ben Nace | Owner: nobody Type: Bug | Status: new Component: Database | Version: 3.2 layer (models, ORM) | Severity: Normal | Keywords: Triage Stage: | Has patch: 0 Unreviewed | Needs documentation: 0 | Needs tests: 0 Patch needs improvement: 0 | Easy pickings: 0 UI/UX: 0 | -------------------------------------+------------------------------------- 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) }}} -- Ticket URL: <https://code.djangoproject.com/ticket/33313> 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 django-updates+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/django-updates/056.7afd8fd4f992460198a083ec430dc520%40djangoproject.com.