Django,检查Django.db.Model子类的实现



我正在创建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

最新更新