Django Test Client Method Override Header
Asked Answered
D

3

63

I am trying to test my update method on my viewset. The viewset is a modelviewset taken from drf. To update i would need to send a put request. As this is not always supported there are 2 ways to tell the server i am making a put request, the first which does not fit my needs is to use an additional field to form called _method and set it to put. As i need to post json data i need to use the second way, which uses the X-HTTP-Method-Override header.

To post my data in the testcase i use the following code:

header = {'X_HTTP_METHOD_OVERRIDE': 'PUT'}
response = client.post('/model/1/', content_type='application/json', data=post_data_clean, **header)

But unfortunately the result I get is {'detail':'Method POST not allowed.'}. I tested the behavior of the server using a addon (Postman) where i specified the X-HTTP-Method-Override header too. No exception is raised. I need to know now how to correctly pass the header to the django test client, otherwise testing will get really annoying over here.

Dachi answered 9/8, 2015 at 9:46 Comment(0)
M
100

You need to specify header as 'HTTP_X_HTTP_METHOD_OVERRIDE' instead of 'X_HTTP_METHOD_OVERRIDE' i.e. add HTTP_ at the beginning of the header.

header = {'HTTP_X_HTTP_METHOD_OVERRIDE': 'PUT'}
response = client.post('/model/1/', content_type='application/json', data=post_data_clean, **header)

From the Django documentation:

HTTP headers in the request are converted to META keys by converting all characters to uppercase, replacing any hyphens with underscores and adding an HTTP_ prefix to the name. So, for example, a header called X-Bender would be mapped to the META key HTTP_X_BENDER.

Michi answered 9/8, 2015 at 10:18 Comment(4)
Why the eff is this so hard to find in the documentation!?Unhinge
@Unhinge - Bigger question is why is this what the behavior is? Anyways, I had basically the same issue trying to use a cookie in my test - to pass a cookie, I needed to do header = {'HTTP_COOKIE': 'position=44.4444,-77.7777'}Andorra
@Andorra it turns out the behavior of the test client is documented (or hinted at, rather), docs.djangoproject.com/en/4.1/topics/testing/tools/…Mcbride
@Mcbride - Not really... it mentions HTTP_HOST and HTTP_ACCEPT but other than that all it does is say that names follow the CGI convention. It doesn't elaborate on what that is, but it does include a link to some CGI docs which start of by saying the docs aren't maintained and that the links are broken.Andorra
M
12

Also, you can pass headers to the constructor of the Client:

from django.test import Client

client = Client(HTTP_USER_AGENT="Mozilla/5.0 ...", HTTP_X_USER_ID="982734")

This way every request will contain default headers.

PS: This approach is valid for DRF TestApiClient and ApiRequestFactory.

Maurreen answered 13/4, 2020 at 10:30 Comment(0)
M
-1

Django 4.2 offers a headers keyword argument. See the Django 4.2 release notes:

The RequestFactory, AsyncRequestFactory, Client, and AsyncClient classes now support the headers parameter, which accepts a dictionary of header names and values. This allows a more natural syntax for declaring headers.

# Before:
self.client.get("/home/", HTTP_ACCEPT_LANGUAGE="fr")
await self.async_client.get("/home/", ACCEPT_LANGUAGE="fr")

# After:
self.client.get("/home/", headers={"accept-language": "fr"})
await self.async_client.get("/home/", headers={"accept-language": "fr"})
Merissameristem answered 7/3 at 7:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.