[Django] #31533: Test client request's post body can be read multiple times, unlike real requests

14 views
Skip to first unread message

Django

unread,
May 3, 2020, 11:02:17 AM5/3/20
to django-...@googlegroups.com
#31533: Test client request's post body can be read multiple times, unlike real
requests
---------------------------------------------+------------------------
Reporter: Ben Beecher | Owner: nobody
Type: Bug | Status: new
Component: Testing framework | Version: 3.0
Severity: Normal | Keywords:
Triage Stage: Unreviewed | Has patch: 0
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
---------------------------------------------+------------------------
I have a DRF based signup flow that looks like this:

{{{
def post(self, request, *args, **kwargs):
register_serializer = self.get_serializer(data=request.data)
login_serializer =
settings.SERIALIZERS.token_create(data=request.data)

if register_serializer.is_valid():
return self.create(request, *args, **kwargs)
elif login_serializer.is_valid():
return TokenCreateView.as_view()(request=request._request)
else:
register_serializer.is_valid(raise_exception=True)
}}}
In short - try to sign a user up, then try to log them in.

with the following test I get a good response:


{{{
def test_register_but_really_login(self):
response = self.client.post(self.url, self.form_data)
self.assertEqual(response.status_code, s.HTTP_201_CREATED)
response = self.client.post(self.url, self.form_data)
self.assertEqual(response.status_code, s.HTTP_200_OK)

}}}

But when run for real I get this response with the same behavior:

