Skip to content

Commit

Permalink
support django 2.1 test client json data automatically serialized (#6511
Browse files Browse the repository at this point in the history
)
  • Loading branch information
terencehonles authored Dec 28, 2024
1 parent a8595a8 commit 089f6a6
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 9 deletions.
7 changes: 5 additions & 2 deletions docs/api-guide/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@ The `APIRequestFactory` class supports an almost identical API to Django's stand
factory = APIRequestFactory()
request = factory.post('/notes/', {'title': 'new idea'})

# Using the standard RequestFactory API to encode JSON data
request = factory.post('/notes/', {'title': 'new idea'}, content_type='application/json')

#### Using the `format` argument

Methods which create a request body, such as `post`, `put` and `patch`, include a `format` argument, which make it easy to generate requests using a content type other than multipart form data. For example:
Methods which create a request body, such as `post`, `put` and `patch`, include a `format` argument, which make it easy to generate requests using a wide set of request formats. When using this argument, the factory will select an appropriate renderer and its configured `content_type`. For example:

# Create a JSON POST request
factory = APIRequestFactory()
Expand All @@ -41,7 +44,7 @@ To support a wider set of request formats, or change the default format, [see th

If you need to explicitly encode the request body, you can do so by setting the `content_type` flag. For example:

request = factory.post('/notes/', json.dumps({'title': 'new idea'}), content_type='application/json')
request = factory.post('/notes/', yaml.dump({'title': 'new idea'}), content_type='application/yaml')

#### PUT and PATCH with form data

Expand Down
17 changes: 13 additions & 4 deletions rest_framework/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,14 +151,19 @@ def _encode_data(self, data, format=None, content_type=None):
Encode the data returning a two tuple of (bytes, content_type)
"""

if data is None:
return ('', content_type)

assert format is None or content_type is None, (
'You may not set both `format` and `content_type`.'
)

if content_type:
try:
data = self._encode_json(data, content_type)
except AttributeError:
pass

if data is None:
data = ''

# Content type specified explicitly, treat data as a raw bytestring
ret = force_bytes(data, settings.DEFAULT_CHARSET)

Expand All @@ -176,7 +181,6 @@ def _encode_data(self, data, format=None, content_type=None):

# Use format and render the data into a bytestring
renderer = self.renderer_classes[format]()
ret = renderer.render(data)

# Determine the content-type header from the renderer
content_type = renderer.media_type
Expand All @@ -185,6 +189,11 @@ def _encode_data(self, data, format=None, content_type=None):
content_type, renderer.charset
)

if data is None:
ret = ''
else:
ret = renderer.render(data)

# Coerce text to bytes if required.
if isinstance(ret, str):
ret = ret.encode(renderer.charset)
Expand Down
24 changes: 21 additions & 3 deletions tests/test_testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
from django.test import TestCase, override_settings
from django.urls import path

from rest_framework import fields, serializers
from rest_framework import fields, parsers, serializers
from rest_framework.authtoken.models import Token
from rest_framework.decorators import api_view
from rest_framework.decorators import api_view, parser_classes
from rest_framework.response import Response
from rest_framework.test import (
APIClient, APIRequestFactory, URLPatternsTestCase, force_authenticate
Expand Down Expand Up @@ -50,6 +50,12 @@ class BasicSerializer(serializers.Serializer):
flag = fields.BooleanField(default=lambda: True)


@api_view(['POST'])
@parser_classes((parsers.JSONParser,))
def post_json_view(request):
return Response(request.data)


@api_view(['POST'])
def post_view(request):
serializer = BasicSerializer(data=request.data)
Expand All @@ -62,7 +68,8 @@ def post_view(request):
path('session-view/', session_view),
path('redirect-view/', redirect_view),
path('redirect-view/<int:code>/', redirect_307_308_view),
path('post-view/', post_view)
path('post-json-view/', post_json_view),
path('post-view/', post_view),
]


Expand Down Expand Up @@ -236,6 +243,17 @@ def test_empty_post_uses_default_boolean_value(self):
assert response.status_code == 200
assert response.data == {"flag": True}

def test_post_encodes_data_based_on_json_content_type(self):
data = {'data': True}
response = self.client.post(
'/post-json-view/',
data=data,
content_type='application/json'
)

assert response.status_code == 200
assert response.data == data


class TestAPIRequestFactory(TestCase):
def test_csrf_exempt_by_default(self):
Expand Down

0 comments on commit 089f6a6

Please sign in to comment.