Django - KeyError on formset form field


Keywords:django 


Question: 

So I'm getting a KeyError, every time I try to submit a formset.
I asked a similar question before here, and the solution seemed to work for a while, but now I get the above mentioned KeyError.
Code and Trace below:

Form:

class CodingForm(forms.Form):
    NATIONAL = 0
    REGIONAL = 1
    LOCAL = 2
    NA = 99
    SCOPE_CHOICES = (
        (NA, 'No Report'),
        (NATIONAL, 'National'),
        (REGIONAL, 'Regional'),
        (LOCAL, 'Local'),
    )

    NO_VIO = 0
    PROP_DMG = 1
    INJ = 2
    KILL = 3

    PART_VIO = (
        (NA, 'No Report'),
        (NO_VIO, 'No Violence'),
        (PROP_DMG, 'Property Damage'),
        (INJ, 'People Injured'),
        (KILL, 'People Killed'),
    )

    NO_PRES = 0
    PRES = 1
    INT = 2
    LETHAL_INT = 3

    SEC_ENG = (
        (NA, 'No Report'),
        (NO_PRES, 'No Presence'),
        (PRES, 'Presence'),
        (INT, 'Intervention'),
        (LETHAL_INT, 'Lethal Intervention'),
    )

    event_date = forms.DateField(required=False)
    location = GeonamesChoiceField(queryset=Geonames.objects.all(), required=False)
    actors = forms.CharField(max_length=100, required=False)
    num_participants = forms.CharField(max_length=200, required=False)  
    issue = forms.CharField(max_length=200, required=False)
    side = forms.NullBooleanField('Side')
    scope = forms.TypedChoiceField(choices=SCOPE_CHOICES, coerce=int, empty_value=None)
    part_violence = forms.TypedChoiceField(choices=PART_VIO, coerce=int, empty_value=None)
    sec_engagement = forms.TypedChoiceField(choices=SEC_ENG, coerce=int)
    relevance = forms.NullBooleanField('relevance')


    def clean(self):
            cleaned_data = self.cleaned_data
            event_date = cleaned_data.get("event_date")
            location = cleaned_data.get("location")

            if event_date and location:
                cleaned_data['relevance'] = True
                print cleaned_data["relevance"]
            else:
                cleaned_data['relevance'] = False

            return cleaned_data

View:

def assignment(request, pk):
"""View for each assignment"""
if request.user.is_authenticated():

    #### Get correct articles
    assignment = get_object_or_404(Assignment, pk=pk)
    country = assignment.country.cowcode
    start_date = assignment.start_date
    end_date = assignment.end_date
    articles = Article.objects.filter(cowcode=country).filter(pubdate__range=(start_date,end_date))

    #### Pagination ####
    paginator = Paginator(articles, 1)
    page = request.GET.get('page')
    try:
        articles = paginator.page(page)
    except PageNotAnInteger:
        articles = paginator.page(1)
    except EmptyPage:
        articles = paginator(page(paginator.num_pages))

    # Check if on first page and enable redirect
    if page is None:
        current_page = 1
    else:
        current_page = page
    redirect_to = "?page=%s" % current_page

    CodingFormSet = formset_factory(CodingForm, extra=0)
    formset = CodingFormSet(request.POST or None, prefix="coding_form")

    location_queryset = Geonames.objects.filter(cowcode=country).order_by('name')
    for form in formset.forms:
           form.fields['location'].queryset = location_queryset

    ##### Check if coder wants to go to next page or stay
    if "coding_form_next" in request.POST:
        process_form(formset, request, current_page, paginator)
        current_page = int(current_page) + 1
        redirect_to = "?page=%s" % current_page
        return HttpResponseRedirect(redirect_to)
    elif "coding_form_save" in request.POST:
        process_form(formset, request, current_page, paginator)
        redirect_to = "?page=%s" % current_page
        return HttpResponseRedirect(redirect_to)

else:
    print ERROR
return render(request, 'coding/assignment.html', 
{'articles':articles,'assignment':assignment,'formset':formset})

def process_form(formset, request, current_page, paginator):
if formset.is_valid():
    for form in formset.forms:
        form = form.cleaned_data

        if form['relevance'] == False:
            pass
        elif form['relevance'] == True:


            event_form = EventRecordForm()

            event = event_form.save(commit=False)
            event.article = paginator.page(current_page).object_list[0]
            event.coder = request.user
            event.last_updated = datetime.datetime.today()
            event.event_date = form["event_date"]
            event.location = form["location"]
            event.actors = form["actors"]
            event.num_participants = form["num_participants"]
            event.issue = form["issue"]
            event.side = form["side"]
            event.scope = form["scope"]
            event.part_violence = form["part_violence"]
            event.sec_engagement = form["sec_engagement"]
            event.save()

    ##### Add info on who worked on the article when
    history_form = ArticleHistoryForm()
    article_history = history_form.save(commit=False)
    article_history.article = paginator.page(current_page).object_list[0]
    article_history.coder = request.user
    article_history.last_updated = datetime.datetime.now()
    article_history.save()

