Как изменить аргументы поля в подклассах модели Django?
Скажем, у меня есть модель Django, которая является абстрактным базовым классом:
class Foo(models.Model):
value=models.IntegerField()
class Meta:
abstract = True
и он имеет два производных класса, где я хотел бы, чтобы значение по умолчанию для поля было различным для каждого дочернего класса. Я не могу просто переопределить поле
class Bar(Foo):
value=models.IntegerField(default=9)
потому что Django не позволит вам переопределять поля в подклассах. Я видел сообщения о попытках изменить доступные варианты, но в этом случае я больше всего беспокоюсь об изменении значения по умолчанию. Любые советы?
Ответы
Ответ 1
Проблема с переопределением метода сохранения, как предложено в другом ответе, заключается в том, что ваше значение не будет установлено до тех пор, пока вы не сохраните объект модели.
Другой вариант, который не имеет этой проблемы, заключается в переопределении __init__
в дочернем классе (класс Bar):
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)
чтобы избежать проблем с NULL с базой данных.
Ответ 3
См. fooobar.com/questions/90933/...:
Вы можете сделать это следующим образом:
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...
Ответ 4
Это определяет 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.