response.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. """
  2. The Response class in REST framework is similar to HTTPResponse, except that
  3. it is initialized with unrendered data, instead of a pre-rendered string.
  4. The appropriate renderer is called during Django's template response rendering.
  5. """
  6. from http.client import responses
  7. from django.template.response import SimpleTemplateResponse
  8. from rest_framework.serializers import Serializer
  9. class Response(SimpleTemplateResponse):
  10. """
  11. An HttpResponse that allows its data to be rendered into
  12. arbitrary media types.
  13. """
  14. def __init__(self, data=None, status=None,
  15. template_name=None, headers=None,
  16. exception=False, content_type=None):
  17. """
  18. Alters the init arguments slightly.
  19. For example, drop 'template_name', and instead use 'data'.
  20. Setting 'renderer' and 'media_type' will typically be deferred,
  21. For example being set automatically by the `APIView`.
  22. """
  23. super().__init__(None, status=status)
  24. if isinstance(data, Serializer):
  25. msg = (
  26. 'You passed a Serializer instance as data, but '
  27. 'probably meant to pass serialized `.data` or '
  28. '`.error`. representation.'
  29. )
  30. raise AssertionError(msg)
  31. self.data = data
  32. self.template_name = template_name
  33. self.exception = exception
  34. self.content_type = content_type
  35. if headers:
  36. for name, value in headers.items():
  37. self[name] = value
  38. @property
  39. def rendered_content(self):
  40. renderer = getattr(self, 'accepted_renderer', None)
  41. accepted_media_type = getattr(self, 'accepted_media_type', None)
  42. context = getattr(self, 'renderer_context', None)
  43. assert renderer, ".accepted_renderer not set on Response"
  44. assert accepted_media_type, ".accepted_media_type not set on Response"
  45. assert context is not None, ".renderer_context not set on Response"
  46. context['response'] = self
  47. media_type = renderer.media_type
  48. charset = renderer.charset
  49. content_type = self.content_type
  50. if content_type is None and charset is not None:
  51. content_type = "{}; charset={}".format(media_type, charset)
  52. elif content_type is None:
  53. content_type = media_type
  54. self['Content-Type'] = content_type
  55. ret = renderer.render(self.data, accepted_media_type, context)
  56. if isinstance(ret, str):
  57. assert charset, (
  58. 'renderer returned unicode, and did not specify '
  59. 'a charset value.'
  60. )
  61. return ret.encode(charset)
  62. if not ret:
  63. del self['Content-Type']
  64. return ret
  65. @property
  66. def status_text(self):
  67. """
  68. Returns reason text corresponding to our HTTP response status code.
  69. Provided for convenience.
  70. """
  71. return responses.get(self.status_code, '')
  72. def __getstate__(self):
  73. """
  74. Remove attributes from the response that shouldn't be cached.
  75. """
  76. state = super().__getstate__()
  77. for key in (
  78. 'accepted_renderer', 'renderer_context', 'resolver_match',
  79. 'client', 'request', 'json', 'wsgi_request'
  80. ):
  81. if key in state:
  82. del state[key]
  83. state['_closable_objects'] = []
  84. return state