Let's say I have some Django model that is an abstract base class:
class Foo(models.Model):
value=models.IntegerField()
class Meta:
abstract = True
and it has two derived classes, where I'd like the default value of the field to be different for each child classes. I can't simply override the field
class Bar(Foo):
value=models.IntegerField(default=9)
because Django won't let you override fields in subclasses. I've seen posts about trying to changes available choices, but in this case I care mostly about changing the default value. Any advice?
The problem with redefining the save method as suggested in the other answer is that your value will not be set until you save your model object.
Another option that doesn't have this problem is to redefine the __init__
in the child class (Bar class):
def __init__(self, *args, **kwargs):
if 'value' not in kwargs:
kwargs['value'] = 9
super(Bar, self).__init__(*args, **kwargs)
I think that what you're trying to do is not possible, at least not in Django. You have to see inheritance in Django as a ForeignKey to the super-class (that's pretty much it), and you can't change the default value of an attribute in a FK relation.
So, the best thing you could do is to redefine the save()
method. It would be something like:
def save(self, *args, **kwargs):
if not self.value:
self.value = 9
super(Bar, self).save(*args, **kwargs)
And, in Foo (super-class):
value = models.IntegerField(blank=True)
to avoid NOT NULL problems with the database.
See https://stackoverflow.com/a/6379556/15690:
You can actually do this as follows:
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...
This defines a metaclass (and a base class) that provides all subclasses a field of a given type, with defaults that can be set in the subclass, but don't have to be:
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')
Unless you have a bunch of subclasses and/or multiple methods/attributes and assumptions that tie into the BaseTextFieldClass, this is probably overkill... but it should do what OP requested.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With