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