Template:

<div id="coding">
<div id="coding-inner">
    {% with formset.empty_form as form %}
    <div id="empty_form" style="display:none">
        <table border="0" cellspacing="5" cellpadding="5">
            <tbody>
            <tr>
                <td>{{ form.event_date.label_tag}}</td>
                <td>{{ form.event_date}}</td>
            </tr>
            <tr>
                <td>{{ form.location.label_tag }}</td>
                <td><div class="location_wrapper">{{ form.location }}</div></td>
            </tr>
            <tr>
                <td>{{ form.actors.label_tag }}</td>
                <td>{{ form.actors }}</td>
            </tr>
            <tr>
                <td>{{ form.num_participants.label_tag }}</td>
                <td>{{ form.num_participants }}</td>
            </tr>
            <tr>
                <td>{{ form.issue.label_tag }}</td>
                <td>{{ form.issue }}</td>
            </tr>
            <tr>
                <td>{{ form.side.label_tag }}</td>
                <td>{{ form.side }}</td>
            </tr>
            <tr>
                <td>{{ form.scope.label_tag }}</td>
                <td>{{ form.scope }}</td>
            </tr>
            <tr>
                <td>{{ form.part_violence.label_tag}}</td>
                <td>{{ form.part_violence}}</td>
            </tr>
            <tr>
                <td>{{ form.sec_engagement.label_tag }}</td>
                <td>{{ form.sec_engagement }}</td>
            </tr>
            <tr>
                <td>{{ form.relevance.label_tag }}<td>
                <td>{{ form.relevance }}<td>
            </tr>
        </tbody>
        </table>
    </div>
    {% endwith %}
    <form action="" method="post" accept-charset="utf-8" id="form"> 
        {% csrf_token %}
        <div class="form-container"></div>
        {{ formset.management_form }}
        <div id="form-nav">

            <div id="save-stay">
                <input type="submit" name="coding_form_save" value="Save">
            </div>
            <div id="save-next">
                <input type="submit" name="coding_form_next" value="Save &#38; Next">
            </div>

        </div>
    </form>

</div>

And the Trace:

Environment:
Request Method: POST
Request URL: http://127.0.0.1:8000/coding/assignment/1/?page=1

Django Version: 1.5
Python Version: 2.7.2
Installed Applications:
('django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.sites',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'django.contrib.admin',
 'coding',
 'south')
Installed Middleware:
('django.middleware.common.CommonMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware')


Traceback:
File "/Users/lukaskawerau/.virtualenvs/MMAD/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  115.                         response = callback(request, *callback_args, **callback_kwargs)
File "/Users/lukaskawerau/Dropbox/Coding/mmad/app/mmad/coding/views.py" in assignment
  179.             process_form(formset, request, current_page, paginator)
File "/Users/lukaskawerau/Dropbox/Coding/mmad/app/mmad/coding/views.py" in process_form
  36.             if form['relevance'] == False:

Exception Type: KeyError at /coding/assignment/1/
Exception Value: 'relevance'

What am I doing wrong? I've tried everything I could think of, but nothing works.
Any help is hugely appreciated!

Update: I looked at local vars again, and notices that form is empty:

formset <django.forms.formsets.CodingFormFormSet object at 0x10eb9dd10>
form    {}

Why would that be?

Update 2: The problem was a different thing entirely: The JavaScript plugin I was using was messing with the HTML "name" tag of my inputs, so Django only saw an empty form.
I'm accepting @Francis answer, because it was the most detailed and still helpful. My bounty also shouldn't go to waste.
What do we learn from this? Check your JS.


3 Answers: 

I haven't set up this example in my test project, but something that strikes me as a possible problem is this:

relevance = forms.NullBooleanField('relevance')

The NullBooleanField allows NULL as one of the options. I wonder if the form is seeing a NULL/None and removing the key/value from the cleaned_data dict...

either way, I'd call the form's super's clean method and start from there...

def clean(self):
    #this line is kinda important
    cleaned_data = super(CodingForm, self).clean()

    event_date = cleaned_data.get("event_date")
    location = cleaned_data.get("location")

    if event_date and location:
        cleaned_data['relevance'] = True
        print cleaned_data["relevance"]
    else:
        cleaned_data['relevance'] = False

    return cleaned_data

additionally wrap calls to dict elements like this to prevent key errors

if 'relevance' in form:
    if form['relevance'] == False:
        ...
    elif form['relevance'] == True:
        ...

or

try: 
    if form['relevance'] == False:
        ...
    elif form['relevance'] == True:
        ...
except KeyError, e:
    ...
 

It should be form.fields['relevance'] in place of form['relevance'].

 

There's an error in your clean method. It should start with

cleaned_data = super(CodingForm, self).clean()

like the example in the docs.