我正在创建Django的Model类的一个子类,并要求用户实现一些属性和方法。我继承了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
- 和CCD_ 7
如果任何检查失败,就会引发错误。我试图让错误在定义类时发生,即在它被实例化之前,因为它可能在很长一段时间内不会被实例化。
我会制作自己的元类来执行检查,但在迁移过程中,我又回到了元类错误的原点。
我目前正在做的None理想解决方案是:我创建了实例方法,如果它们没有被子类化,就会引发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