【问题标题】:How do you change field arguments in Django model subclasses?如何更改 Django 模型子类中的字段参数?
【发布时间】:2011-12-14 15:31:09
【问题描述】:

假设我有一些 Django 模型,它是一个抽象基类:

class Foo(models.Model):
    value=models.IntegerField()

    class Meta:
        abstract = True

它有两个派生类,我希望每个子类的字段默认值不同。我不能简单地覆盖该字段

class Bar(Foo):
    value=models.IntegerField(default=9)

因为 Django 不允许您覆盖子类中的字段。我看过有关尝试更改可用选项的帖子,但在这种情况下,我主要关心的是更改默认值。有什么建议吗?

【问题讨论】:

    标签: inheritance django-models default-value


    【解决方案1】:

    按照另一个答案中的建议重新定义保存方法的问题是,在您保存模型对象之前不会设置您的值。 不存在这个问题的另一个选择是在子类(Bar 类)中重新定义__init__

    def __init__(self, *args, **kwargs):
        if 'value' not in kwargs:
            kwargs['value'] = 9
        super(Bar, self).__init__(*args, **kwargs)
    

    【讨论】:

      【解决方案2】:

      我认为您尝试做的事情是不可能的,至少在 Django 中是不可能的。您必须将 Django 中的继承视为超类的 ForeignKey(仅此而已),并且您无法更改 FK 关系中属性的默认值。

      因此,您能做的最好的事情就是重新定义save() 方法。它会是这样的:

      def save(self, *args, **kwargs):
          if not self.value:
              self.value = 9
          super(Bar, self).save(*args, **kwargs)
      

      而且,在 Foo(超类)中:

          value = models.IntegerField(blank=True)
      

      避免数据库出现 NOT NULL 问题。

      【讨论】:

      • 如果我只是为基类中的所有元素定义默认值,那么我们是否可以只覆盖所有子类的保存方法,或者基类中定义的默认值会导致实例变量永远不会是空的? (也就是说,如果我在父类中定义了一个默认值,这个默认值是在保存的时候添加的还是在初始化对象的时候添加的?)
      • 再次,您必须将 Django 中的继承视为对超类的 FK,因此如果超类有一个具有默认值的字段,则子类中的该字段也会具有默认值的字段,因为该字段实际上不是来自子类而是来自超类。
      【解决方案3】:

      这定义了一个metaclass(和一个基类),它为所有子类提供了一个给定类型的字段,默认值可以在子类中设置,但不一定是:

      from django.db import models
      class DefaultTextFieldMetaclass(models.base.ModelBase):
          DEFAULT_TEXT = 'this is the metaclass default'
      
          def __new__(mcs, name, parents, _dict):
              if not (('Meta' in _dict) and hasattr(_dict['Meta'], 'abstract') and _dict['Meta'].abstract):
                  # class is concrete
                  if 'DEFAULT_TEXT' in _dict:
                      default = _dict['DEFAULT_TEXT']
                  else:       # Use inherited DEFAULT_TEXT if available
                      default_set = False
                      for cls in parents:
                          if hasattr(cls, 'DEFAULT_TEXT'):
                              default = cls.DEFAULT_TEXT
                              default_set = True
                      if not default_set:
                          default = mcs.DEFAULT_TEXT
                  _dict['modeltext'] = models.TextField(default=default)
              return super(DefaultTextFieldMetaclass, mcs).__new__(mcs, name, parents, _dict)
      
      
      class BaseTextFieldClass(models.Model):
          class Meta(object):
              abstract = True
          __metaclass__ = DefaultTextFieldMetaclass
          DEFAULT_TEXT = 'superclass default'
      
      
      class TextA(BaseTextFieldClass):
          DEFAULT_TEXT = 'A default for TextA'
      
      class TextB(BaseTextFieldClass):
          DEFAULT_TEXT = 'The default for TextB'
          number = models.IntegerField(default=43)
      
      class TextC(BaseTextFieldClass):
          othertext = models.TextField(default='some other field')
      

      除非您有一堆子类和/或多个方法/属性和与 BaseTextFieldClass 相关的假设,否则这可能有点矫枉过正......但它应该满足 OP 的要求。

      【讨论】:

        【解决方案4】:

        https://stackoverflow.com/a/6379556/15690:

        您实际上可以这样做:

        class BaseMessage(models.Model):
            is_public = models.BooleanField(default=False)
            # some more fields...
        
            class Meta:
                abstract = True
        
        BaseMessage._meta.get_field('is_public').default = True
        
        class Message(BaseMessage):
            # some fields...
        

        【讨论】:

        • 我想你的意思是Message._meta.get_field('is_public').default = True
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-11-06
        • 1970-01-01
        • 1970-01-01
        • 2015-12-30
        • 2016-06-30
        • 1970-01-01
        • 2013-02-03
        相关资源
        最近更新 更多