Django Meta class abstract changing from True to False

Question:

I understand that when a class inherits from an Abstract Django Model, it will not inherit the meta attribute abstract = True, which makes sense.

However in the below example nothing has inherited from it, but yet it’s Meta.abstract is False even though its defined to be True:

from django.db import models
from django.db.models.base import ModelBase


class MyMeta(ModelBase):
    def __new__(cls, name, bases, attrs, **kwargs):
        """Check that all implemented (not abstract) classes have a foo attribute"""        
        Class = super().__new__(cls, name, bases, attrs, **kwargs)
        if not Class.Meta.abstract:
            print(Class)
            print('Class.Meta.ordering:', Class.Meta.ordering)  # Sanity check
            print('Class.Meta.abstract:', Class.Meta.abstract)
            if not hasattr(Class, 'foo'):
                raise NotImplementedError('Please add a foo attribute')
        return Class


class MyAbstractModel(models.Model, metaclass=MyMeta):
    name = models.CharField(max_length=250)

    class Meta:
        abstract = True
        ordering = ('-name',)

Prints:

<class 'myapp.models.base.MyAbstractModel'>
Class.Meta.ordering: -name
Class.Meta.abstract: False

Add raises (even though it should not raise because it is an abstract class):

NotImplementedError: Please add a foo attribute
Asked By: run_the_race

||

Answers:

Abstract is False because children classes don’t become abstract classes themselves. You need to explicitly set it True.
There is info about that in the docs.
https://docs.djangoproject.com/en/4.1/topics/db/models/

Answered By: gornichnyx

You are accessing the wrong attribute, namely Class.Meta. Since this attribute will be inherited by any child classes Django actually modifies [GitHub] it for abstract models:

if abstract:
    # Abstract base models can't be instantiated and don't appear in
    # the list of models for an app. We do the final setup for them a
    # little differently from normal models.
    attr_meta.abstract = False
    new_class.Meta = attr_meta
    return new_class

The attribute Django actually uses behind the scenes (All of the attributes are properly copied over to it) is _meta. Hence if you write the following you’ll get the expected result:

if not Class._meta.abstract:
    ...
Answered By: Abdul Aziz Barkat
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.