Specifying different initial values for fields in inherited models (django)
- by Shawn Chin
Question : What is the recommended way to specify an initial value for fields if one uses model inheritance and each child model needs to have different default values when rendering a ModelForm?
Take for example the following models where CompileCommand and TestCommand both need different initial values when rendered as ModelForm.
# ------ models.py
class ShellCommand(models.Model):
command = models.Charfield(_("command"), max_length=100)
arguments = models.Charfield(_("arguments"), max_length=100)
class CompileCommand(ShellCommand):
# ... default command should be "make"
class TestCommand(ShellCommand):
# ... default: command = "make", arguments = "test"
I am aware that one can used the initial={...} argument when instantiating the form, however I would rather store the initial values within the context of the model (or at least within the associated ModelForm).
My current approach
What I'm doing at the moment is storing an initial value dict within Meta, and checking for it in my views.
# ----- forms.py
class CompileCommandForm(forms.ModelForm):
class Meta:
model = CompileCommand
initial_values = {"command":"make"}
class TestCommandForm(forms.ModelForm):
class Meta:
model = TestCommand
initial_values = {"command":"make", "arguments":"test"}
# ------ in views
FORM_LOOKUP = { "compile": CompileCommandFomr, "test": TestCommandForm }
CmdForm = FORM_LOOKUP.get(command_type, None)
# ...
initial = getattr(CmdForm, "initial_values", {})
form = CmdForm(initial=initial)
This feels too much like a hack. I am eager for a more generic / better way to achieve this. Suggestions appreciated.
Other attempts
I have toyed around with overriding the constructor for the submodels:
class CompileCommand(ShellCommand):
def __init__(self, *args, **kwargs):
kwargs.setdefault('command', "make")
super(CompileCommand, self).__init__(*args, **kwargs)
and this works when I try to create an object from the shell:
>>> c = CompileCommand(name="xyz")
>>> c.save()
<CompileCommand: 123>
>>> c.command
'make'
However, this does not set the default value when the associated ModelForm is rendered, which unfortunately is what I'm trying to achieve.
Update 2 (looks promising)
I now have the following in forms.py which allow me to set Meta.default_initial_values without needing extra code in views.
class ModelFormWithDefaults(forms.ModelForm):
def __init__(self, *args, **kwargs):
if hasattr(self.Meta, "default_initial_values"):
kwargs.setdefault("initial", self.Meta.default_initial_values)
super(ModelFormWithDefaults, self).__init__(*args, **kwargs)
class TestCommandForm(ModelFormWithDefaults):
class Meta:
model = TestCommand
default_initial_values = {"command":"make", "arguments":"test"}