Opened 17 months ago

Last modified 17 months ago

#34555 closed Bug

Dynamic model fields without using a metaclass — at Initial Version

Reported by: hottwaj Owned by: nobody
Component: Database layer (models, ORM) Version: 4.2
Severity: Normal Keywords: ModelBase init_subclass
Cc: Carlton Gibson Triage Stage: Unreviewed
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

I'd like to be able to write some abstract model "templates" that can be re-used/customised in various other django apps. Some of these templates require ForeignKeys to other "templates" which are also abstract. Obviously ForeignKeys to Abstract models are not possible

Having used metaclasses in a similar situation before, but hoping to find a less complex approach, I thought I might be able to implement this by providing a init_subclass method on my "template" model, with that method designed to set up the ForeignKey to a given concrete model, but this does not work. It seems that init_subclass is called after ModelBase.new collects fields that require "contribute_to_class", so the fields I add in init_subclass are never "seen" by e.g. the migration builder.

Example approach below (but note this does not work for reasons explained above):

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

Essentially what I'd like is some way of doing some extra work after BaseModel.new is called without resorting to having to write a metaclass for BaseBookModel - I'm just adding some extra fields and a metaclass is complex for most mortals to read let alone write. There does not seem to be an appropriate hook method that I can override to do this for every subclass of BaseBookModel

Thanks for reading and appreciate any thoughts!

Change History (0)

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