Django formset is not valid- why not?

2019-03-04 15:14发布

问题:

I am trying to use a form to allow users to upload images to projects stored in a database in my Django project, however, I'm currently getting console output that's telling me that the formset I'm using is not valid...

The view that I'm trying to use to upload the images to a project has been defined with:

def upload_budget_pdfs(request, project_id):
    project = Project.objects.get(id=project_id)
    print("Value of project in 'upload_budget_pdfs()': ", project)

    presentations = project.budget_versions.select_related('meeting').prefetch_related('budget_items', 'cci_items', 'presenters').filter(version_number__isnull=False).annotate(vn=F('version_number') * -1).order_by('presentation_date', 'created', '-vn')
    print("Value of presentations in 'upload_budget_pdfs()': ", presentations)
    drawing_formset = DrawingUploadFormset(prefix="drawings", queryset=Drawing.objects.filter(budget__in=presentations).order_by('budget__presentation_date', 'budget__created'))
    print("Value of drawing_formset in 'upload_budget_pdfs()': ", drawing_formset)

    if drawing_formset.is_valid():
        print 'Saving drawing_formset'
        print "Before", [b.id for b in project.budget_versions.all()]
        for drawing_form in drawing_formset:
            if drawing_form.instance.budget:
                print 'Instance', drawing_form.instance.budget
                drawing = drawing_form.save(commit=False)
                drawing.budget = drawing_form.instance.budget
                drawing.save()
            print drawing, [b.id for b in project.budget_versions.all()]
    else: print 'Drawing formset not valid.',drawing_formset.errors

    budget_formset = BudgetPresentationFormset(request.POST, request.FILES, instance=project, prefix="presentations")

    if budget_formset.is_valid() and budget_formset.has_changed():
        updated_budget_presentations = budget_formset.save()
    elif budget_formset.has_changed(): print 'Budget formset not valid.',budget_formset.errors



    return HttpResponseRedirect(reverse('projects:concept', args=[project_id]))

and the console output I'm getting when this view is called is:

("Value of project in 'upload_budget_pdfs()': ", < Project: Test 1 >)

("Value of presentations in 'upload_budget_pdfs()': ", [< Budget: Test 1: Version -1 >, < Budget: Test 1: Version -1 >, < Budget: Test 1: Version 0 >, < Budget: Test 1: Version 0 >, , , , , , , , , , , , , , , , , '...(remaining elements truncated)...'])

("Value of drawing_formset in 'upload_budget_pdfs()': ", < django.forms.formsets.DrawingFormFormSet object at 0x10cee3790 >)

Drawing formset not valid. []

So it's clear that the else statement in my view is what is being run here, but I'm not sure why, given that the console clearly indicates that the variables all hold the correct data...

What is wrong with the way I am initialising the drawing_formset variable here? Is the queryset I'm passing into DrawingUploadFormset(...) correct? What does is_valid() actually check?

Edit

I made the change in the accepted answer, so that my declaration of drawing_formset is now:

        drawing_formset = DrawingUploadFormset(request.POST, request.FILES, prefix="drawings", queryset=Drawing.objects.filter(budget__in=presentations).order_by('budget__presentation_date', 'budget__created'))

But it seems I'm now getting an 'Internal Server Error' and MultiValueDictKeyError on the line:

for drawing_form in drawing_formset: #This line is what's causing the MultiValueDictKeyError

I don't understand why this is? Any suggestions?

回答1:

Your formset does not contain any data. You need to pass request.POST and request.FILES as the first and second argument (or as the data and files keyword arguments), but only if it is an actual form submission.

If there is no data passed into a form or formset, it is considered unbound, and will return False without checking for errors.

The usual pattern is to pass them when request.method == 'POST', and then validate the formset:

def upload_budget_pdfs(request, project_id):
    ...
    if request.method == 'POST':
        drawing_formset = DrawingUploadFormset(request.POST, request.FILES, prefix='drawings', queryset=...)
        if drawing_formset.is_valid():
            # save formset
            return HttpResponseRedirect(...)
    else:
        drawing_formset = DrawingUploadFormset(prefix='drawings', queryset=...)
    return render(...)  # Render formset

This will show a blank form on GET, a filled-in form with error messages on an unsuccessful submission, and it will save the formset and redirect on a successful submission.