it would seem obvious to call these next two members template and context but those names are reserved as part of the test client api to avoid the name collision we use different names request stores the current request object in subclasses that know about requests like templateresponse it s defined in the base class to minimize code duplication it s called self request because self request gets overwritten by django test client client unlike templatename and contextdata request should not be considered part of the public api content argument doesn t make sense here because it will be replaced with rendered template so we always pass empty string in order to prevent errors and provide shorter signature isrendered tracks whether the template and context has been baked into a final response super init doesn t know any better than to set self content to the empty string we just gave it which wrongly sets isrendered true so we initialize it to false after the call to super init raise an exception if trying to pickle an unrendered response pickle only rendered data not the data used to construct the response accept a template object pathtotemplate or list of paths if isinstancetemplate list tuple return selecttemplatetemplate usingself using elif isinstancetemplate str return gettemplatetemplate usingself using else return template def resolvecontextself context return context property def renderedcontentself template self resolvetemplateself templatename context self resolvecontextself contextdata return template rendercontext self request def addpostrendercallbackself callback if self isrendered callbackself else self postrendercallbacks appendcallback def renderself retval self if not self isrendered self content self renderedcontent for postcallback in self postrendercallbacks newretval postcallbackretval if newretval is not none retval newretval return retval property def isrenderedself return self isrendered def iterself if not self isrendered raise contentnotrenderederror the response content must be rendered before it can be iterated over return super iter property def contentself if not self isrendered raise contentnotrenderederror the response content must be rendered before it can be accessed return super content content setter def contentself value it would seem obvious to call these next two members template and context but those names are reserved as part of the test client api to avoid the name collision we use different names _request stores the current request object in subclasses that know about requests like templateresponse it s defined in the base class to minimize code duplication it s called self _request because self request gets overwritten by django test client client unlike template_name and context_data _request should not be considered part of the public api content argument doesn t make sense here because it will be replaced with rendered template so we always pass empty string in order to prevent errors and provide shorter signature _is_rendered tracks whether the template and context has been baked into a final response super __init__ doesn t know any better than to set self content to the empty string we just gave it which wrongly sets _is_rendered true so we initialize it to false after the call to super __init__ raise an exception if trying to pickle an unrendered response pickle only rendered data not the data used to construct the response accept a template object path to template or list of paths return the freshly rendered content for the template and context described by the templateresponse this does not set the final content of the response to set the response content you must either call render or set the content explicitly using the value of this property add a new post rendering callback if the response has already been rendered invoke the callback immediately render thereby finalizing the content of the response if the content has already been rendered this is a no op return the baked response instance set the content for the response
from django.http import HttpResponse

from .loader import get_template, select_template


class ContentNotRenderedError(Exception):
    pass


class SimpleTemplateResponse(HttpResponse):
    rendering_attrs = ["template_name", "context_data", "_post_render_callbacks"]

    def __init__(
        self,
        template,
        context=None,
        content_type=None,
        status=None,
        charset=None,
        using=None,
        headers=None,
    ):
        
        
        
        self.template_name = template
        self.context_data = context

        self.using = using

        self._post_render_callbacks = []

        
        
        
        
        
        
        self._request = None

        
        
        
        super().__init__("", content_type, status, charset=charset, headers=headers)

        
        
        
        
        
        self._is_rendered = False

    def __getstate__(self):
        
        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

    def resolve_template(self, template):
        
        if isinstance(template, (list, tuple)):
            return select_template(template, using=self.using)
        elif isinstance(template, str):
            return get_template(template, using=self.using)
        else:
            return template

    def resolve_context(self, context):
        return context

    @property
    def rendered_content(self):
        
        template = self.resolve_template(self.template_name)
        context = self.resolve_context(self.context_data)
        return template.render(context, self._request)

    def add_post_render_callback(self, callback):
        
        if self._is_rendered:
            callback(self)
        else:
            self._post_render_callbacks.append(callback)

    def render(self):
        
        retval = self
        if not self._is_rendered:
            self.content = self.rendered_content
            for post_callback in self._post_render_callbacks:
                newretval = post_callback(retval)
                if newretval is not None:
                    retval = newretval
        return retval

    @property
    def is_rendered(self):
        return self._is_rendered

    def __iter__(self):
        if not self._is_rendered:
            raise ContentNotRenderedError(
                "The response content must be rendered before it can be iterated over."
            )
        return super().__iter__()

    @property
    def content(self):
        if not self._is_rendered:
            raise ContentNotRenderedError(
                "The response content must be rendered before it can be accessed."
            )
        return super().content

    @content.setter
    def content(self, value):
        
        HttpResponse.content.fset(self, value)
        self._is_rendered = True


class TemplateResponse(SimpleTemplateResponse):
    rendering_attrs = SimpleTemplateResponse.rendering_attrs + ["_request"]

    def __init__(
        self,
        request,
        template,
        context=None,
        content_type=None,
        status=None,
        charset=None,
        using=None,
        headers=None,
    ):
        super().__init__(
            template, context, content_type, status, charset, using, headers=headers
        )
        self._request = request