Django:继承和扩展`ModelBase`的自定义元类
Django: Custom Metaclass Inheriting From And Extending `ModelBase`
我正在尝试做一些元类魔术。我想要我自己的元类
从 ModelBase
继承,然后我想添加额外的逻辑
扩展其 __new__
方法。但是我觉得有些东西
MRO/inheritance 命令在我使用它的方式中发生了奇怪的事情。
基本情况如下:
from django.db.models import Model, ModelBase
class CustomMetaclass(ModelBase):
def __new__(cls, name, bases, attrs):
# As I am trying to extend `ModelBase`, I was expecting this
# call to `super` to give me the return value from here:
# https://github.com/django/django/blob/master/django/db/models/base.py#L300
# And that I would be able to access everyhing in `_meta` with
# `clsobj._meta`. But actually this object is
# `MyAbstractModel` and has no `_meta` property so I'm pretty
# sure `__new__` isn't being called on `ModelBase` at all at
# this point.
clsobj = super().__new__(cls, name, bases, attrs)
# Now, I want to have access to the `_meta` property setup by
# `ModelBase` so I can dispatch on the data in there. For
# example, let's do something with the field definitions.
for field in clsobj._meta.get_fields():
do_stuff_with_fields()
return clsobj
class MyAbstractModel(metaclass=CustomMetaclass):
"""This model is abstract because I only want the custom metaclass
logic to apply to those models of my choosing and I don't want to
be able to instantiate it directly. See the class definitions below.
"""
class Meta:
abstract = True
class MyModel(Model):
"""Regular model, will be derived from metaclass `ModelBase` as usual.
"""
pass
class MyCustomisedModel(MyAbstractModel):
"""This model should enjoy the logic defined by our extended `__new__` method.
"""
pass
知道为什么 ModelBase
上的 __new__
没有被调用
CustomMetaClass
?如何以这种方式正确扩展 ModelBase
?我很确定元类继承是可能的
但好像我错过了什么...
获取带有 _meta
属性的 clsobj
的方法很简单:
class CustomMetaclass(ModelBase):
def __new__(cls, name, bases, attrs):
bases = (Model,)
clsobj = super().__new__(cls, name, bases, attrs)
for field in clsobj._meta.get_fields():
do_stuff_with_fields()
return clsobj
我们可以用 MyAbstractModel(Model, metaclass=CustomMetaclass)
做同样的事情。
但是,这里的最终成功仍然取决于我们打算在 __new__
方法中进行的工作类型。如果我们想以某种方式内省并使用元编程处理 class 的字段,我们需要意识到我们正在尝试使用 处的 __new__
重写 class import 时间因此(因为这是 Django)app registry 还没有准备好,如果出现某些情况(例如,我们被禁止访问或使用反向关系),这可能会引发异常.即使将 Model
作为基础传递到 __new__
中,也会发生这种情况。
我们可以通过使用以下对 _get_fields
的非 public 调用(Django 在某些地方自己执行)来半规避其中的一些问题:
class CustomMetaclass(ModelBase):
def __new__(cls, name, bases, attrs):
bases = (Model,)
clsobj = super().__new__(cls, name, bases, attrs)
for field in clsobj._meta._get_fields(reverse=False):
do_stuff_with_fields()
return clsobj
但根据具体情况和我们想要实现的目标,我们可能仍然会遇到问题;例如,我们将无法使用我们的 metaclass 访问任何反向关系。所以还是不行
为了克服这个限制,我们必须利用应用程序注册表中的信号来使我们的 classes 像我们希望的那样动态,并具有对 _meta.get_fields
.
的完全访问权限
看到这张票:https://code.djangoproject.com/ticket/24231
主要收获是:"a Django model class is not something you are permitted to work with outside the context of a prepared app registry."
我正在尝试做一些元类魔术。我想要我自己的元类
从 ModelBase
继承,然后我想添加额外的逻辑
扩展其 __new__
方法。但是我觉得有些东西
MRO/inheritance 命令在我使用它的方式中发生了奇怪的事情。
基本情况如下:
from django.db.models import Model, ModelBase
class CustomMetaclass(ModelBase):
def __new__(cls, name, bases, attrs):
# As I am trying to extend `ModelBase`, I was expecting this
# call to `super` to give me the return value from here:
# https://github.com/django/django/blob/master/django/db/models/base.py#L300
# And that I would be able to access everyhing in `_meta` with
# `clsobj._meta`. But actually this object is
# `MyAbstractModel` and has no `_meta` property so I'm pretty
# sure `__new__` isn't being called on `ModelBase` at all at
# this point.
clsobj = super().__new__(cls, name, bases, attrs)
# Now, I want to have access to the `_meta` property setup by
# `ModelBase` so I can dispatch on the data in there. For
# example, let's do something with the field definitions.
for field in clsobj._meta.get_fields():
do_stuff_with_fields()
return clsobj
class MyAbstractModel(metaclass=CustomMetaclass):
"""This model is abstract because I only want the custom metaclass
logic to apply to those models of my choosing and I don't want to
be able to instantiate it directly. See the class definitions below.
"""
class Meta:
abstract = True
class MyModel(Model):
"""Regular model, will be derived from metaclass `ModelBase` as usual.
"""
pass
class MyCustomisedModel(MyAbstractModel):
"""This model should enjoy the logic defined by our extended `__new__` method.
"""
pass
知道为什么 ModelBase
上的 __new__
没有被调用
CustomMetaClass
?如何以这种方式正确扩展 ModelBase
?我很确定元类继承是可能的
但好像我错过了什么...
获取带有 _meta
属性的 clsobj
的方法很简单:
class CustomMetaclass(ModelBase):
def __new__(cls, name, bases, attrs):
bases = (Model,)
clsobj = super().__new__(cls, name, bases, attrs)
for field in clsobj._meta.get_fields():
do_stuff_with_fields()
return clsobj
我们可以用 MyAbstractModel(Model, metaclass=CustomMetaclass)
做同样的事情。
但是,这里的最终成功仍然取决于我们打算在 __new__
方法中进行的工作类型。如果我们想以某种方式内省并使用元编程处理 class 的字段,我们需要意识到我们正在尝试使用 处的 __new__
重写 class import 时间因此(因为这是 Django)app registry 还没有准备好,如果出现某些情况(例如,我们被禁止访问或使用反向关系),这可能会引发异常.即使将 Model
作为基础传递到 __new__
中,也会发生这种情况。
我们可以通过使用以下对 _get_fields
的非 public 调用(Django 在某些地方自己执行)来半规避其中的一些问题:
class CustomMetaclass(ModelBase):
def __new__(cls, name, bases, attrs):
bases = (Model,)
clsobj = super().__new__(cls, name, bases, attrs)
for field in clsobj._meta._get_fields(reverse=False):
do_stuff_with_fields()
return clsobj
但根据具体情况和我们想要实现的目标,我们可能仍然会遇到问题;例如,我们将无法使用我们的 metaclass 访问任何反向关系。所以还是不行
为了克服这个限制,我们必须利用应用程序注册表中的信号来使我们的 classes 像我们希望的那样动态,并具有对 _meta.get_fields
.
看到这张票:https://code.djangoproject.com/ticket/24231
主要收获是:"a Django model class is not something you are permitted to work with outside the context of a prepared app registry."