Django Generic Model Formsets

109 Views Asked by At

So I have a site where users are capable of registering devices and then registering commands to those devices. These commands have an associated widget (button, slider, etc) that determine the unique properties that the command has.

I am trying to figure out the most generic way to use the model formsets in my application. I have things working, where I create a model_formset for each ModelForm, and get data from each model to place in the formset, and then show it in my template.

What I would really like to do is something like this:

command_formset = modelformset_factory(Command, extra=0, fields='__all__')
formset = command_formset(queryset=Command.objects.filter(device_id=device_id))

Where I get all of my commands (which returns both buttons and sliders) and then make a single command_formset, which has all command objects.

In this case it does part of what I am looking for, where I can query my model and get all button and slider commands, but the formset only includes the fields from command - so the min and max value for the slider are lost.

Is something like this possible? Here is my more complete code:

Models

class Command(models.Model):
    command_name = models.CharField(max_length=100)
    command = models.CharField(max_length=100)
    command_url = models.CharField(max_length=100)
    command_type = models.CharField(max_length=100)
    device = models.ForeignKey(Device, on_delete=models.CASCADE, default=1)  # Each command has one device

    def __str__(self):
        return self.command_name


class ButtonCommand(Command):
    button_color = models.CharField()


class SliderCommand(Command):
    min_value = models.IntegerField()
    max_value = models.IntegerField()

Forms

class CommandForm(ModelForm):
    class Meta:
        model = Command
        fields = '__all__'

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in iter(self.fields):
            self.fields[field].widget.attrs.update({'class': 'form-control'})


class ButtonCommandForm(ModelForm):
    class Meta:
        model = ButtonCommand
        fields = '__all__'

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in iter(self.fields):
            self.fields[field].widget.attrs.update({'class': 'form-control'})


class SliderCommandForm(ModelForm):
    class Meta:
        model = SliderCommand
        fields = '__all__'

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in iter(self.fields):
            self.fields[field].widget.attrs.update({'class': 'form-control'})

View

command_formset = modelformset_factory(Command, extra=0, fields='__all__')
button_command_formset = modelformset_factory(ButtonCommand, form=ButtonCommandForm, extra=0,
                                              fields=('command_name', 'command', 'command_type', 'command_url'))
slider_command_formset = modelformset_factory(SliderCommand, extra=0,
                                              fields=('command_name', 'command', 'command_type', 'command_url',
                                                      'min_value', 'max_value'))

device_form = DeviceForm(initial=device)

formset = command_formset(queryset=Command.objects.filter(device_id=device_id))
button_formset = button_command_formset(queryset=ButtonCommand.objects.filter(device_id=device_id),
                                        prefix="button_form")
slider_formset = slider_command_formset(queryset=SliderCommand.objects.filter(device_id=device_id),
                                        prefix="slider_form")

Edit Additional View Code

template = 'device_control/device_management.html'
return TemplateResponse(request, template, {'device': device,
                                            'device_form': device_form,
                                            'button_formset': button_formset,
                                            'slider_formset': slider_formset,
                                            'formset' : formset})

Another Related Question

    input_type = request.POST['command_type']
    if input_type == 'button':
        form = ButtonCommandForm(request.POST)
    elif input_type == 'slider':
        form = SliderCommandForm(request.POST)
    else:
        form = None

Hopefully I am not overwhelming, but the above is a very similar question that seems much simpler. User posts a form, both of which inherit from Command. While the above is quite simple, if I eventually have 20+ different types of CommandForms, this will get fairly nasty.

I am really hoping that I am missing some way that Django can tell which child form should be used to build the form.

0

There are 0 best solutions below