Search code examples
djangohttp-redirectherokudjango-testing

Django test client POST command not registering through 301 redirect


I'm writing Django tests for a live Heroku server, and am having trouble getting Django to recognize a POST request through a redirect.

On my test server, things work fine:

views.py

def detect_post(request):
    """
    Detect whether this is a POST or a GET request.
    """

    if request.method == 'POST':

        return HttpResponse(
            json.dumps({"POST request": "POST request detected"}),
            content_type="application/json"
        )

    # If this is a GET request, return an error
    else:
        return HttpResponse(
            json.dumps({"Access denied": "You are not permitted to access this page."}),
            content_type="application/json"
        )

python manage.py shell

>>> from django.urls import reverse
>>> from django.test import Client
>>> c = Client()

# GET request returns an error, as expected:
>>> response = c.get(reverse('detectpost'), follow=True)
>>> response.status_code
200
>>> response.content
b'{"Access denied": "You are not permitted to access this page."}'

# POST request returns success, as expected
>>> response = c.post(reverse('detectpost'))
>>> response.status_code
200
>>> response.content
b'{"POST request": "POST request detected"}'

However, when I move over to my production server, I encounter problems. I think it's because my production server has SECURE_SSL_REDIRECT, so all pages are redirecting to an SSL-enabled version of the same page. Here's what happens when I try to run the same test code on my production server:

heroku run python manage.py shell

>>> from django.urls import reverse
>>> from django.test import Client
>>> c = Client()

# GET request returns an error, as expected:
>>> response = c.get(reverse('detectpost'), follow=True)
>>> response.status_code
200
>>> response.content
b'{"Access denied": "You are not permitted to access this page."}'

# POST request, however, has problems
>>> response = c.post(reverse('detectpost'), follow=True)
>>> response.status_code
200
>>> response.content
b'{"Access denied": "You are not permitted to access this page."}'
>>> response.redirect_chain
[('https://testserver/testpost/', 301)]

# I believe that the POST request isn't being detected because my site uses redirects
>>> response = c.post(reverse('detectpost'))
>>> response.status_code
301

How can I get my Django TestClient to register a POST request even through a redirect? I expected the follow=True flag to accomplish this, but it doesn't seem to be working.


Solution

  • When you use client.post, you can simulate a request to the https URL by setting secure=True.

    response = c.post(reverse('detectpost'), follow=True, secure=True)
    

    When the Django secure middleware returns a 301 redirect from http to https, browsers will make a GET request to the new URL even if the original request was a POST request.

    There is a 307 response which tells the browser not to change the method. However I would not try to get Django to return a 307. Just change the client.post() call to use secure=True as I suggested above.