如何覆盖django中超类模型字段的详细名称

sha*_*nyu 27 python django inheritance django-models

假设我有一个继承自SuperFoo的模型Foo:

class SuperFoo(models.Model):
    name = models.CharField('name of SuperFoo instance', max_length=50)
    ...

class Foo(SuperFoo):
    ... # do something that changes verbose_name of name field of SuperFoo
Run Code Online (Sandbox Code Playgroud)

在类Foo中,我想覆盖SuperFoo的name字段的verbose_name.我可以吗?如果没有,是在模型表单定义中设置标签以使其显示在模板中的最佳选项吗?

小智 53

我用过的一个简单的黑客是:

class SuperFoo(models.Model):
    name = models.CharField('name of SuperFoo instance', max_length=50)
    ...
    class Meta: 
        abstract = True

class Foo(SuperFoo):
    ... # do something that changes verbose_name of name field of SuperFoo
Foo._meta.get_field('name').verbose_name = 'Whatever'
Run Code Online (Sandbox Code Playgroud)

  • 这实际上更改了SuperFoo类的字段的verbose_name.例如,如果你有:class Bar(SuperFoo),'name'字段的verbose_name也是'Whatever'...即使你按照与Bar._meta.get_field('name')相同的程序.verbose_name = '无论2' (3认同)
  • 如果SuperFoo是一个抽象基类,则上述注释不正确.只要SuperFoo类是抽象的,这个方法就可以正常工作,所以类Meta:abstract = True (2认同)

And*_*ker 10

请记住,修改Foo._meta.fields也会影响超类 - 因此只有在超类是抽象的时候才真正有用,我已经将@Gerry放弃的答案包装成可重复使用的类装饰器:

def modify_fields(**kwargs):
    def wrap(cls):
        for field, prop_dict in kwargs.items():
            for prop, val in prop_dict.items():
                setattr(cls._meta.get_field(field), prop, val)
        return cls
    return wrap
Run Code Online (Sandbox Code Playgroud)

像这样使用它:

@modify_fields(timestamp={
    'verbose_name': 'Available From',
    'help_text': 'Earliest date you can book this'})
class Purchase(BaseOrderItem):
    pass
Run Code Online (Sandbox Code Playgroud)

上面的示例更改了继承字段"timestamp"的verbose_name和help_text.您可以传入与要修改的字段一样多的关键字参数.


Ste*_*eef 5

您最好的选择是在表单中设置/更改标签.参考模型的name领域Foo(例如,通过查找Foo._meta.fields)实际上将为您提供对该name领域的参考SuperFoo,因此改变verbose_name它将在两个模型中改变它.

此外,向该类添加name字段Foo也不起作用,因为 ......

在父模型中覆盖字段会导致诸如初始化新实例(指定正在进行哪个字段Model.__init__)和序列化等方面的困难 .这些是普通Python类继承不必以完全相同的方式处理的特性,因此Django模型继承和Python类继承之间的区别不仅仅是任意的.


vdb*_*oor 5

看看Django-CMS是如何做到这一点的,它们会覆盖db_table继承自的模型中的字段CMSPlugin.基础知识(我也用于我自己的东西)归结为:

class SuperFooMetaClass(ModelBase):
    def __new__(cls, name, bases, attrs):
        new_class = super(SuperFooMetaClass, cls).__new__(cls, name, bases, attrs)
        new_class._meta.verbose_name = "...."   # perhaps only if not customized
        return new_class


class SuperFoo(models.Model):
    __metaclass__ = SuperFooMetaClass

    ....
Run Code Online (Sandbox Code Playgroud)

您可以添加一些检查,例如,仅更新子类(不是直接类型),或仅在未自定义值时更新.