diff --git a/AUTHORS b/AUTHORS
index 1c8036b74a..edd7d50726 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -73,7 +73,6 @@ answer newbie questions, and generally made Django that much better:
Eugene Lazutkin
limodou
Martin Maney
- Maniac
Manuzhai
Petar Marić
mark@junklight.com
@@ -95,6 +94,7 @@ answer newbie questions, and generally made Django that much better:
J. Rademaker
Brian Ray
Oliver Rutherfurd
+ Ivan Sagalaev (Maniac)
David Schein
sopel
Radek Švarz
diff --git a/django/core/handlers/modpython.py b/django/core/handlers/modpython.py
index eec35ff072..37499c6f06 100644
--- a/django/core/handlers/modpython.py
+++ b/django/core/handlers/modpython.py
@@ -162,7 +162,8 @@ def populate_apache_request(http_response, mod_python_req):
for c in http_response.cookies.values():
mod_python_req.headers_out.add('Set-Cookie', c.output(header=''))
mod_python_req.status = http_response.status_code
- mod_python_req.write(http_response.get_content_as_string(settings.DEFAULT_CHARSET))
+ for chunk in http_response.iterator:
+ mod_python_req.write(chunk)
def handler(req):
# mod_python hooks into this function.
diff --git a/django/core/handlers/wsgi.py b/django/core/handlers/wsgi.py
index 23204e63a6..7541a5fd9d 100644
--- a/django/core/handlers/wsgi.py
+++ b/django/core/handlers/wsgi.py
@@ -172,6 +172,5 @@ class WSGIHandler(BaseHandler):
response_headers = response.headers.items()
for c in response.cookies.values():
response_headers.append(('Set-Cookie', c.output(header='')))
- output = [response.get_content_as_string(settings.DEFAULT_CHARSET)]
start_response(status, response_headers)
- return output
+ return response.iterator
diff --git a/django/middleware/common.py b/django/middleware/common.py
index aa8f4ec071..3643fce553 100644
--- a/django/middleware/common.py
+++ b/django/middleware/common.py
@@ -67,7 +67,7 @@ class CommonMiddleware:
# Use ETags, if requested.
if settings.USE_ETAGS:
- etag = md5.new(response.get_content_as_string(settings.DEFAULT_CHARSET)).hexdigest()
+ etag = md5.new(response.content).hexdigest()
if request.META.get('HTTP_IF_NONE_MATCH') == etag:
response = httpwrappers.HttpResponseNotModified()
else:
diff --git a/django/utils/cache.py b/django/utils/cache.py
index 7c7a41bafa..b888cef179 100644
--- a/django/utils/cache.py
+++ b/django/utils/cache.py
@@ -74,7 +74,7 @@ def patch_response_headers(response, cache_timeout=None):
cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS
now = datetime.datetime.utcnow()
if not response.has_header('ETag'):
- response['ETag'] = md5.new(response.get_content_as_string('utf8')).hexdigest()
+ response['ETag'] = md5.new(response.content).hexdigest()
if not response.has_header('Last-Modified'):
response['Last-Modified'] = now.strftime('%a, %d %b %Y %H:%M:%S GMT')
if not response.has_header('Expires'):
diff --git a/django/utils/httpwrappers.py b/django/utils/httpwrappers.py
index 8716505f2e..9d731d23c9 100644
--- a/django/utils/httpwrappers.py
+++ b/django/utils/httpwrappers.py
@@ -143,14 +143,20 @@ def parse_cookie(cookie):
cookiedict[key] = c.get(key).value
return cookiedict
-class HttpResponse:
+class HttpResponse(object):
"A basic HTTP response, with content and dictionary-accessed headers"
def __init__(self, content='', mimetype=None):
+ from django.conf.settings import DEFAULT_CONTENT_TYPE, DEFAULT_CHARSET
+ self._charset = DEFAULT_CHARSET
if not mimetype:
- from django.conf.settings import DEFAULT_CONTENT_TYPE, DEFAULT_CHARSET
mimetype = "%s; charset=%s" % (DEFAULT_CONTENT_TYPE, DEFAULT_CHARSET)
- self.content = content
- self.headers = {'Content-Type':mimetype}
+ if hasattr(content, '__iter__'):
+ self.iterator = content
+ self._is_string = False
+ else:
+ self.iterator = [content]
+ self._is_string = True
+ self.headers = {'Content-Type': mimetype}
self.cookies = SimpleCookie()
self.status_code = 200
@@ -193,25 +199,32 @@ class HttpResponse:
except KeyError:
pass
- def get_content_as_string(self, encoding):
- """
- Returns the content as a string, encoding it from a Unicode object if
- necessary.
- """
- if isinstance(self.content, unicode):
- return self.content.encode(encoding)
- return self.content
+ def _get_content(self):
+ content = ''.join(self.iterator)
+ if isinstance(content, unicode):
+ content = content.encode(self._charset)
+ return content
+
+ def _set_content(self, value):
+ self.iterator = [value]
+ self._is_string = True
+
+ content = property(_get_content, _set_content)
# The remaining methods partially implement the file-like object interface.
# See http://docs.python.org/lib/bltin-file-objects.html
def write(self, content):
- self.content += content
+ if not self._is_string:
+ raise Exception, "This %s instance is not writable" % self.__class__
+ self.iterator.append(content)
def flush(self):
pass
def tell(self):
- return len(self.content)
+ if not self._is_string:
+ raise Exception, "This %s instance cannot tell its position" % self.__class__
+ return sum(len(chunk) for chunk in self.iterator)
class HttpResponseRedirect(HttpResponse):
def __init__(self, redirect_to):
diff --git a/docs/request_response.txt b/docs/request_response.txt
index 818c639d9a..fc78a3459b 100644
--- a/docs/request_response.txt
+++ b/docs/request_response.txt
@@ -304,6 +304,10 @@ Methods
Instantiates an ``HttpResponse`` object with the given page content (a
string) and MIME type. The ``DEFAULT_MIME_TYPE`` is ``"text/html"``.
+ **New in Django development version:** ``content`` can be an iterator
+ instead of a string. This iterator should return strings, and those strings
+ will be joined together to form the content of the response.
+
``__setitem__(header, value)``
Sets the given header name to the given value. Both ``header`` and
``value`` should be strings.
@@ -341,7 +345,12 @@ Methods
``get_content_as_string(encoding)``
Returns the content as a Python string, encoding it from a Unicode object
- if necessary.
+ if necessary. **Removed in Django development version.**
+
+``content``
+ **New in Django development version.** Returns the content as a Python
+ string, encoding it from a Unicode object if necessary. Note this is a
+ property, not a method, so use ``r.content`` instead of ``r.content()``.
``write(content)``, ``flush()`` and ``tell()``
These methods make an ``HttpResponse`` instance a file-like object.