diff --git a/django/http/__init__.py b/django/http/__init__.py
index 49acd57..d609eb5 100644
a
|
b
|
class HttpResponse(HttpResponseBase):
|
751 | 751 | else: |
752 | 752 | __str__ = serialize |
753 | 753 | |
| 754 | def _consume_content(self): |
| 755 | # If the response was instantiated with an iterator, and its content |
| 756 | # is accessed, the iterator is going be exhausted and the content |
| 757 | # loaded in memory. At this point, it's better to abandon the original |
| 758 | # iterator and save the content for later reuse. |
| 759 | if not isinstance(self._container, list): |
| 760 | self._container = list(self._container) |
| 761 | |
754 | 762 | @property |
755 | 763 | def content(self): |
| 764 | self._consume_content() |
756 | 765 | return b''.join(self.make_bytes(e) for e in self._container) |
757 | 766 | |
758 | 767 | @content.setter |
759 | 768 | def content(self, value): |
760 | 769 | if hasattr(value, '__iter__') and not isinstance(value, (bytes, six.string_types)): |
761 | 770 | self._container = value |
762 | | self._base_content_is_iter = True |
763 | 771 | if hasattr(value, 'close'): |
764 | 772 | self._closable_objects.append(value) |
765 | 773 | else: |
766 | 774 | self._container = [value] |
767 | | self._base_content_is_iter = False |
768 | 775 | |
769 | 776 | def __iter__(self): |
770 | 777 | self._iterator = iter(self._container) |
… |
… |
class HttpResponse(HttpResponseBase):
|
782 | 789 | next = __next__ # Python 2 compatibility |
783 | 790 | |
784 | 791 | def write(self, content): |
785 | | if self._base_content_is_iter: |
786 | | raise Exception("This %s instance is not writable" % self.__class__.__name__) |
| 792 | self._consume_content() |
787 | 793 | self._container.append(content) |
788 | 794 | |
789 | 795 | def tell(self): |
790 | | if self._base_content_is_iter: |
791 | | raise Exception("This %s instance cannot tell its position" % self.__class__.__name__) |
792 | | return sum([len(chunk) for chunk in self]) |
| 796 | self._consume_content() |
| 797 | return sum(len(chunk) for chunk in self) |
793 | 798 | |
794 | 799 | class StreamingHttpResponse(HttpResponseBase): |
795 | 800 | """ |
diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt
index 89d0fe8..6dda15e 100644
a
|
b
|
use this technique, the iterator should return strings.
|
578 | 578 | If you want to guarantee that your response will stream to the client, you |
579 | 579 | should use the new :class:`StreamingHttpResponse` class instead. |
580 | 580 | |
581 | | If an :class:`HttpResponse` instance has been initialized with an iterator as |
582 | | its content, you can't use it as a file-like object. Doing so will raise an |
583 | | exception. |
| 581 | .. versionchanged:: 1.5 |
| 582 | |
| 583 | You can now use :class:`HttpResponse` as a file-like object even if it was |
| 584 | instantiated with an iterator. Django will consume and save the content of |
| 585 | the iterator on first access. |
584 | 586 | |
585 | 587 | Setting headers |
586 | 588 | ~~~~~~~~~~~~~~~ |
diff --git a/tests/regressiontests/httpwrappers/tests.py b/tests/regressiontests/httpwrappers/tests.py
index bfb4ae1..5de4c15 100644
a
|
b
|
class HttpResponseTests(unittest.TestCase):
|
330 | 330 | self.assertRaises(UnicodeEncodeError, |
331 | 331 | getattr, r, 'content') |
332 | 332 | |
| 333 | # content can safely be accessed multiple times. |
| 334 | r = HttpResponse(iter(['hello', 'world'])) |
| 335 | self.assertEqual(r.content, r.content) |
| 336 | self.assertEqual(r.content, b'helloworld') |
| 337 | |
| 338 | # additional content can be written to the response. |
| 339 | r.write('!') |
| 340 | self.assertEqual(r.content, b'helloworld!') |
| 341 | |
| 342 | |
333 | 343 | def test_file_interface(self): |
334 | 344 | r = HttpResponse() |
335 | 345 | r.write(b"hello") |
… |
… |
class HttpResponseTests(unittest.TestCase):
|
338 | 348 | self.assertEqual(r.tell(), 17) |
339 | 349 | |
340 | 350 | r = HttpResponse(['abc']) |
341 | | self.assertRaises(Exception, r.write, 'def') |
| 351 | r.write('def') |
| 352 | self.assertEqual(r.tell(), 6) |
| 353 | self.assertEqual(r.content, b'abcdef') |
342 | 354 | |
343 | 355 | def test_unsafe_redirect(self): |
344 | 356 | bad_urls = [ |