python - Django Exception Type: KeyError Exception Value: 'pk'


Keywords:python 


Question: 

I am probably doing something stupid but cant figure out what and it makes me insane since it is trivial and I have other apps working with same logic.

So I have a Model Customer and Model Notes. Each customer I can create a lot of Notes. In Notes Customer is defined as Foreign key .

view.py

@login_required
def note_new(request,pk):
    contact = get_object_or_404(Contacts, pk=pk)
    if request.method == "POST":
        form = NoteForm(request.POST)
        if form.is_valid():
            note = form.save(commit=False)
            note.pub_date = timezone.now()
            note.save()
            return redirect('contact_details',pk=contact.id)
    else:
        form = NoteForm(pk=contact.id)
    return render(request, 'customer/note_edit.html', {'form': form})

forms.py

class NoteForm(forms.ModelForm):
    class Meta:
        model = Note
        fields = [ 'title','body','contact' ]

    def __init__(self,*args,**kwargs):
        contact_id = kwargs.pop('pk')
        # self.fields['contact'].initial = contact_id
        super(NoteForm,self).__init__(*args,**kwargs)
        self.initial['contact'] = contact_id
        self.fields['contact'].widget.attrs['readonly'] = True

So form displays ok and after I click save I am getting

KeyError at /customer/note/new/9 'pk' Request Method: POST Request URL: Django Version: 1.8 Exception Type: KeyError Exception Value:
'pk' Exception Location: C:\Users\I812624\dev\mrp\src\customer\forms.py in __init__, line 48 Python Version: 2.7.1

It should redirect me to contact_details where contact info is displayed and it loops over all Notes.

contact_details.html

 {% for field in data.notes %}
    <tr>

       <td> {{ field }}</td>

    </tr>
    {% endfor %}

Any suggestions?

Thank you .


2 Answers: 

@Shang has already explained why you are getting the error and how to fix it, so I won't repeat that. My answer is to suggest a different approach in your view.

If you don't want the contact field to be editable, it is best to exclude it from the form.

class NoteForm(forms.ModelForm):
        class Meta:
            model = Note
            fields = [ 'title','body']

Then in your view, you can set the contact after saving with commit=False

    if form.is_valid():
        note = form.save(commit=False)
        note.pub_date = timezone.now()
        note.contact = contact
        note.save()
        return redirect('contact_details', pk=contact.id)

Now you shouldn't have to override your form's __init__ method at all.

If you want to display the contact in the template, then include it in the template context.

return render(request, 'customer/note_edit.html', {'form': form, 'contact': contact})

Then include {{ contact }} in the template.

 

This is because you try to do this in your form __init__:

contact_id = kwargs.pop('pk')

but in your views.py method you didn't pass it to the form constructor. Change your note_new method to pass the pk in form:

form = NoteForm(request.POST, pk=pk)

A safer way for avoid the exception is to use default parameter for pop:

contact_id = kwargs.pop('pk', None)
super(NoteForm,self).__init__(*args,**kwargs)
if contact_id:
    # do something if pk is passed, otherwise contact_id is None