AlexW
AlexW

Reputation: 2587

Django Crispy forms - inline Formsets 'ManagementForm data' error with update view

Good Evening,

Im having trouble with a crispy forms inlineformset. I have followed guides as per:

https://github.com/timhughes/django-cbv-inline-formset/blob/master/music/views.py https://django-crispy-forms.readthedocs.io/en/latest/crispy_tag_formsets.html#formsets

EDIT I think the issue is something to do with the dual submit buttons. the devicemodel form has a button that when pressed produces this error. but there is also a save button as part of the resource helper, when that's submitted I get an empty model form error.

I've added screenshots of what happens when you action each button

Error when resource save button pressed

error when update device button is pressed

and I must be missing something as am getting the error:

['ManagementForm data is missing or has been tampered with']

here is my update view:

class EditDeviceModel(PermissionRequiredMixin, SuccessMessageMixin, UpdateView):
    model = DeviceModel
    form_class = DeviceModelForm

    template_name = "app_settings/base_formset.html"
    permission_required = 'config.change_devicemodel'
    success_message = 'Device  Type "%(model)s" saved successfully'

    def get_success_url(self, **kwargs):         
        return '{}#device_models'.format(reverse("config:config_settings"))

    def get_success_message(self, cleaned_data):
        return self.success_message % dict(
            cleaned_data,
            model=self.object.model,
        )     

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title']='Edit Device Model'
        if self.request.POST:
            context['formset'] = DeviceFormSet(self.request.POST, instance=self.object)
        else:
            context['formset'] = DeviceFormSet(instance=self.object)
        context['helper'] = DeviceFormSetHelper()
        return context

    def form_valid(self, form):
        context = self.get_context_data()
        formset = context['formset']
        if formset.is_valid():
            self.object = form.save()
            formset.instance = self.object
            formset.save()
            return redirect(self.success_url)
        else:
            return self.render_to_response(self.get_context_data(form=form)) 

Here are my forms:

class MonitoredResourceForm(forms.ModelForm):
    class Meta:
        model = MonitoredResource
        fields = ['resource','model']

    def __init__(self, *args, **kwargs):
        self.is_add = kwargs.pop("is_add", False)
        super(MonitoredResourceForm, self).__init__(*args, **kwargs)
        self.helper = FormHelper(self)
        self.helper.form_id = 'snmp_resource_form'
        self.helper.form_method = 'POST'
        self.helper.layout = Layout(
            Div(    
                Div(
                    Field('model'),
                    Field('resource', placeholder="Resource"),
                    css_class='col-lg-3'
                ),
            css_class='row'
            ),
            Div(
                Div(
                    HTML("""<input type="submit" name="submit" value="""),
                    HTML('"Add' if self.is_add else '"Update' ),
                    HTML(""" monitored resource" class="btn btn-primary"/>"""),
                    HTML("""<a href="{% url 'config:config_settings' %}#monitored_resources" class="btn btn-primary">Cancel</a>"""),
                    HTML("""{% if object %}
                            <a href="{% url 'config:delete_monitoredresource' object.id %}"
                            class="btn btn-danger">
                            Delete <i class="fa fa-trash-o" aria-hidden="true"></i></a>
                            {% endif %}"""),
                    css_class='col-lg-12'
                    ),
                css_class='row'
                ),
        ) 

class DeviceModelForm(forms.ModelForm):
    class Meta:
        model = DeviceModel
        fields = ['model','vendor','device_type','ports','uplink_speed']

    def __init__(self, *args, **kwargs):
        self.is_add = kwargs.pop("is_add", False)
        super(DeviceModelForm, self).__init__(*args, **kwargs)
        self.helper = FormHelper(self)
        self.helper.form_id = 'device_type_form'
        self.helper.form_method = 'POST'
        self.helper.layout = Layout(
            Div(    
                Div(
                    Field('model', placeholder="Model"),
                    Field('vendor',),
                    Field('device_type',),
                    Field('ports', placeholder="Ports"),
                    Field('uplink_speed', placeholder="Uplink Speed"),
                    css_class='col-lg-6'
                ),
            css_class='row'
            ),
            Div(
                Div(
                    HTML("""<input type="submit" name="submit" value="""),
                    HTML('"Add' if self.is_add else '"Update' ),
                    HTML(""" Device Model" class="btn btn-primary"/>"""),
                    HTML("""<a href="{% url 'config:config_settings' %}#device_models" class="btn btn-primary">Cancel</a>"""),
                    HTML("""{% if object %}
                            <a href="{% url 'config:delete_device_model' object.id %}"
                            class="btn btn-danger">
                            Delete <i class="fa fa-trash-o" aria-hidden="true"></i></a>
                            {% endif %}"""),
                    css_class='col-lg-12'
                    ),
                css_class='row'
                ),
        )

DeviceFormSet = inlineformset_factory(DeviceModel, MonitoredResource, form=MonitoredResourceForm, extra=1)

class DeviceFormSetHelper(FormHelper):
    def __init__(self, *args, **kwargs):
        super(DeviceFormSetHelper, self).__init__(*args, **kwargs)
        self.form_method = 'post'
        self.render_required_fields = True
        self.form_id = 'snmp_resource_form'
        self.form_method = 'POST'
        self.add_input(Submit("submit", "Save"))
        self.layout = Layout(
            Div(    
                Div(
                    Field('model'),
                    Field('resource', placeholder="Resource"),
                    css_class='col-lg-6'
                ),
            css_class='row'
            ),
        ) 

and in the templates I render:

{% block content %} 
{% include "home/form_errors.html" %}
<div class="col-lg-6">
    {% crispy form %}
</div>
<div class="col-lg-6">
    {% crispy formset helper %}
</div>
<!-- /.row -->
{% endblock %}

is anyone able to see what im missing?

Upvotes: 4

Views: 2786

Answers (4)

Austin not from Boston
Austin not from Boston

Reputation: 132

I had the same problem and found the answer in the documentation:

{{ formset.management_form|crispy }}
{% for form in formset %}
    {% crispy form %}
{% endfor %}

Upvotes: 0

rchurch4
rchurch4

Reputation: 899

Your problem is that each form in a formset has its own management_form. I haven't dealt with this specifically in crispy, but in the general formsets, that was the problem that I had. You have to manually spell out each piece of the formset, either by iteration or hardcoding, and make sure that each has its management_form.

Upvotes: 0

Ashfaque Ali Solangi
Ashfaque Ali Solangi

Reputation: 1891

I think you have to render management form in your template, explained here why you need that

Management Form is used by the formset to manage the collection of forms contained in the formset. If you don’t provide this management data, an exception will be raised

add this in view html

{{ DeviceFormSet.management_form }}

Upvotes: 1

Rakon_188
Rakon_188

Reputation: 573

You are missing a tag and also {{format.management_form|crispy}} I guess

Upvotes: 0

Related Questions