【发布时间】:2012-10-20 23:30:56
【问题描述】:
我正在为大约 10 个模型构建一些抽象模型。我需要以某种方式使 1 字段未在抽象模型中声明,但必须在继承模型中声明。
怎么做?有什么方法可以使用NotImplementedError吗?
【问题讨论】:
标签: python django django-models
我正在为大约 10 个模型构建一些抽象模型。我需要以某种方式使 1 字段未在抽象模型中声明,但必须在继承模型中声明。
怎么做?有什么方法可以使用NotImplementedError吗?
【问题讨论】:
标签: python django django-models
如果可能的话,恐怕没有一种简单的方法可以在不深入研究 Django 的情况下实现这一目标。
主要原因是 Django 中的Field name "hiding" is not permitted。这意味着如果您想在作为Field 实例的基本抽象类中声明一个抽象属性,则与正常的Python 类继承范例相反,您将无法在子类中重写它。引用文档:
在正常的 Python 类继承中,允许子类覆盖父类的任何属性。在 Django 中,这对于作为 Field 实例的属性是不允许的(至少目前不允许)。如果基类有一个名为 author 的字段,则不能在继承自该基类的任何类中创建另一个名为 author 的模型字段。
覆盖父模型中的字段会导致在初始化新实例(指定在 Model.init 中初始化哪个字段)和序列化等方面出现困难。这些是普通 Python 类继承不必以完全相同的方式处理的特性,因此 Django 模型继承和 Python 类继承之间的区别不是任意的。
此限制仅适用于作为 Field 实例的属性。如果您愿意,可以覆盖普通的 Python 属性。它也仅适用于 Python 看到的属性名称:如果您手动指定数据库列名,则可以在子模型和祖先模型中出现相同的列名以进行多表继承(它们是列在两个不同的数据库表中)。
如果您覆盖任何祖先模型中的任何模型字段,Django 将引发 FieldError。
但是,如果属性不是 Field 实例(尽管可能性很小),您将能够通过使用 @property 装饰器来实现您想要的。像这样的东西应该可以工作:
class Person(models.Model):
def __init__(self, *args, **kwargs):
super(Person, self).__init__(*args, **kwargs)
self.last_name
first_name = models.CharField(max_length=30)
@property
def last_name(self):
raise NotImplementedError
class Meta:
abstract = True
class Student(Person):
home_group = models.CharField(max_length=5)
last_name = "Doe" # "models.CharField()" will not work!
class BadStudent(Person):
home_group = models.CharField(max_length=5)
# "NotImplmentedError" will be raised when instantiating BadStudent()
您可能还想看看abc.abstractproperty。我不确定它如何与 Django 的模型继承一起工作。
【讨论】:
你为什么要这样做?什么是公共字段不能在 AbstractModel 中声明的原因?
如果您真的想这样做,请使用此处的说明: add methods in subclasses within the super class constructor
【讨论】: