Martin Emerson
Martin Emerson

Reputation: 93

Django Model Formset Data Always Persists

I feel like I must be missing something obvious but I am having a problem where my model formsets insist on retaining their data after submission. I am creating a page that allows a user to create a project and then add an arbitrary amount of materials to that project. JavaScript is taking care of dynamically adding new instances of the formset as needed. The code works fine the first time through, after that it "remembers" previous data. It happens for the material formset but not for the regular model form above it.

I'm thinking it must have something to do with the way I am creating my model formset. When the page is requested the view seems to be passing back the formset bound to old data rather than an unbound set. I'm new to Django and am trying to teach myself so there are probably things at work I do not quite grasp yet. Below is the code for the view:

def addproject_page(request):

# Define the formset to use to add the materials
MaterialFormSet = modelformset_factory(Material, exclude = ('project',))

# Check to see if there is some POST data from an attempt to fill out the form
if request.method == 'POST':

    # Create a form for the project and for the material and use a prefix to separate the POST data for the two
    project_form = ProjectForm(request.POST, prefix='project')
    # Instantiate the formset to display multiple materials when creating a project
    material_formset = MaterialFormSet(request.POST, prefix='material')

    # Check both forms with the validators and if both are good process the data
    if project_form.is_valid() and material_formset.is_valid():
        # Save the data for the newly created project
        created_project = project_form.save()
        # Tell each material to be associated with the above created project
        instances = material_formset.save(commit=False)
        for instance in instances:
            instance.project = created_project
            instance.save()

        # After the new project and its materials are created, go back to the main project page
        return HttpResponseRedirect('/members/projects/')

# If there is no post data, create and show the blank forms
else:
    project_form = ProjectForm(prefix='project')
    material_formset = MaterialFormSet(prefix='material')
return render(request, 'goaltracker/addproject.html', {
    'project_form': project_form,
    'material_formset': material_formset,
})

Edit to add in my template code too in case it helps:

{% extends "base.html" %}

{% block external %}
<script src="{{ static_path }}js/projects.js" type="text/javascript"></script>
{% endblock %}

{% block title %}: Add Project{% endblock %}

{% block content %}

<h1>Add a Project</h1> 

<form id="new_project_form" method="post" action="">
{{ project_form.as_p }}

<!--  The management form must be rendered first when iterating manually -->
<div>{{ material_formset.management_form }}</div>
<!--  Show the initial blank form(s) before offering the option to add more via JavaScript -->
{% for material_form in material_formset.forms %}
    <div>{{ material_form.as_p }}</div>
{% endfor %}

<input type="button" value="Add Material" id="add_material">
<input type="button" value="Remove Material" id="remove_material">
<input type="submit" value="add" />
</form>

{% endblock %}

Upvotes: 3

Views: 936

Answers (2)

Sarfraz Ahmad
Sarfraz Ahmad

Reputation: 1439

Answer of the question the old data is always persists in modelformset is here. https://docs.djangoproject.com/en/1.8/topics/forms/modelforms/#changing-the-queryset as it is given in the docs chenge the queryset by overriding the constructor of the basemodelformset

from django.forms.models import BaseModelFormSet

from myapp.models import Author

class CalendarFormset(BaseModelFormSet):
    def __init__(self, *args, **kwargs):
        super(CalendarFormset, self).__init__(*args, **kwargs)
        self.queryset = Calendar.objects.none()

A same problem was discussed here
django modelformset_factory sustains the previously submitted data even after successfully created the objects

Upvotes: 0

Alasdair
Alasdair

Reputation: 308849

I think you need to use a custom queryset, so that your formset is instantiated with an empty queryset. You need to specify the queryset in the POST and GET branches of your if statement.

if request.method == "POST":
    ...
    material_formset = MaterialFormSet(request.POST, prefix='material', queryset=Material.objects.none())
    ...
else:
    material_formset = MaterialFormSet(prefix='material', queryset=Material.objects.none())

At the moment, your formset is using the default queryset, which contains all objects in the model.

Upvotes: 3

Related Questions