djangoでMultiValueDictKeyError

あるview関数のテストをしていてMultiValueDictKeyErrorというものに遭遇したのでメモ

これで再現します。

# views.py
from django.views.generic import View
from braces.views import CsrfExemptMixin

class NotificationView(CsrfExemptMixin, View):
    http_method_names = ["post"]

    def post(self, request, *args, **kwargs):
        name = request.POST['name']
        return HttpResponse(name)

# urls.py
url(r'^notification/?$', NotificationView.as_view(), name="notification")

# tests.py
class NotificationViewTest(TestCase):

    def setUp(self):
        super(NotificationViewTest, self).setUp()
        self.data = {'name': 'brainstorm'}

    def test_ok(self):
        client = Client()
        response = client.post(reverse_lazy('notification'),
                               data=self.data,
                               content_type='application/x-www-form-urlencoded')
        self.assertEquals(response.content, 'brainstorm')

testを実施すると以下のエラーが発生します

MultiValueDictKeyError: 'Key \'name\' not found in <QueryDict: {u"{\'name\': \'brainstorm\'}": [u\'\']}>'

問題はcontent_typeを指定していながらdataが辞書であることです。
ちなみにcontent_typeを指定しないとデフォルトでmultipart/form-dataが使われます。

content_type='application/x-www-form-urlencoded'を指定する場合は、urlencodedな値を渡さないといけません。

from urllib import urlencode
...

class NotificationViewTest(TestCase):

    def setUp(self):
        super(NotificationViewTest, self).setUp()
        self.data = {'name': 'brainstorm'}

    def test_ok(self):
        client = Client()

        response = client.post(reverse_lazy('notification'),
                               data=urlencode(self.data),
                               content_type='application/x-www-form-urlencoded')
        self.assertEquals(response.content, 'brainstorm')

これでエラーを回避できるようになりました。

参考 : https://github.com/jgorset/django-respite/issues/38