summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnv3sh <anveshgreat11@gmail.com>2022-06-16 21:34:13 +0530
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2022-06-20 08:51:26 +0200
commitd7f5bfd241666c0a76e90208da1e9ef81aec44db (patch)
tree46ee5a0f15aead8589dcddc2b0d2c03ab431548a
parent901a1691982cab76349d33e51b72c40120ec927a (diff)
Fixed #32969 -- Fixed pickling HttpResponse and subclasses.
-rw-r--r--AUTHORS1
-rw-r--r--django/http/response.py19
-rw-r--r--django/template/response.py15
-rw-r--r--docs/releases/4.2.txt3
-rw-r--r--tests/test_client/tests.py16
-rw-r--r--tests/test_client/urls.py1
-rw-r--r--tests/test_client/views.py5
7 files changed, 51 insertions, 9 deletions
diff --git a/AUTHORS b/AUTHORS
index d8a3cf9103..7660d3a154 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -95,6 +95,7 @@ answer newbie questions, and generally made Django that much better:
Antti Haapala <antti@industrialwebandmagic.com>
Antti Kaihola <http://djangopeople.net/akaihola/>
Anubhav Joshi <anubhav9042@gmail.com>
+ Anvesh Mishra <anveshgreat11@gmail.com>
Aram Dulyan
arien <regexbot@gmail.com>
Armin Ronacher
diff --git a/django/http/response.py b/django/http/response.py
index 62b8b1c087..2bcd549f34 100644
--- a/django/http/response.py
+++ b/django/http/response.py
@@ -366,12 +366,31 @@ class HttpResponse(HttpResponseBase):
"""
streaming = False
+ non_picklable_attrs = frozenset(
+ [
+ "resolver_match",
+ # Non-picklable attributes added by test clients.
+ "asgi_request",
+ "client",
+ "context",
+ "json",
+ "templates",
+ "wsgi_request",
+ ]
+ )
def __init__(self, content=b"", *args, **kwargs):
super().__init__(*args, **kwargs)
# Content is a bytestring. See the `content` property methods.
self.content = content
+ def __getstate__(self):
+ obj_dict = self.__dict__.copy()
+ for attr in self.non_picklable_attrs:
+ if attr in obj_dict:
+ del obj_dict[attr]
+ return obj_dict
+
def __repr__(self):
return "<%(cls)s status_code=%(status_code)d%(content_type)s>" % {
"cls": self.__class__.__name__,
diff --git a/django/template/response.py b/django/template/response.py
index c38b95e9de..b4f0e171f1 100644
--- a/django/template/response.py
+++ b/django/template/response.py
@@ -8,7 +8,9 @@ class ContentNotRenderedError(Exception):
class SimpleTemplateResponse(HttpResponse):
- rendering_attrs = ["template_name", "context_data", "_post_render_callbacks"]
+ non_picklable_attrs = HttpResponse.non_picklable_attrs | frozenset(
+ ["template_name", "context_data", "_post_render_callbacks"]
+ )
def __init__(
self,
@@ -55,16 +57,11 @@ class SimpleTemplateResponse(HttpResponse):
Raise an exception if trying to pickle an unrendered response. Pickle
only rendered data, not the data used to construct the response.
"""
- obj_dict = self.__dict__.copy()
if not self._is_rendered:
raise ContentNotRenderedError(
"The response content must be rendered before it can be pickled."
)
- for attr in self.rendering_attrs:
- if attr in obj_dict:
- del obj_dict[attr]
-
- return obj_dict
+ return super().__getstate__()
def resolve_template(self, template):
"""Accept a template object, path-to-template, or list of paths."""
@@ -145,7 +142,9 @@ class SimpleTemplateResponse(HttpResponse):
class TemplateResponse(SimpleTemplateResponse):
- rendering_attrs = SimpleTemplateResponse.rendering_attrs + ["_request"]
+ non_picklable_attrs = SimpleTemplateResponse.non_picklable_attrs | frozenset(
+ ["_request"]
+ )
def __init__(
self,
diff --git a/docs/releases/4.2.txt b/docs/releases/4.2.txt
index 124470cf5b..ca1c2a0eaa 100644
--- a/docs/releases/4.2.txt
+++ b/docs/releases/4.2.txt
@@ -249,7 +249,8 @@ PostgreSQL 12 and higher.
Miscellaneous
-------------
-* ...
+* The undocumented ``SimpleTemplateResponse.rendering_attrs`` and
+ ``TemplateResponse.rendering_attrs`` are renamed to ``non_picklable_attrs``.
.. _deprecated-features-4.2:
diff --git a/tests/test_client/tests.py b/tests/test_client/tests.py
index ddc063f33d..57dc22ea0c 100644
--- a/tests/test_client/tests.py
+++ b/tests/test_client/tests.py
@@ -20,6 +20,7 @@ rather than the HTML rendered to the end-user.
"""
import itertools
+import pickle
import tempfile
from unittest import mock
@@ -80,6 +81,21 @@ class ClientTest(TestCase):
self.assertEqual(response.context["var"], "\xf2")
self.assertEqual(response.templates[0].name, "GET Template")
+ def test_pickling_response(self):
+ tests = ["/cbv_view/", "/get_view/"]
+ for url in tests:
+ with self.subTest(url=url):
+ response = self.client.get(url)
+ dump = pickle.dumps(response)
+ response_from_pickle = pickle.loads(dump)
+ self.assertEqual(repr(response), repr(response_from_pickle))
+
+ async def test_pickling_response_async(self):
+ response = await self.async_client.get("/async_get_view/")
+ dump = pickle.dumps(response)
+ response_from_pickle = pickle.loads(dump)
+ self.assertEqual(repr(response), repr(response_from_pickle))
+
def test_query_string_encoding(self):
# WSGI requires latin-1 encoded strings.
response = self.client.get("/get_view/?var=1\ufffd")
diff --git a/tests/test_client/urls.py b/tests/test_client/urls.py
index 2508346cf8..228e6c6a78 100644
--- a/tests/test_client/urls.py
+++ b/tests/test_client/urls.py
@@ -7,6 +7,7 @@ from . import views
urlpatterns = [
path("upload_view/", views.upload_view, name="upload_view"),
path("get_view/", views.get_view, name="get_view"),
+ path("cbv_view/", views.CBView.as_view()),
path("post_view/", views.post_view),
path("post_then_get_view/", views.post_then_get_view),
path("put_view/", views.put_view),
diff --git a/tests/test_client/views.py b/tests/test_client/views.py
index cff0463788..773e9e4e98 100644
--- a/tests/test_client/views.py
+++ b/tests/test_client/views.py
@@ -18,6 +18,7 @@ from django.shortcuts import render
from django.template import Context, Template
from django.test import Client
from django.utils.decorators import method_decorator
+from django.views.generic import TemplateView
def get_view(request):
@@ -418,3 +419,7 @@ class TwoArgException(Exception):
def two_arg_exception(request):
raise TwoArgException("one", "two")
+
+
+class CBView(TemplateView):
+ template_name = "base.html"