Opened 15 years ago

Closed 13 years ago

Last modified 13 years ago

#11369 closed (fixed)

verbose_name_plural not inherited if base class is abstract.

Reported by: Beetle_B Owned by: Ramiro Morales
Component: Database layer (models, ORM) Version: 1.0
Severity: Keywords: verbose_name_plural, inheritance, abstract
Cc: mueen@…, bronger@… Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Perhaps this is desired behavior. However, I think the docs should mention this explicitly.

The situation where this arose is that I was creating a CategoryBase class (abstract), which was a bit barebones. I then subclassed this to get a number of derived classes (all abstract as well) - because I have different use cases (some need an HTML description, which will be formatted using Textile or Markdown, etc). Then for one particular app, I needed one of these, so I subclassed (without making it an abstract class).

I had set verbose_name_plural in the Meta of the original CategoryBase class. This did not get inherited...

In summary:

CategoryBase --> CategoryMarkup --> Category

where all but Category were abstract classes.

Anyway, I'm not arguing it should be inherited - will leave that up to you to decide. I just feel that if it isn't, it should be reflected in the docs for abstract base classes.

Attachments (1)

11369-r11267.diff (2.4 KB ) - added by Ramiro Morales 15 years ago.

Download all attachments as: .zip

Change History (18)

comment:1 by Beetle_B, 15 years ago

Cc: mueen@… added

comment:3 by Beetle_B, 15 years ago

It's not obvious from those docs.

It explicitly states that Meta options are inherited if you don't define a Meta class. It then goes on to say that some options aren't (giving the examples of db_table and abstract). That's about it. Are we supposed to guess whether verbose_name_plural is inherited?

db_table is obvious.
abstract is not that obvious, which is why it was explicitly explained.

Likewise, I think stuff like verbose_name_plural is not obvious.

