Django, проверьте реализацию подкласса django.db.Model

Я создаю подкласс класса Model из Django и требую от него реализации некоторых атрибутов и методов. Я сделал это, наследуя от ABC

from django.db.models.base import ModelBase


class AbstractFooMeta(ModelBase, ABCMeta):
    """To avoid metaclass conflicts"""


class AbstractFoo(ABC, models.Model, metaclass=AbstractUploadMeta):
    my_num: int
    magic_string: str
    name = models.CharField(max_length=250)

    class Meta:
        abstract = True

    @abstractmethod
    def do_thing(self):
        pass

    @classmethod
    @abstractmethod
    def do_class_thing(cls):
        pass


class AbstractMagicFoo(AbstractFoo):
    magic_str = "answer is 42"


class Foo(AbstractMagicFoo):
    my_num = 7

    def do_thing(self) -> str:
        print('did the thing')

    @classmethod
    def do_class_thing(cls) -> str:
        print('did the class thing')

Я могу создать миграцию, но когда я пытаюсь запустить миграцию, я получаю:

  File "/home/michael/.venv/project/lib/python3.9/site-packages/django/db/migrations/state.py", line 362, in reload_model
    self._reload(related_models)
  File "/home/michael/.venv/project/lib/python3.9/site-packages/django/db/migrations/state.py", line 395, in _reload
    self.apps.render_multiple(states_to_be_rendered)
  File "/home/michael/.venv/project/lib/python3.9/site-packages/django/db/migrations/state.py", line 597, in render_multiple
    model.render(self)
  File "/home/michael/.venv/project/lib/python3.9/site-packages/django/db/migrations/state.py", line 872, in render
    return type(self.name, bases, body)
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

Я предполагаю, что Django не использует определенный мной метакласс. Я пробовал установить метакласс для всех дочерних классов, но это не помогло.

Как я могу прикрепить некоторую логику к AbstractFoo, чтобы при использовании конкретного класса (т.е. не AbstractMagicFoo, а только с Foo), он выполнял проверку того, что реализованы следующие атрибуты и методы:

  • my_num
  • magic_str
  • do_thing
  • и do_class_thing?

И если какая-либо из проверок не проходит, то возникает ошибка. Я пытаюсь добиться того, чтобы ошибка возникала, когда класс определен, т.е. до его инстанцирования, потому что он может не инстанцироваться в течение длительного времени.

Я бы сделал свой собственный метакласс для выполнения проверок, но тогда я вернусь к началу с ошибкой метакласса во время миграции.

Неидеальное решение, к которому я пришел в настоящее время: Я создал методы экземпляра, которые поднимают NotImplementedError, если они не являются подклассом. Не идеально, потому что ошибка обнаруживается только во время выполнения, а не при создании класса.

Я решил эту проблему, добавив проверку в пользовательский мета-класс, который, на удивление, работает с миграциями Django:

class AbstractFooMeta(ModelBase, ABCMeta):
    """Not using ABC with django.db.Model because it errors out when running a migration"""

    def __new__(cls, name, bases, attrs, **kwargs):
        Class = super().__new__(cls, name, bases, attrs, **kwargs)
        if not Class.Meta.abstract:
            # Only check concrete models, e.g. not for Example AbstractS3Upload
            if not hasattr(Class, 'my_num'):
                raise NotImplementedError('Please add a my_num attribute')
        return Class
Вернуться на верх