﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
34555	ModelBase metaclass implementation prevents addition of model fields via __init_subclass__	hottwaj	nobody	"The current implementation of ModelBase.__new__ prevents addition of model fields via python's __init_subclass__ method (described in PEP 487 and provided since python 3.6).  

My understanding is that currently, fields can be added to models outside of a class body by: 
i) calling ""model_cls.add_to_class(...)"" after the definition of model_cls e.g. in module level code after class definition, 
ii) using a class decorator to call .add_to_class as in point i), or
iii) using a metaclass to completely customise class creation

Inheritance and __init_subclass__ should in theory provide a relatively straightforward way to customise class creation without using a metaclass (option iii above), as described/encouraged in PEP 487, but Django does not currently support this for Field attributes of subclasses of Model because the ModelBase metaclass does not correctly pickup Fields added to a class during the execution of __init_subclass__

It seems that __init_subclass__ is called after ModelBase.__new__ collects Fields that require calling of their ""contribute_to_class"" method, so ModelBase does not do appropriate bookkeeping on fields added in __init_subclass__ and such Fields are then ultimately not  ""seen"" by e.g. the migration builder.

Correctly collecting Fields added by __init_subclass__ in ModelBase.__new__ would allow for easier customisation of model fields outside of a model class body and provide an implementation that works with __init_subclass__ in a way that matches (rather than contrary to) behaviour supported elsewhere in python.

A simple example that currently does not work, but I believe ought to work, is provided below.  In this example, the ""author"" attribute added in BaseBookModel.__init_subclass__is not collected by current implementation of ModelBase.__new__ so is not created in migrations and not available as might be expected in subclass Book.

{{{#!python
from django.db.models import Model, ForeignKey, CASCADE, CharField

class BaseBookModel(Model):
    class Meta:
        abstract = True

    def __init_subclass__(cls, author_model_cls: Type[Model], **kwargs,):
        super().__init_subclass__(**kwargs)
        author = ForeignKey(author_model_cls, on_delete = CASCADE)
        cls.add_to_class('author', author)

class Author(Model):
    name = CharField(max_len = 256, unique = True)

class Book(BaseBookModel, author_model_cls = Author):
    pass
}}}

Thanks for reading and appreciate any thoughts!
"	Bug	new	Database layer (models, ORM)	4.2	Normal		ModelBase init_subclass	Carlton Gibson	Someday/Maybe	1	0	0	0	0	0
