2

I am trying to define entity architecture that, if simplified, can be expressed like this:

class M(models.Model):
    field_m = models.CharField(max_length=255)
    class Meta:
        abstract = True

class A(M):
    field_a_1 = models.CharField(max_length=255)
    field_a_2 = models.CharField(max_length=255)
    class Meta:
        abstract = True

class B(A):
    field_b = models.CharField(max_length=255)
    class Meta:
        abstract = True

class C(A):
    field_c = models.CharField(max_length=255)
    class Meta:
        abstract = True


class D(A):
    field_d = models.CharField(max_length=255)
    class Meta:
        abstract = True

class DD(D):
    class Meta:
        abstract = True

class X(B, C, DD):
    field_x = models.CharField(max_length=255)
    pass

As you can see, X has some mixins (abstract entitites). Each of the mixin has their own custom logic implemented inside them. But ultimately all of them have 1 common parent- abstract class A.

As far as I understand, this should work. And MRO resolution, indeed, works. However, when starting the project I get 2 errors per each field field A (that are inherited in X):

X.field_m : (models.E006) The field 'field_m ' clashes with the field 'field_m ' from model 'X'.
X.field_m : (models.E006) The field 'field_m ' clashes with the field 'field_m ' from model 'X'.
X.field_a_1 : (models.E006) The field 'field_a_1 ' clashes with the field 'field_a_1 ' from model 'X'.
X.field_a_1 : (models.E006) The field 'field_a_1 ' clashes with the field 'field_a_1 ' from model 'X'.
X.field_a_2 : (models.E006) The field 'field_a_2 ' clashes with the field 'field_a_2 ' from model 'X'.
X.field_a_2 : (models.E006) The field 'field_a_2 ' clashes with the field 'field_a_2 ' from model 'X'.

I am working with Django 1.11

TylerH
  • 20,799
  • 66
  • 75
  • 101
Arsen Simonean
  • 362
  • 2
  • 17

1 Answers1

2

There is an old issue ticket here that resulted in making Django validate these kinds of problems https://code.djangoproject.com/ticket/24542

It is because B and C inherit from A so they will have the same field_m and it is not valid. Django devs decided that Django will validate this instead of

ignore subsequent model fields from abstract model base classes (per MRO-order) with the same name as existing fields.

On a side note. This is bad design and you should keep your inheritance simple as per the docs https://docs.djangoproject.com/en/2.2/topics/db/models/#s-multiple-inheritance

Generally, you won’t need to inherit from multiple parents. The main use-case where this is useful is for “mix-in” classes: adding a particular extra field or method to every class that inherits the mix-in. Try to keep your inheritance hierarchies as simple and straightforward as possible so that you won’t have to struggle to work out where a particular piece of information is coming from.

Srdjan Cosic
  • 324
  • 2
  • 9
  • I was wondering if this is bad design or not. For example I have an "number" field that represents a human readable id. I want to use it for logging and exception messages in each mixin class that inherits from A, for example. How should I design it? I used to add properties to the class with the same name, but that seems like an even worse design. Moreover, it is possible in python, only Django limits it and states that it is a bad design so I am still not sure about that. But I get it, the framework does not allow it so I should design it differently. – Arsen Simonean Aug 29 '19 at 09:47
  • Yeah I cannot say why the Django devs decided it was not allowed but reading the issue, it was due to the fact that later on Django tried to save the same field twice to the DB, resulting in an incomprehensible error message. I assume it was easier to validate that it is not allowed, rather than ignore subsequent model fields. As for the ID part. You should add a separate Abstract class called LoggingID and have that id field in it. Instead of inheriting LoggingID to another Abstract class you do `class RealModel(AbstractClass, AnotherAbstractClass, LoggingID)` – Srdjan Cosic Aug 29 '19 at 11:48
  • Just make sure that `AbstractClass` and `AnotherAbstractClass` don't inherit from `LoggingID` or have fields that clash with `LoggingID`. But again try to keep it simple, I just made that example so it would be clear what I meant :) – Srdjan Cosic Aug 29 '19 at 11:49