Shawn Chin
Shawn Chin

Reputation: 86944

Specifying initial field values for ModelForm associated with inherited models

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.

Updated solution (looks promising)

I now have the following in forms.py which allow me to set Meta.default_initial_values without needing extra boilerplate code in views. Default values are used if user does not specify initial={...} args.

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"}

Upvotes: 2

Views: 1358

Answers (1)

Tommaso Barbugli
Tommaso Barbugli

Reputation: 12031

I don't see that much use in setting initial_values on form's meta if you then have to send to the form init.

I would rather create a subclass of ModelForm that overrides the constructor method and then use that subclass as parent class of the other forms.

e.g.

class InitialModelForm(forms.ModelForm):
    #here you override the constructor
    pass

class TestCommandForm(InitialModelForm):
    #form meta

class CompileCommandForm(InitialModelForm):
    #form meta

Upvotes: 1

Related Questions