Modelformset in django generic CreateView and UpdateView
Asked Answered
M

2

5

I have a model Organization with two fields 'id' and 'name'. I intend to populate it using dynamic model formsets.The code I have this far is as follows.

forms

class OrganizationForm(forms.ModelForm):
    class Meta:
        model = Organization
        fields = ('name',)

OrganizationFormset = modelformset_factory(Organization, form=OrganizationForm, fields=('name', ), extra=1)

views

class OrganizationCreate(CreateView):
    model = Organization
    form_class = OrganizationForm

    def get_context_data(self, **kwargs):
        context = super(OrganizationCreate, self).get_context_data(**kwargs)
        context['formset'] = OrganizationFormset()
        return context

    def post(self, request, *args, **kwargs):
        formset = OrganizationFormset(request.POST)
        if formset.is_valid():
            return self.form_valid(formset)

    def form_valid(self, formset):
        formset.save()
        return HttpResponseRedirect('/')

    def form_invalid(self, formset):
        return self.render_to_response(self.get_context_data(formset=formset))


class OrganizationUpdate(UpdateView):
    model = Organization
    form_class = OrganizationForm
    template_name_suffix = '_update_form'

    def get_context_data(self, **kwargs):
        context = super(OrganizationUpdate, self).get_context_data(**kwargs)
        context['formset'] = OrganizationFormset()
        return context

    def post(self, request, *args, **kwargs):
        formset = OrganizationFormset(request.POST)
        if formset.is_valid():
            return self.form_valid(formset)

    def form_valid(self, formset):
        formset.save()
        return HttpResponseRedirect('/')

    def form_invalid(self, formset):
        return self.render_to_response(self.get_context_data(formset=formset))

template create

<form id="myForm" method="post" action="">
            {% csrf_token %}
            {% for hidden in form.hidden_fields %} {{ hidden }} {% endfor %}
           {{ form }}
           <table border="0" cellpadding="0" cellspacing="0">
               <tbody>
                   {% for form in formset.forms %}
                   {% for hidden in form.hidden_fields %} {{ hidden }} {% endfor %}
                   <tr>
                      <td>
                         {% if form.instance.pk %}{{ form.DELETE }}{% endif %}

                         {{ form.field1 }}
                      </td>
                      <td>{{ form.name }}</td>
                   </tr>
                   {% endfor %}
               </tbody>
           </table>
           {{ formset.management_form }}
            <input type="submit" value="Create Location">
       </form>

update

<form id="myForm" method="post" action="">
            {% csrf_token %}

           <table border="0" cellpadding="0" cellspacing="0">
               <tbody>
                   {% for form in formset.forms %}
                   {% for hidden in form.hidden_fields %} {{ hidden }} {% endfor %}
                   <tr>
                      <td>
                         {% if form.instance.pk %}{{ form.DELETE }}{% endif %}

                         {{ form.field1 }}
                      </td>
                      <td>{{ form.name }}</td>
                   </tr>
                   {% endfor %}
               </tbody>
           </table>
           {{ formset.management_form }}
            <input type="submit" value="Update organizations">
       </form>

However, both the create template and the update template display pre-existing organizations while this should only be happening in the update template. Moreover, deleting pre-existing organizations in the update page returns the following error:

The view aims.views.OrganizationUpdate didn't return an HttpResponse object. It returned None instead.

I am new to django generic views and working with formsets. What I'm I doing wrong?

Thanks in advance.

Mut answered 19/4, 2015 at 6:23 Comment(0)
K
9

By default django model formsets will show any items that it can find from database. So you have to override the queryset parameter, when creating one, to let it know which items to use and when. For example, you should provide -

Organization.objects.none()

for your create view like this -

class OrganizationCreate(CreateView):
    ...

    def get_context_data(self, **kwargs):
        ...
        context['formset'] = OrganizationFormset(queryset=Organization.objects.none()) # providing none

    def post(self, request, *args, **kwargs):
        ...

    def form_valid(self, formset):
        ...

    def form_invalid(self, formset):
        ...

this will force the formset to render a blank form. Similarly change in update view to edit an specific item -

class OrganizationUpdate(UpdateView):
    ...

    def get_context_data(self, **kwargs):
        context = super(OrganizationUpdate, self).get_context_data(**kwargs)
        context['formset'] = OrganizationFormset(queryset=Organization.objects.get(pk=< get the pk from url when editing>))
        return context

    def post(self, request, *args, **kwargs):
        ...

    def form_valid(self, formset):
        ...

    def form_invalid(self, formset):
        ...

Hope this fixes your problem. If you still see the problem, then please update the question with details of urls and a possible screenshot. I will see what I can do.

Kayser answered 19/4, 2015 at 11:13 Comment(2)
Hi, how can I get the pk form the url?Remediless
That depends how you define your routes. If it is a route segment, then you can get it directly as a parameter to the respective class view method. for example - for a route like this for POST - ....user/edit/(?<pk>(\d+))/ I would use somthing like - def post(self, request, *args, pk = none, **kwargs): . Or, else you can always get it from request object.Kayser
C
3

Your post method returns nothing when formset is invalid in both Class based views (create and update), you should return form_invalid in else branch:

if formset.is_valid():
    return self.form_valid(formset)
else:
    return self.form_invalid(formset)
Curia answered 19/4, 2015 at 8:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.