假设我有一些 Django 模型,它是一个抽象基类:
class Foo(models.Model):
value=models.IntegerField()
class Meta:
abstract = True
它有两个派生类,我希望每个子类的字段默认值都不同。我不能简单地覆盖该字段
class Bar(Foo):
value=models.IntegerField(default=9)
因为 Django 不会让你覆盖子类中的字段。我看过有关尝试更改可用选项的帖子,但在这种情况下,我最关心的是更改默认值。有什么建议吗?
按照另一个答案中的建议重新定义保存方法的问题是,在保存模型对象之前,不会设置您的值。 不存在此问题的另一个选择是在子类(Bar 类)中重新定义
__init__
:
def __init__(self, *args, **kwargs):
if 'value' not in kwargs:
kwargs['value'] = 9
super(Bar, self).__init__(*args, **kwargs)
参见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...
我认为你想要做的事情是不可能的,至少在 Django 中是不可能的。您必须将 Django 中的继承视为超类的外键(仅此而已),并且您无法更改 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 问题。
这定义了一个元类(和一个基类),为所有子类提供给定类型的字段,默认值可以在子类中设置,但不必是:
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 的要求进行操作。
这可能是一个较新的功能,但在 Django
4.2.4
中,我可以重新定义该字段,Django 会在迁移文件中正确地重新创建它。
class Foo(models.Model):
value=models.IntegerField()
class Meta:
abstract = True
class Bar(Foo):
value=models.IntegerField(default=9)