{{{
10:57:18 backend.1 | 127.0.0.1 - - [03/May/2020 14:57:18] "POST
/auth/register/ HTTP/1.1" 500 -
10:57:18 backend.1 | Traceback (most recent call last):
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/django/contrib/staticfiles/handlers.py", line 68, in __call__
10:57:18 backend.1 | return self.application(environ, start_response)
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/django/core/handlers/wsgi.py", line 133, in __call__
10:57:18 backend.1 | response = self.get_response(request)
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/django/core/handlers/base.py", line 75, in get_response
10:57:18 backend.1 | response = self._middleware_chain(request)
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/django/core/handlers/exception.py", line 36, in inner
10:57:18 backend.1 | response = response_for_exception(request, exc)
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/django/core/handlers/exception.py", line 90, in
response_for_exception
10:57:18 backend.1 | response = handle_uncaught_exception(request,
get_resolver(get_urlconf()), sys.exc_info())
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/django/core/handlers/exception.py", line 125, in
handle_uncaught_exception
10:57:18 backend.1 | return debug.technical_500_response(request,
*exc_info)
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/django_extensions/management/technical_response.py", line 37, in
null_technical_500_response
10:57:18 backend.1 | six.reraise(exc_type, exc_value, tb)
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/six.py", line 702, in reraise
10:57:18 backend.1 | raise value.with_traceback(tb)
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/django/core/handlers/exception.py", line 34, in inner
10:57:18 backend.1 | response = get_response(request)
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/django/core/handlers/base.py", line 115, in _get_response
10:57:18 backend.1 | response =
self.process_exception_by_middleware(e, request)
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/django/core/handlers/base.py", line 113, in _get_response
10:57:18 backend.1 | response = wrapped_callback(request,
*callback_args, **callback_kwargs)
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/django/views/decorators/csrf.py", line 54, in wrapped_view
10:57:18 backend.1 | return view_func(*args, **kwargs)
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/django/views/generic/base.py", line 71, in view
10:57:18 backend.1 | return self.dispatch(request, *args, **kwargs)
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/rest_framework/views.py", line 505, in dispatch
10:57:18 backend.1 | response = self.handle_exception(exc)
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/rest_framework/views.py", line 465, in handle_exception
10:57:18 backend.1 | self.raise_uncaught_exception(exc)
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/rest_framework/views.py", line 476, in raise_uncaught_exception
10:57:18 backend.1 | raise exc
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/rest_framework/views.py", line 502, in dispatch
10:57:18 backend.1 | response = handler(request, *args, **kwargs)
10:57:18 backend.1 | File
"/home/bbeecher/workspace/sampleapp/sampleapp/account/views.py", line 31,
in post
10:57:18 backend.1 | return
TokenCreateView.as_view()(request=request._request)
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/django/views/decorators/csrf.py", line 54, in wrapped_view
10:57:18 backend.1 | return view_func(*args, **kwargs)
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/django/views/generic/base.py", line 71, in view
10:57:18 backend.1 | return self.dispatch(request, *args, **kwargs)
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/rest_framework/views.py", line 505, in dispatch
10:57:18 backend.1 | response = self.handle_exception(exc)
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/rest_framework/views.py", line 465, in handle_exception
10:57:18 backend.1 | self.raise_uncaught_exception(exc)
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/rest_framework/views.py", line 476, in raise_uncaught_exception
10:57:18 backend.1 | raise exc
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/rest_framework/views.py", line 502, in dispatch
10:57:18 backend.1 | response = handler(request, *args, **kwargs)
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/djoser/utils.py", line 36, in post
10:57:18 backend.1 | serializer =
self.get_serializer(data=request.data)
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/rest_framework/request.py", line 209, in data
10:57:18 backend.1 | self._load_data_and_files()
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/rest_framework/request.py", line 272, in _load_data_and_files
10:57:18 backend.1 | self._data, self._files = self._parse()
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/rest_framework/request.py", line 322, in _parse
10:57:18 backend.1 | stream = self.stream
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/rest_framework/request.py", line 196, in stream
10:57:18 backend.1 | self._load_stream()
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/rest_framework/request.py", line 302, in _load_stream
10:57:18 backend.1 | self._stream = io.BytesIO(self.body)
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/rest_framework/request.py", line 409, in __getattr__
10:57:18 backend.1 | return getattr(self._request, attr)
10:57:18 backend.1 | File
"/home/bbeecher/.virtualenvs/sampleapp/lib/python3.8/site-
packages/django/http/request.py", line 292, in body
10:57:18 backend.1 | raise RawPostDataException("You cannot access
body after reading from request's data stream")
10:57:18 backend.1 | django.http.request.RawPostDataException: You cannot
access body after reading from request's data stream
}}}

The error message makes total sense - I am in fact trying to access the
request body a second time. But I do the same thing in test successfully.

--
Ticket URL: <https://code.djangoproject.com/ticket/31533>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
May 3, 2020, 10:02:11 PM5/3/20
to django-...@googlegroups.com
#31533: Test client request's post body can be read multiple times, unlike real
requests
-----------------------------------+--------------------------------------

Reporter: Ben Beecher | Owner: nobody
Type: Bug | Status: new
Component: Testing framework | Version: 3.0
Severity: Normal | Resolution:

Keywords: | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-----------------------------------+--------------------------------------

Comment (by Ichlasul Affan):

Hi Ben,

Django's {{{django.test.Client}}} is a browser-like simulator. Each time
you run {{{self.client.post}}, {{self.client.get}}, etc., {{{Client}}}
will create a new, different request even though the body is the same.

Are you using {{{djoser}}}? If yes, I think that's not how you use
{{{djoser}}} properly. You just need to set up {{{djoser}}} in your
{{{settings.py}}} and {{{urls.py}}}, then use that URL to create tokens or
something else. You don't need to recall {{{djoser}}}'s view inside your
view using your own request intended for your view, it will be
problemmatic in some places.

--
Ticket URL: <https://code.djangoproject.com/ticket/31533#comment:1>

Django

unread,
May 4, 2020, 12:18:36 AM5/4/20
to django-...@googlegroups.com
#31533: Test client request's post body can be read multiple times, unlike real
requests.
-----------------------------------+--------------------------------------

Reporter: Ben Beecher | Owner: nobody
Type: Bug | Status: closed

Component: Testing framework | Version: 3.0
Severity: Normal | Resolution: invalid

Keywords: | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-----------------------------------+--------------------------------------
Changes (by felixxm):

* status: new => closed
* resolution: => invalid


Comment:

[https://docs.djangoproject.com/en/3.0/topics/testing/tools/#django.test.Client.post
test.Client.post()] makes a new POST request on each call. I don't see any
issue in this behavior.

--
Ticket URL: <https://code.djangoproject.com/ticket/31533#comment:2>

Reply all
Reply to author
Forward
0 new messages