(Unless I'm missing something else from the docs...)

comment:4 by Alex Gaynor, 15 years ago

Component: UncategorizedDocumentation
Triage Stage: UnreviewedAccepted

The documentation should explicitly state what properties are/are not inherited.

comment:5 by Ramiro Morales, 15 years ago

Some tests I've performed trying to understand this ticket (also available at http://dpaste.de/45tH/), the <django.utils.functional.__proxy__ object hints this is getting handled by the string_concat calls in django.db.models.options.Option.contribute_to_class:

# models.py

from django.db import models

class CategoryBase(models.Model):
    class Meta:
        abstract = True
        verbose_name_plural = u'CategoryBase verbose_name_plural'

class Category(CategoryBase):
    title = models.CharField(max_length=100)

#----------------------
>>> from t11369.models import Category

>>> c = Category(title=u'Title A')
>>> c.save()
>>> print Category._meta.verbose_name_plural
CategoryBase verbose_name_plural
>>> print c._meta.verbose_name_plural
CategoryBase verbose_name_plural

#======================
# models.py

from django.db import models

class CategoryBase(models.Model):
    class Meta:
        abstract = True
        verbose_name_plural = u'CategoryBase verbose_name_plural'

class CategoryMarkup(CategoryBase):
    class Meta:
        abstract = True
        verbose_name_plural = u'CategoryMarkup verbose_name_plural'

class Category(CategoryMarkup):
    title = models.CharField(max_length=100)

#----------------------
>>> from t11369.models import Category

>>> c = Category(title=u'Title A')
>>> c.save()
>>> print Category._meta.verbose_name_plural
CategoryMarkup verbose_name_plural
>>> print c._meta.verbose_name_plural
CategoryMarkup verbose_name_plural

#======================
# models.py

from django.db import models

class CategoryBase(models.Model):
    class Meta:
        abstract = True
        verbose_name_plural = u'CategoryBase verbose_name_plural'

class CategoryMarkup(CategoryBase):
    class Meta:
        abstract = True

class Category(CategoryMarkup):
    title = models.CharField(max_length=100)

#----------------------
>>> from t11369.models import Category

>>> c = Category(title=u'Title A')
>>> c.save()
>>> print Category._meta.verbose_name_plural
<django.utils.functional.__proxy__ object at 0xb75a76ec>
>>> print c._meta.verbose_name_plural
<django.utils.functional.__proxy__ object at 0xb75a76ec>

comment:6 by Ramiro Morales, 15 years ago

Component: DocumentationDatabase layer (models, ORM)

As can be seen above, problem occurs when there is one or more intermediate abstract classes between the one containing the verbose_name_plural definition and the non-abstract one.

In the example posted by the OP, the CategoryMarkup model needs to define its own inner Meta class basause it has to explicitely specify it is a abstract class. Then, if it is desired it inherits the verbose_name_plural attribute from CategoryBase we need to make sure it extends its Meta inner class by subclassing it:

class CategoryBase(models.Model):
    class Meta:
        abstract = True
        verbose_name_plural = u'CategoryBase verbose_name_plural'

class CategoryMarkup(CategoryBase):
    class Meta(CategoryBase.Meta):
        abstract = True

#...

OP didn´t paste his models se we don't know if he was taking this in account.

But even if this usage error isn't present in user's models there is a Django bug (Category.verbose_name_plural values shown are <django.utils.functional.proxy... as above and they evaluate to "Categorys" in the admin). IMHO it should treat verbose_name_plural in a way consistent with verbose_name`. I'm changing the component field of this ticket accordingly.

by Ramiro Morales, 15 years ago

Attachment: 11369-r11267.diff added

comment:7 by Ramiro Morales, 15 years ago

Has patch: set

comment:8 by Beetle_B, 15 years ago

Owner: changed from nobody to Beetle_B
Status: newassigned

OP here.

Here's how I remember it (I have the code, but it may have been modified since then):

There's one (perhaps important) difference:

class CategoryBase(models.Model):
    class Meta:
        abstract = True
        verbose_name_plural = u'CategoryBase verbose_name_plural'

class CategoryMarkup(CategoryBase):
    class Meta(CategoryBase.Meta):
        abstract = True

class Category(CategoryMarkup):
    title = models.CharField(max_length=100)

The difference is that the intermediate abstract model (CategoryMarkup)'s Meta is subclassing the Meta of the parent. This was as per the docs...

In any case, your examples are illuminating. So normally, verbose_name_plural is inherited from an abstract base class that defines it?

The point of this bug report was that the docs should state explicitly what is or is not inherited. Your examples have, in a sense, confused me, and is suggesting an inconsistent behavior (in which case a separate ticket needs to be opened). On the one hand, it is inherited (if the immediate parent defines it). On the other, it is not (even when the intermediate abstract class's Meta is inheriting directly from the parent's Meta).

comment:9 by Beetle_B, 15 years ago

Owner: Beetle_B removed
Status: assignednew

comment:10 by Beetle_B, 15 years ago

Owner: set to Beetle_B
Status: newassigned

comment:11 by Beetle_B, 15 years ago

Owner: Beetle_B removed
Status: assignednew

I don't know what I'm doing "wrong", but somehow this keeps getting assigned to me. Trying to reset...

in reply to:  8 comment:12 by Ramiro Morales, 15 years ago

Replying to Beetle_B:

OP here.

Here's how I remember it (I have the code, but it may have been modified since then):
...
The difference is that the intermediate abstract model (CategoryMarkup)'s Meta is subclassing the Meta of the parent. This was as per the docs...

Correct.

In any case, your examples are illuminating. So normally, verbose_name_plural is inherited from an abstract base class that defines it?

Correct.

The point of this bug report was that the docs should state explicitly what is or is not inherited.

Oops, sorry. From the tracking of the ticket history I had understood this ticket was initially opened to report the mis-behavior and later changed to be about the docs, that's why I changed is component to ORM.

Your examples have, in a sense, confused me, and is suggesting an inconsistent behavior (in which case a separate ticket needs to be opened). On the one hand, it is inherited (if the immediate parent defines it). On the other, it is not (even when the intermediate abstract class's Meta is inheriting directly from the parent's Meta).

Correct, and the patch solves it, the regression test case added shows this with an very similar example: It fails without the patch and works after applying it.

Anyway, if the patch (or another solution) is committed to solve the problem that Django is treating the verbose_name_plural in a way different to verbose_name when abstract model inheritance is involved then there won't be any special cases to document (apart for the cases already covered: abstract is not inherited and db_table is but it won't make any sense). That is, all the meta model options are inherited just like in the non-abstract scenario and IMHO there won't be any need to explicitely list them.

comment:13 by Ramiro Morales, 14 years ago

Owner: set to Ramiro Morales
Status: newassigned

comment:14 by Torsten Bronger, 14 years ago

Cc: bronger@… added

comment:15 by Ramiro Morales, 13 years ago

Resolution: fixed
Status: assignedclosed

(In [14588]) Fixed #11369 -- Corrected verbose_name_plural model Meta option to be consistent with verbose_name when using abstract model inheritance. Thanks Beetle_B for the report.

comment:16 by Ramiro Morales, 13 years ago

(In [14589]) [1.2.X] Fixed #11369 -- Corrected verbose_name_plural model Meta option to be consistent with verbose_name when using abstract model inheritance. Thanks Beetle_B for the report.

Backport of [14588] from trunk

comment:17 by Torsten Bronger, 13 years ago

I exterience the very same problem with "ordering" instead of "verbose_name_plural". Is this related?

Note: See TracTickets for help on using tickets.
Back to Top