405 error when testing an authed django-rest-framework route
Question:
I’m testing a CreateAPIView
with an APITestCase
class. Things are working as expected as an anonymous user, but when I login() as a user, I get a 405 HttpResponseNotAllowed
exception. I’m able to successfully create an object while authed as a user through the django-rest-framework web frontend. I’m using djangorestframework version 3.9.4 and Django 1.11.29.
Here are the main parts of the code, for a general idea of what I’m doing:
class SherdNoteCreate(CreateAPIView):
serializer_class = SherdNoteSerializer
def post(self, request, *args, **kwargs):
data = request.data.copy()
data['asset'] = kwargs.get('asset_id')
serializer = SherdNoteSerializer(data=data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class SherdNoteTests(APITestCase):
def test_create_sherdnote_on_own_asset(self):
# Removing this auth block allows the test to pass
self.u = UserFactory(username='test_user')
self.u.set_password('test')
self.u.save()
login = self.client.login(username='test_user', password='test')
assert(login is True)
asset = AssetFactory(primary_source='image', author=self.u)
url = reverse('sherdnote-create', kwargs={'asset_id': asset.pk})
data = {
'title': 'note title',
'body': 'note body'
}
response = self.client.post(url, data, format='json')
# This fails with a 405!
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
The route in urls.py:
url(r'^(?P<asset_id>d+)/sherdnote/create/$',
SherdNoteCreate.as_view(),
name='sherdnote-create'),
Here’s the response
and response.__dict__
printed out, from the test above:
<HttpResponseNotAllowed [GET, HEAD, OPTIONS] status_code=405, "text/html; charset=utf-8">
{'_headers': {'content-type': ('Content-Type', 'text/html; charset=utf-8'), 'allow': ('Allow', 'GET, HEAD, OPTIONS'), 'vary': ('Va
ry', 'Origin, Cookie'), 'x-frame-options': ('X-Frame-Options', 'SAMEORIGIN'), 'content-length': ('Content-Length', '0')}, '_closab
le_objects': [<WSGIRequest: POST '/asset/1/sherdnote/create/'>], '_handler_class': None, 'cookies': <SimpleCookie: >, 'closed': Tr
ue, '_reason_phrase': None, '_charset': None, '_container': [b''], 'wsgi_request': <WSGIRequest: POST '/asset/1/sherdnote/create/'
>, 'client': <rest_framework.test.APIClient object at 0x7f9880902f10>, 'request': {'PATH_INFO': '/asset/1/sherdnote/create/', 'REQ
UEST_METHOD': 'POST', 'SERVER_PORT': '80', 'wsgi.url_scheme': 'http', 'CONTENT_LENGTH': 41, 'CONTENT_TYPE': 'application/json; cha
rset=None', 'wsgi.input': <django.test.client.FakePayload object at 0x7f987e834790>, 'QUERY_STRING': ''}, 'templates': [], 'contex
t': None, 'json': <function curry.<locals>._curried at 0x7f988018e440>, 'resolver_match': <SimpleLazyObject: <function Client.requ
est.<locals>.<lambda> at 0x7f988018eef0>>, '_dont_enforce_csrf_checks': True}
I’ve been having trouble tracking down why this is happening. Does anyone have any ideas?
Answers:
Okay, well there was some course middleware in my application that was interfering with my API requests. The solution was to make a sample course and make my test user a student in that course before making the request.
For anyone else that ended up here like me looking for answers. I was upgrading my Django/graphene stack from Django 2.2 -> 3.2 and all the latest graphene-related things. The 405 for me was coming from a URL that didn’t quite line up because of a lack of slashes to close out the URL;
I couldn’t make my tests work because the graphene 3.0 default testing endpoint is set to '/graphql'
, where my url pattern in django was
path(
"graphql/",
csrf_exempt(
ConsistentErrorsGraphQLView.as_view(graphiql=settings.USE_GRAPHIQL)
),
),
to fix it, I set my graphene testing endpoint in my django settings:
GRAPHENE = {
...
"TESTING_ENDPOINT": "/graphql/"
...
}
I’m testing a CreateAPIView
with an APITestCase
class. Things are working as expected as an anonymous user, but when I login() as a user, I get a 405 HttpResponseNotAllowed
exception. I’m able to successfully create an object while authed as a user through the django-rest-framework web frontend. I’m using djangorestframework version 3.9.4 and Django 1.11.29.
Here are the main parts of the code, for a general idea of what I’m doing:
class SherdNoteCreate(CreateAPIView):
serializer_class = SherdNoteSerializer
def post(self, request, *args, **kwargs):
data = request.data.copy()
data['asset'] = kwargs.get('asset_id')
serializer = SherdNoteSerializer(data=data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class SherdNoteTests(APITestCase):
def test_create_sherdnote_on_own_asset(self):
# Removing this auth block allows the test to pass
self.u = UserFactory(username='test_user')
self.u.set_password('test')
self.u.save()
login = self.client.login(username='test_user', password='test')
assert(login is True)
asset = AssetFactory(primary_source='image', author=self.u)
url = reverse('sherdnote-create', kwargs={'asset_id': asset.pk})
data = {
'title': 'note title',
'body': 'note body'
}
response = self.client.post(url, data, format='json')
# This fails with a 405!
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
The route in urls.py:
url(r'^(?P<asset_id>d+)/sherdnote/create/$',
SherdNoteCreate.as_view(),
name='sherdnote-create'),
Here’s the response
and response.__dict__
printed out, from the test above:
<HttpResponseNotAllowed [GET, HEAD, OPTIONS] status_code=405, "text/html; charset=utf-8">
{'_headers': {'content-type': ('Content-Type', 'text/html; charset=utf-8'), 'allow': ('Allow', 'GET, HEAD, OPTIONS'), 'vary': ('Va
ry', 'Origin, Cookie'), 'x-frame-options': ('X-Frame-Options', 'SAMEORIGIN'), 'content-length': ('Content-Length', '0')}, '_closab
le_objects': [<WSGIRequest: POST '/asset/1/sherdnote/create/'>], '_handler_class': None, 'cookies': <SimpleCookie: >, 'closed': Tr
ue, '_reason_phrase': None, '_charset': None, '_container': [b''], 'wsgi_request': <WSGIRequest: POST '/asset/1/sherdnote/create/'
>, 'client': <rest_framework.test.APIClient object at 0x7f9880902f10>, 'request': {'PATH_INFO': '/asset/1/sherdnote/create/', 'REQ
UEST_METHOD': 'POST', 'SERVER_PORT': '80', 'wsgi.url_scheme': 'http', 'CONTENT_LENGTH': 41, 'CONTENT_TYPE': 'application/json; cha
rset=None', 'wsgi.input': <django.test.client.FakePayload object at 0x7f987e834790>, 'QUERY_STRING': ''}, 'templates': [], 'contex
t': None, 'json': <function curry.<locals>._curried at 0x7f988018e440>, 'resolver_match': <SimpleLazyObject: <function Client.requ
est.<locals>.<lambda> at 0x7f988018eef0>>, '_dont_enforce_csrf_checks': True}
I’ve been having trouble tracking down why this is happening. Does anyone have any ideas?
Okay, well there was some course middleware in my application that was interfering with my API requests. The solution was to make a sample course and make my test user a student in that course before making the request.
For anyone else that ended up here like me looking for answers. I was upgrading my Django/graphene stack from Django 2.2 -> 3.2 and all the latest graphene-related things. The 405 for me was coming from a URL that didn’t quite line up because of a lack of slashes to close out the URL;
I couldn’t make my tests work because the graphene 3.0 default testing endpoint is set to '/graphql'
, where my url pattern in django was
path(
"graphql/",
csrf_exempt(
ConsistentErrorsGraphQLView.as_view(graphiql=settings.USE_GRAPHIQL)
),
),
to fix it, I set my graphene testing endpoint in my django settings:
GRAPHENE = {
...
"TESTING_ENDPOINT": "/graphql/"
...
}