Hi! I was going to submit this as a ticket, but I glanced over the patch submission guidelines once more and this seems to fall under the category of a "non-trivial change" that would require a design decision. The code samples/tracebacks can be viewed nicely formatted on github. [1]
Assume the following models in `app`:
from django.db import models
class Orange(models.Model):
pass
class BaseClass(models.Model):
field = models.ForeignKey(Orange)
class Meta:
abstract = True
class Parent1(BaseClass):
class Meta:
abstract=True
class Parent2(BaseClass):
class Meta:
abstract=True
class Child(Parent1, Parent2):
pass
Currently, this definition will raise the following errors during model validation:
Unhandled exception in thread started by <bound method Command.inner_run of <django.core.management.commands.runserver.Command object at 0x101470490>>
Traceback (most recent call last):
File "..../django/core/management/commands/runserver.py", line 88, in inner_run
self.validate(display_num_errors=True)
File "..../django/core/management/base.py", line 253, in validate
raise CommandError("One or more models did not validate:\n%s" % error_text)
django.core.management.base.CommandError: One or more models did not validate:
app.child: Accessor for field 'field' clashes with related field 'Orange.child_set'. Add a related_name argument to the definition for 'field'.
app.child: Accessor for field 'field' clashes with related field 'Orange.child_set'. Add a related_name argument to the definition for 'field'.
Using the %(app_label)s_%(class)s_related syntax only makes things worse:
Unhandled exception in thread started by <bound method Command.inner_run of <django.core.management.commands.runserver.Command object at 0x10146e4d0>>
Traceback (most recent call last):
File "..../django/core/management/commands/runserver.py", line 88, in inner_run
self.validate(display_num_errors=True)
File "..../django/core/management/base.py", line 253, in validate
raise CommandError("One or more models did not validate:\n%s" % error_text)
django.core.management.base.CommandError: One or more models did not validate:
app.child: Accessor for field 'field' clashes with related field 'Orange.app_child_related'. Add a related_name argument to the definition for 'field'.
app.child: Reverse query name for field 'field' clashes with related field 'Orange.app_child_related'. Add a related_name argument to the definition for 'field'.
app.child: Accessor for field 'field' clashes with related field 'Orange.app_child_related'. Add a related_name argument to the definition for 'field'.
app.child: Reverse query name for field 'field' clashes with related field 'Orange.app_child_related'. Add a related_name argument to the definition for 'field'.
Instead of causing errors, it seems like the field should only be inherited once from BaseClass. My patch [1] handles this as follows: On each field instance, track the class it was originally declared for and the first non-abstract class that it shows up in. Then, when a field is being added to a class, check to make sure that it only gets added if it isn't a "duplicate". (The patch would incidentally also need to move the get_FIELD_display method declaration [2] into cls._meta.add_field.) If this is an acceptable idea, I would be happy to add tests and docs. If the general idea is acceptable, but the implementation is flawed, I would be happy to write patches using other methods that people may suggest; this was just what made sense to me.