Opened 13 years ago
Closed 13 years ago
#16646 closed Bug (invalid)
Only first meta inner class inherited with multiple abstract base models.
Reported by: | John Shimek | Owned by: | nobody |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 1.3 |
Severity: | Normal | Keywords: | |
Cc: | Triage Stage: | Unreviewed | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
Code shows this better:
app.models.py
from django.db import models class FirstMixin(models.Model): name = models.CharField(max_length=100) class Meta: abstract = True class SecondMixin(models.Model): order = models.PositiveIntegerField() class Meta: abstract = True ordering = ['order'] class Concrete(FirstMixin, SecondMixin): published = models.BooleanField()
Ordering is set on the Concrete instances:
>>> from red.models import Concrete >>> c = Concrete() >>> c._meta.ordering []
If instead concrete is defined with
class Concrete(SecondMixin, FirstMixin): published = models.BooleanField()
Notice that SecondMixin is the first class that Concrete subclasses instead of FirstMixin. This results in
>>> c = Concrete() >>> c._meta.ordering ['order']
It is very unintuitive and in my opinion incorrect. The order of which classes to inherit from shouldn't prevent inheriting the Meta inner class as well. This could cause hard to find bugs, such as why 'ordering' is not correct or why two different concrete classes have different meta settings while having the same parent classes.
The other way to address this is like so:
class Concrete(FirstMixin, SecondMixin): published = models.BooleanField() class Meta(SecondMixin.Meta): pass
This is more explicit, but would a developer think this has to be done until they ran into this issue? Also, I think this is the only way to possibly get Meta options from multiple parent classes by having class Meta(SecondMixin.Meta, FirstMixin.Meta).
Change History (4)
comment:1 by , 13 years ago
Resolution: | → worksforme |
---|---|
Status: | new → closed |
follow-up: 3 comment:2 by , 13 years ago
Resolution: | worksforme |
---|---|
Status: | closed → reopened |
They do not address the concern. For starters, ordering was not copied over to the child class in the first case. Second, from the docs:
When an abstract base class is created, Django makes any Meta inner class you declared in the base class available as an attribute. If a child class does not declare its own Meta class, it will inherit the parent's Meta.
https://docs.djangoproject.com/en/dev/topics/db/models/#meta-inheritance
My examples use abstract base classes, so the Meta class should be inherited. This is a matter of when using abstract base classes, the meta is not inherited for multiple abstract base classes.
comment:3 by , 13 years ago
I understand that you would like the parents' Meta
classes to be magically merged to build the child Meta
class. Unfortunately, as pointed out in the first link I posted, for most options of Meta
that doesn't make sense. Even for ordering
, there are several way you can merge two values: take the first that isn't None
, concatenate, etc.
You could request this feature but it's likely to get rejected on the grounds of backwards-incompatibility.
Replying to varikin:
For starters, ordering was not copied over to the child class in the first case.
You're using multiple inheritance, and the docs say that "if multiple parents contain a Meta class, only the first one is going to be used, and all others will be ignored." This is the second link I posted.
In your first case, the first Meta
class contains no ordering. Hence the child contains no ordering.
One way to understand this is as follows:
>>> class A(object): ... attr = "foo" ... >>> class B(object): ... attr = "bar" ... >>> class C(A, B): ... pass ... >>> C.attr 'foo' >>> class D(B, A): ... pass ... >>> D.attr 'bar' >>>
Just put class Meta
instead of attr
in this example, and hopefully you will Django's behavior will look more natural.
You could also take a look at Python's behavior regarding __metaclass__
and multiple inheritance; it's a very similar problem.
Replying to varikin:
My examples use abstract base classes, so the Meta class should be inherited.
That's true; I had forgotten that; please disregard my first sentence.
This is a matter of when using abstract base classes, the meta is not inherited for multiple abstract base classes.
Only the first one — see above.
So I still believe Django behaves according to the documentation, and there isn't a bug here. Since I'm not really into close/reopen wars, I'll let someone else decide what to do with this ticket.
comment:4 by , 13 years ago
Resolution: | → invalid |
---|---|
Status: | reopened → closed |
Ok, I am convinced that it is working as documented though it feels wrong.
Generally, child classes don't inherit the
Meta
class of their parents. (There are with two limited exceptions,ordering
andget_latest_by
.) But you seem to assume that they do. It's probably a source of confusion.This document explains why this is the correct behavior: https://docs.djangoproject.com/en/dev/topics/db/models/#meta-and-multi-table-inheritance
The multiple inheritance situation is described here: https://docs.djangoproject.com/en/dev/topics/db/models/#multiple-inheritance
Please review these sections of the documentation as they address your questions.