NickleDave
NickleDave

Reputation: 372

"save and add another" in Django (not admin): submit then pre-populate one field of form

I have a form, "results", where one of the fields, "subjectID", is many-to-many because there's more than one result for each subject. I want one of the submit buttons to let me save what I've entered, then redirect to the same form, now unbound except that the many-to-many "subjectID" field stays the same so I can enter more results for that subject.

Edit: I should have made it clear that I wanted the instance that I had selected in the subjectID field to stay the same. I posted the code below that actually seems to be working for me

from models.py

 class ResultsForm(forms.Modelform):
     class Meta:
         model = models.Results
         fields = ['subjectID', # this is the field want
      # to populate the form with when I "save and add another"
              'slideNum', # IntegerField
              'resultType' ] # ForeignKey

from views.py

def addResults(request):
    if request.method == 'POST'
         form = ResultsForm(request.POST)
         if form.is_valid():
             form.save()
         if 'Save_and_add_another' in request.POST:
            subjectID = form.fields['subjectID']
            prepop = {'subjectID' : subjectID}
            form = ResultsForm(initial=prepop)
            return render(request, 'slideAdmin/addResults.html', {'form': form})
        elif 'Save_and_return' in request.POST:
            return HttpResponseRedirect('/home/')    
    else:
        form = ResultsForm()
    return render(request, 'slideAdmin/addResults.html', {'form': form})

Right now when I click on "save and add another" from my addResults form, I get this error:

TypeError at /slidebox/addResults

'ModelMultipleChoiceField' object is not iterable

which happens when rendering {{ form.as_p }} in the template.

Edit: Changes I made to views.py

    if 'Save_and_add_another' in request.POST:
        subjectID = form.cleaned_data.get('subjectID')
        form = ResultsForm(initial={'subjectID': subjectID})
        return render(request, 'slideAdmin/addResults.html', {'form': form})

As far as I can tell, this change works. Thanks again

Upvotes: 2

Views: 2009

Answers (2)

Brandon Taylor
Brandon Taylor

Reputation: 34553

You should always use form.cleaned_data.get('subjectID') versus pulling the field directly from the post data. You need to pass in a list of the pk's for the M2M field.

Your view can also use a touch of cleanup:

from django.core.urlresolvers import reverse


def addResults(request):
    form = ResultsForm(request.POST or None)

    if request.method == 'POST' and form.is_valid():
        form.save()

        if 'Save_and_add_another' in request.POST:
            subjectID = form.cleaned_data.get('subjectID', [])
            if subjectID:
                subjectID = subjectIDs.split(',')
            form = ResultsForm(initial={'subjectID': subjectID})

        elif 'Save_and_return' in request.POST:
            return HttpResponseRedirect(reverse('home'))  # don't hard code

    return render(request, 'slideAdmin/addResults.html', {'form': form})

Upvotes: 1

Luke
Luke

Reputation: 1

I'm not sure if you will be able to keep the form unbound when initialized.

Your form.fields is an ordered dict of django.forms.fields objects. You just want the ids, and not all the other info that comes across it.

Get the data straight from the POST dictionary.

subjectID = request.POST.get('subjectID', '')

If this is a true many to many model. You need to make sure the data is setup correctly for the initialization.

# We have to special-case M2Ms as a list of comma-separated PKs.
if isinstance(f, models.ManyToManyField):
    initial[k] = initial[k].split(",")

Here is the initialization method from the django source code for Admin (or as I call it my super detailed and complicated Django cheat sheet, I am pedantic)

def get_changeform_initial_data(self, request):
    """
    Get the initial form data.
    Unless overridden, this populates from the GET params.
    """
    initial = dict(request.GET.items())
    for k in initial:
        try:
            f = self.model._meta.get_field(k)
        except FieldDoesNotExist:
            continue
        # We have to special-case M2Ms as a list of comma-separated PKs.
        if isinstance(f, models.ManyToManyField):
            initial[k] = initial[k].split(",")
    return initial

Some PEP8 nonsense as well

classes are camel case ex: class MyAwesomeClass(object): everything else is lower with underscores. ex: awesome_id = awesome1245

Good Luck!!

Upvotes: 0

Related Questions