thanksd
thanksd

Reputation: 55664

CSRF verification failure for single form with two models

Working with Django, I have a model Foo with a foreign key to a model Bar. When I create a Foo object on my site, I want to be able to set attributes of the Bar object in the same form.

I'm able to set the view and template up to use these two models in the same form. However, when I go to submit the form, I get a 403 Forbidden error saying:

CSRF token missing or incorrect.

I have a the {% csrf_token %} tag in my form, so I'm not sure how to fix this. Does anyone have any ideas?

In my views.py:

def foo_add(request):
    if request.method == "POST":
        fooForm = FooForm(request.POST, instance=Foo())
        barForm = BarForm(request.POST, instance=Bar())
        if fooForm.is_valid() and barForm.is_valid():
            foo = fooForm.save()
            bar = barForm.save()
            return HttpResponseRedirect('/foos/add')
    else:
        fooForm = FooForm(instance=Foo())
        barForm = BarForm(instance=Bar())
    return render_to_response(
        'foo_app/foo_add.html', 
        {'foo_form': fooForm, 'bar_form': barForm}
    )

foo_add.html:

{% extends "foo_app/__base.html" %}
{% load bootstrap3 %} 

{% block content %}

    <form action="" method="post">{% csrf_token %}
        {% bootstrap_form foo_form layout='inline' %}
        {% bootstrap_form var_form layout='inline' %}
        {% buttons %}
            <button type="submit" class="btn btn-primary">
                {% bootstrap_icon "star" %} Save Foo
            </button>
        {% endbuttons %}
    </form>

{% endblock %}

EDIT: I'm not asking how to create a form with two models. I'm pretty sure that the form is getting created correctly and information is getting passed as expected. I'm asking why I get the 403 error after I have filled out the form and submitted it.

Here is what the generated HTML looks like:

<form action="" method="post">
  <div class="form-group">
    <label class="sr-only" for="id_foo-a">A</label>
    <input class="form-control" id="id_foo-a" name="foo-a" 
           placeholder="A" required="required" title="" type="text" />
  </div>
  <div class="form-group">
    <label class="sr-only" for="id_bar-b">B</label>
    <input class="form-control" id="id_bar-b" min="0" name="bar-b" 
           placeholder="B" required="required" title="" type="number" />
  </div>
  <div class="form-group">
    <button type="submit" class="btn btn-primary">
      <span class="glyphicon glyphicon-star"></span> Save Foo
    </button>
  </div>
</form>

Upvotes: 0

Views: 49

Answers (1)

Alasdair
Alasdair

Reputation: 309069

Use the render shortcut instead of render_to_response. This ensures that the template is rendered with the request, which allows the csrf_token tag to work.

from django.shortcuts import render

def foo_add(request):
    ...
    return render(
        request,
        'foo_app/foo_add.html', 
        {'foo_form': fooForm, 'bar_form': barForm},
    )

Upvotes: 1

Related Questions