POST document with Django RequestFactory instead of form data

Question:

I’d like to build a request for testing middleware, but I don’t want POST requests to always assume I’m sending form data. Is there a way to set request.body on a request generated from django.test.RequestFactory?

I.e., I’d like to do something like:

from django.test import RequestFactory
import json

factory = RequestFactory(content_type='application/json')
data = {'message':'A test message'}
body = json.dumps(data)
request = factory.post('/a/test/path/', body)

# And have request.body be the encoded version of `body`

The code above will fail the test because my middleware needs the data to be passed as the document in request.body not as form data in request.POST. However, RequestFactory always sends the data as form data.

I can do this with django.test.Client:

from django.test import Client
import json

client = Client()
data = {'message':'A test message'}
body = json.dumps(data)
response = client.post('/a/test/path/', body, content_type='application/json')

I’d like to do the same thing with django.test.RequestFactory.

Asked By: Jay

||

Answers:

RequestFactory has built-in support for JSON payloads. You don’t need to dump your data first. But you should be passing the content-type to post, not to the instantiation.

factory = RequestFactory()
data = {'message':'A test message'}
request = factory.post('/a/test/path/', data, content_type='application/json')
Answered By: Daniel Roseman

I’ve tried Jay’s solution and didn’t work, but after some reseach, this did (Django 2.1.2)

factory = RequestFactory()    
request = factory.post('/post/url/')
request.data = {'id': 1}
Answered By: Saigesp

In later version of Django (tested on 4.0) this is no longer an issue. On the other hand, to pass data to request.POST might be.

In default, when passing content-type to a RequestFactory, data goes into request.body and when you don’t, data goes into request.POST.

request_factory = RequestFactory()

# provide content-type
request = request_factory.post(f'url', data={'foo': 'bar'}, content_type="application/json")
print(request.body)  # b'{"foo": "bar"}'

# don't provide content type
request = request_factory.post(f'url', data={'foo': 'bar'})
print(request.POST)  # <QueryDict: {'foo': ['bar']}>
Answered By: NelliaS

Here’s what worked for me in Django 4.1:

from django.contrib.sessions.middleware import SessionMiddleware
from django.test import TestCase, RequestFactory
from customauth import views


class RegistrationViewTest(TestCase):

    def setUp(self):
        self.factory = RequestFactory()
    
    def test_post_request_creates_new_user(self):
        data = {
            'email': '[email protected]',
            'screen_name': 'new_user',
            'password1': 'new_user_password',
            'password2': 'new_user_password',
        }
        request = self.factory.post('/any/path/will/do/', data )
        middleware = SessionMiddleware(request)
        middleware.process_request(request)
        request.session.save()
        response = views.registration_view(request)

        self.assertEqual(response.status_code, 302)
        # ok

This test passes. The form was successfully processed in views.registration_view.

Note:

  • When I included content_type='application/json' in the call to self.factory.post (as the accepted answer suggests), request.POST had no content in the view. Without that, it worked. I don’t know why but would be happy to learn.
  • I needed to manually added SessionMiddleware to request.
Answered By: Matt
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.