Opened 13 months ago

Closed 13 months ago

Last modified 13 months ago

#28218 closed Bug (duplicate)

KeyError 'content-type' http/response.py when printing response

Reported by: Denise Mauldin Owned by: nobody
Component: HTTP handling Version: 1.10
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Hi all,

I'm using Django Rest Framework for my API. I'm testing account creation using my API.

from django.urls import reverse
from django.contrib.auth import get_user_model
from rest_framework import status
from rest_framework.request import Request
from rest_framework.test import APITestCase, APIClient, APIRequestFactory
from rest_framework.authtoken.models import Token

class AccountCreationTests(APITestCase):
    """
    This is the account creation tests for the /api/users endpoint on angular
    """
    fixtures = ['user_dev', 'order_dev']

    def setUp(self):
        self.client = APIClient()
        self.factory = APIRequestFactory()

    def test_create_account_fails(self):
        """
        Ensure we can't create a new account object without required keys on angular
        """
        url = reverse('user-list')
        data = {'email': 'test@example.com'}
        # fails because of required fields
        response = self.client.post(url, data, format='json')
        print("\n\ntest_create_account_fails response {}\n\n".format(response.__dict__))
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST,
                         "Creating a user without required fields is a bad request")

    def test_create_account(self):
        """ Creating an account only works if there is no request.user and all required fields provided """
        starting_user_count = get_user_model().objects.count()
        url = reverse('user-list')
        data = {'email': 'test@example.com', 'first_name': 'John', 'last_name': 'Doe',
                'phone': '2065551234', 'institution': 'Test', 'groupLead': 'John',
                'password': 'testpassword'}
        response = self.client.post(url, data, format='json')
        print("\n\ntest_create_account response {}\n\n".format(response.__dict__))
        self.assertEqual(response.status_code, status.HTTP_201_CREATED,
                         "User API POST unsuccessful {}".format(response.data))
        self.assertEqual(get_user_model().objects.count(), starting_user_count + 1,
                         "One user was created")
        self.assertEqual(get_user_model().objects.get(email=data['email']).email, 'test@example.com',
                         "Email exists in database {}".format(
                             get_user_model().objects.filter(email=data['email']).__dict__)

The test_create_account_fails method works and returns an expected failure. The test_create_account fails because http/response.py line 152 can't find the 'headers' key. I'm not sure why....

Traceback (most recent call last):
  File "/var/www/ann/ann/users/tests/test_account_creation.py", line 41, in test_create_account
    print("\n\ntest_create_account response {}\n\n".format(response))
  File "/home/vagrant/.virtualenvs/ann/lib/python3.5/site-packages/django/http/response.py", line 299, in __repr__
    'content_type': self['Content-Type'],
  File "/home/vagrant/.virtualenvs/ann/lib/python3.5/site-packages/django/http/response.py", line 152, in __getitem__
    return self._headers[header.lower()][1]
KeyError: 'content-type'

If I patch the code to:

    def __getitem__(self, header):
        if self._headers.get(header.lower()):
            return self._headers[header.lower()][1]
        return self._headers.get(header.lower())

then test_create_account returns the following object during a print:

test_create_account response {'resolver_match': <SimpleLazyObject: <function Client.request.<locals>.<lambda> at 0x7f8399ed4400>>, 'using': None, 'template_name': None, 'context': [[{'False': False, 'None': None, 'True': True}, {'activate_url': 'http://testserver/accounts/confirm-email/MTU:1dB3Lm:43Wje1L2IirjhfKIBf_uzsUclZQ/', 'key': 'MTU:1dB3Lm:43Wje1L2IirjhfKIBf_uzsUclZQ', 'user': <User: John Doe>, 'current_site': <Site: ann.test.org>}], [{'False': False, 'None': None, 'True': True}, {'activate_url': 'http://testserver/accounts/confirm-email/MTU:1dB3Lm:43Wje1L2IirjhfKIBf_uzsUclZQ/', 'key': 'MTU:1dB3Lm:43Wje1L2IirjhfKIBf_uzsUclZQ', 'user': <User: John Doe>, 'current_site': <Site: ann.test.org>}, {}], [{'False': False, 'None': None, 'True': True}, {'activate_url': 'http://testserver/accounts/confirm-email/MTU:1dB3Lm:43Wje1L2IirjhfKIBf_uzsUclZQ/', 'key': 'MTU:1dB3Lm:43Wje1L2IirjhfKIBf_uzsUclZQ', 'user': <User: John Doe>, 'current_site': <Site: ann.test.org>}], [{'False': False, 'None': None, 'True': True}, {'activate_url': 'http://testserver/accounts/confirm-email/MTU:1dB3Lm:43Wje1L2IirjhfKIBf_uzsUclZQ/', 'key': 'MTU:1dB3Lm:43Wje1L2IirjhfKIBf_uzsUclZQ', 'user': <User: John Doe>, 'current_site': <Site: ann.test.org>}, {'user_display': 'John Doe'}]], '_dont_enforce_csrf_checks': True, '_post_render_callbacks': [], '_is_rendered': True, 'client': <rest_framework.test.APIClient object at 0x7f83997a2470>, 'cookies': <SimpleCookie: csrftoken='HPkblAYrdPfbJs5c2OAc7vH0k89wuj1n7elaPP8VZhvaB7CcouQxjy6bV47s3jgR' sessionid='f0ukkwdn00jyt1tx52r86c9fax1ut2yv'>, 'context_data': None, 'templates': [<django.template.base.Template object at 0x7f8399a57e10>, <django.template.base.Template object at 0x7f8399adef98>, <django.template.base.Template object at 0x7f8399ade748>, <django.template.base.Template object at 0x7f8399adef28>], 'json

test_create_account_fails returns this object:

test_create_account_fails response {'context_data': None, 'template_name': None, 'templates': [], 'resolver_match': <SimpleLazyObject: <function Client.request.<locals>.<lambda> at 0x7fd5038941e0>>, 'closed': True, '_is_rendered': True, 'status_code': 400, '_post_render_callbacks': [], '_charset': None, '_reason_phrase': None, '_request': None, '_headers': {'content-type': ('Content-Type', 'application/json'), 'allow': ('Allow', 'GET, POST, OPTIONS'), 'vary': ('Vary', 'Cookie'), 'x-frame-options': ('X-Frame-Options', 'SAMEORIGIN')}, '_handler_class': None, 'using': None, 'exception': True, 'data': {'institution': ['This field is required.'], 'phone': ['This field is required.'], 'group_lead': ['This field is required.'], 'password': ['This field is required.'], 'first_name': ['This field is required.'], 'last_name': ['This field is required.']}, 'accepted_renderer': <djangorestframework_camel_case.render.CamelCaseJSONRenderer object at 0x7fd50356cb00>, 'content_type': None, 'accepted_media_type': 'application/json', 'client': <rest_framework.test.APIClient object at 0x7fd50356c630>, 'wsgi_request': <WSGIRequest: POST '/api/users'>, 'context': None, '_closable_objects': [<WSGIRequest: POST '/api/users'>], 'renderer_context': {'view': <ann.users.apiviews.UserViewSet object at 0x7fd50356c978>, 'args': (), 'response': <Response status_code=400, "application/json">, 'request': <rest_framework.request.Request object at 0x7fd50356ca20>, 'kwargs': {}}, '_container': [b'{"institution":["This field is required."],"phone":["This field is required."],"groupLead":["This field is required."],"password":["This field is required."],"firstName":["This field is required."],"lastName":["This field is required."]}'], 'cookies': <SimpleCookie: >, 'json': <function curry.<locals>._curried at 0x7fd503894510>, 'request': {'REQUEST_METHOD': 'POST', 'CONTENT_LENGTH': 28, 'wsgi.url_scheme': 'http', 'QUERY_STRING': '', 'wsgi.input': <django.test.client.FakePayload object at 0x7fd5034d9908>, 'SERVER_PORT': '80', 'CONTENT_TYPE': 'application/json; charset=None', 'PATH_INFO': '/api/users'}, '_dont_enforce_csrf_checks': True}

Maybe this is a bug with Django Rest Framework or CamelCaseJSONParser, but I'd expect http/response to have a more robust getter?

Change History (3)

comment:1 Changed 13 months ago by Tim Graham

Resolution: duplicate
Status: newclosed

Duplicate of #27640, fixed in Django 1.11.

comment:2 in reply to:  1 Changed 13 months ago by Denise Mauldin

Replying to Tim Graham:

Duplicate of #27640, fixed in Django 1.11.

Except that upgrading to 1.11 from 1.10 is a pain. No plans to fix in 1.10?

comment:3 Changed 13 months ago by Tim Graham

No, it doesn't qualify for a backport. Per our supported versions policy, 1.10 is only receiving security fixes.

Note: See TracTickets for help on using tickets.
Back to Top