【问题标题】:how to override the verbose name of a superclass model field in django如何覆盖 Django 中超类模型字段的详细名称
【发布时间】:2010-10-29 23:43:46
【问题描述】:

假设我有一个继承自 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

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

【问题讨论】:

  • 感谢您的回答!所以我们可以(i)在表单中设置标签,或者(ii)在通过 _meta.get_fields 方法定义类之后设置verbose_name。两者都很容易实现,但是我认为我更喜欢使用 _meta.get_fields 方法,这样我就可以将所有与模型相关的东西保存在模型模块中。

标签: python django inheritance django-models


【解决方案1】:

最好的办法是设置/更改表单本身的标签。引用Foo 模型的name 字段(例如,通过在Foo._meta.fields 中查找)实际上会给您提供对SuperFooname 字段的引用,因此更改其verbose_name 将改变它在两个模型中。

另外,将name 字段添加到Foo 类也不起作用,because...

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

【讨论】:

    【解决方案2】:

    我使用的一个简单技巧是:

    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'
    

    【讨论】:

    • 这实际上改变了 SuperFoo 类的字段的详细名称。例如,如果您有:class Bar(SuperFoo),则“name”字段的 verbose_name 也是“Whatever”...即使您遵循与 Bar._meta.get_field('name').verbose_name = 相同的过程'随便 2'
    • 如果 SuperFoo 是一个抽象基类,上面的注释是不正确的。只要 SuperFoo 类是抽象的,这个方法就很好用,所以 class Meta: abstract = True
    • 问题是 SuperFoo 有时不能抽象
    【解决方案3】:

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

    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
    
        ....
    

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

    【讨论】:

      【解决方案4】:

      请记住修改 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
      

      像这样使用它:

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

      上面的示例更改了继承字段“时间戳”的详细名称和帮助文本。您可以传入任意数量的关键字参数,因为您想要修改的字段。

      【讨论】:

      • 谢谢!在 Django 1.11.4 上像魅力一样工作。
      【解决方案5】:

      我有来自同一个基类的不同子类。而且我只关心 Django Admin 界面中的表单视图。

      唯一对我有用的解决方案是自定义 Django 管理界面并在那里设置详细名称(或帮助文本):

      class FooAdmin(admin.ModelAdmin):
          def get_form(self, request, obj=None, **kwargs):
              form = super().get_form(request, obj, **kwargs)
              form.base_fields['name'].verbose_name = "my verbose name"
              return form
      
      admin.site.register(models.Foo, FooAdmin)
      

      【讨论】:

        猜你喜欢
        • 2019-07-25
        • 1970-01-01
        • 2011-01-26
        • 1970-01-01
        • 2011-08-23
        • 2016-07-18
        • 2010-09-20
        • 2021-09-09
        • 2013-01-07
        相关资源
        最近更新 更多