diff --git a/django/shortcuts/__init__.py b/django/shortcuts/__init__.py
--- a/django/shortcuts/__init__.py
+++ b/django/shortcuts/__init__.py
@@ -5,6 +5,7 @@
 """
 
 from django.template import loader
+from django.template.response import TemplateResponse
 from django.http import HttpResponse, Http404
 from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect
 from django.db.models.manager import Manager
@@ -23,16 +24,16 @@
     """
     Returns an HttpResponseRedirect to the apropriate URL for the arguments
     passed.
-    
+
     The arguments could be:
-    
+
         * A model: the model's `get_absolute_url()` function will be called.
-    
+
         * A view name, possibly with arguments: `urlresolvers.reverse()` will
           be used to reverse-resolve the name.
-         
+
         * A URL, which will be used as-is for the redirect location.
-        
+
     By default issues a temporary redirect; pass permanent=True to issue a
     permanent redirect
     """
@@ -40,11 +41,11 @@
         redirect_class = HttpResponsePermanentRedirect
     else:
         redirect_class = HttpResponseRedirect
-    
+
     # If it's a model, use get_absolute_url()
     if hasattr(to, 'get_absolute_url'):
         return redirect_class(to.get_absolute_url())
-    
+
     # Next try a reverse URL resolution.
     try:
         return redirect_class(urlresolvers.reverse(to, args=args, kwargs=kwargs))
@@ -55,7 +56,7 @@
         # If this doesn't "feel" like a URL, re-raise.
         if '/' not in to and '.' not in to:
             raise
-        
+
     # Finally, fall back and assume it's a URL
     return redirect_class(to)
 
@@ -101,4 +102,6 @@
     obj_list = list(queryset.filter(*args, **kwargs))
     if not obj_list:
         raise Http404('No %s matches the given query.' % queryset.model._meta.object_name)
-    return obj_list
\ No newline at end of file
+    return obj_list
+
+render = TemplateResponse
diff --git a/django/template/response.py b/django/template/response.py
new file mode 100644
--- /dev/null
+++ b/django/template/response.py
@@ -0,0 +1,89 @@
+from django.http import HttpResponse
+from django.template import loader, Context, RequestContext
+
+class SimpleTemplateResponse(HttpResponse):
+
+    def __init__(self, template, context=None, mimetype=None, status=None,
+            content_type=None):
+        # These two properties were originally called 'template' and 'context'
+        # but django.test.client.Client was clobbering those leading to really
+        # tricky-to-debug problems
+        self.template_name = template
+        self.template_context = context
+        self.baked = False
+
+        # 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.
+        super(SimpleTemplateResponse, self).__init__('', mimetype, status,
+                                                     content_type)
+
+    def resolve_template(self, template):
+        "Accepts a template object, path-to-template or list of paths"
+        if isinstance(template, (list, tuple)):
+            return loader.select_template(template)
+        elif isinstance(template, basestring):
+            return loader.get_template(template)
+        else:
+            return template
+
+    def resolve_context(self, context):
+        "context can be a dictionary or a context object"
+        if isinstance(context, Context):
+            return context
+        else:
+            return Context(context)
+
+    def render(self):
+        template = self.resolve_template(self.template_name)
+        context = self.resolve_context(self.template_context)
+        content = template.render(context)
+        return content
+
+    def bake(self):
+        """
+        The template is baked the first time you try to access
+        response.content or iterate over it. This is a bit ugly, but is
+        necessary because Django middleware sometimes expects to be able to
+        over-write the content of a response.
+        """
+        if not self.baked:
+            self.force_bake()
+
+    def force_bake(self):
+        "Call this if you have modified the template or context but are "
+        "unsure if the template has already been baked."
+        self._set_content(self.render())
+        self.baked = True
+
+    def __iter__(self):
+        self.bake()
+        return super(SimpleTemplateResponse, self).__iter__()
+
+    def _get_content(self):
+        self.bake()
+        return super(SimpleTemplateResponse, self)._get_content()
+
+    def _set_content(self, value):
+        "Overrides rendered content, unless you later call force_bake()"
+        super(SimpleTemplateResponse, self)._set_content(value)
+
+    content = property(_get_content, _set_content)
+
+class TemplateResponse(SimpleTemplateResponse):
+
+    def __init__(self, request, template, context=None, mimetype=None,
+            status=None, content_type=None):
+        # self.request gets over-written by django.test.client.Client - and
+        # unlike template_context and template_name the _request should not
+        # be considered part of the public API.
+        self._request = request
+        super(TemplateResponse, self).__init__(
+            template, context, mimetype, status, content_type)
+
+    def resolve_context(self, context):
+        if isinstance(context, Context):
+            return context
+        else:
+            return RequestContext(self._request, context)
+
diff --git a/tests/regressiontests/templates/response.py b/tests/regressiontests/templates/response.py
new file mode 100644
--- /dev/null
+++ b/tests/regressiontests/templates/response.py
@@ -0,0 +1,100 @@
+from django.utils import unittest
+from django.test import RequestFactory
+from django.conf import settings
+from django.template import Template, Context, RequestContext
+from django.template.response import TemplateResponse, SimpleTemplateResponse
+
+# tests rely on fact that django.contrib.auth.context_processors.auth
+# is in settings.TEMPLATE_CONTEXT_PROCESSORS and global context
+# processors should only work when RequestContext is used.
+
+class SimpleTemplateResponseTest(unittest.TestCase):
+
+    def _response(self, template='foo', *args, **kwargs):
+        return SimpleTemplateResponse(Template(template), *args, **kwargs)
+
+    def test_template_resolving(self):
+
+        # templates from 'templates/templates/..' dir can't be loaded so
+        # the base.html template is reused. It is assumed that base.html
+        # contains 'Django Internal Tests' string.
+
+        response = SimpleTemplateResponse('base.html')
+        self.assertIn('Django Internal Tests', response.content)
+
+        response = SimpleTemplateResponse(['foo.html', 'base.html'])
+        self.assertIn('Django Internal Tests', response.content)
+
+        response = self._response()
+        self.assertEqual(response.content, 'foo')
+
+    def test_explicit_baking(self):
+        # explicit baking
+        response = self._response()
+        self.assertFalse(response.baked)
+        response.bake()
+        self.assertTrue(response.baked)
+
+    def test_baking_on_access(self):
+        # the response is baked when content is accessed
+        response = self._response()
+        self.assertFalse(response.baked)
+        content = response.content
+        self.assertTrue(response.baked)
+        self.assertEqual(content, 'foo')
+
+    def test_dict_context(self):
+        response = self._response('{{ foo }}{{ user.is_anonymous }}',
+                                  {'foo': 'bar'})
+        self.assertEqual(response.template_context, {'foo': 'bar'})
+        self.assertEqual(response.content, 'bar')
+
+    def test_context_instance(self):
+        response = self._response('{{ foo }}{{ user.is_anonymous }}',
+                                  Context({'foo': 'bar'}))
+        self.assertEqual(response.template_context.__class__, Context)
+        self.assertEqual(response.content, 'bar')
+
+    def test_kwargs(self):
+        response = self._response(content_type = 'application/json', status=504)
+        self.assertEqual(response['content-type'], 'application/json')
+        self.assertEqual(response.status_code, 504)
+
+    def test_args(self):
+        response = SimpleTemplateResponse('', {}, 'application/json', 504)
+        self.assertEqual(response['content-type'], 'application/json')
+        self.assertEqual(response.status_code, 504)
+
+
+class TemplateResponseTest(unittest.TestCase):
+    def setUp(self):
+        self.factory = RequestFactory()
+
+    def _response(self, template='foo', *args, **kwargs):
+        return TemplateResponse(self.factory.get('/'), Template(template),
+                                *args, **kwargs)
+
+    def test_render(self):
+        response = self._response('{{ foo }}{{ user.is_anonymous }}')
+        self.assertEqual(response.content, 'True')
+
+    def test_render_with_requestcontext(self):
+        response = self._response('{{ foo }}{{ user.is_anonymous }}',
+                                  {'foo': 'bar'})
+        self.assertEqual(response.content, 'barTrue')
+
+    def test_render_with_context(self):
+        response = self._response('{{ foo }}{{ user.is_anonymous }}',
+                                  Context({'foo': 'bar'}))
+        self.assertEqual(response.content, 'bar')
+
+    def test_kwargs(self):
+        response = self._response(content_type = 'application/json', status=504)
+        self.assertEqual(response['content-type'], 'application/json')
+        self.assertEqual(response.status_code, 504)
+
+    def test_args(self):
+        response = TemplateResponse(self.factory.get('/'), '', {},
+                                    'application/json', 504)
+        self.assertEqual(response['content-type'], 'application/json')
+        self.assertEqual(response.status_code, 504)
diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py
--- a/tests/regressiontests/templates/tests.py
+++ b/tests/regressiontests/templates/tests.py
@@ -27,6 +27,7 @@
 from unicode import unicode_tests
 from nodelist import NodelistTest
 from smartif import *
+from response import *
 
 try:
     from loaders import *
diff --git a/tests/regressiontests/views/templates/debug/render_test.html b/tests/regressiontests/views/templates/debug/render_test.html
new file mode 100644
--- /dev/null
+++ b/tests/regressiontests/views/templates/debug/render_test.html
@@ -0,0 +1,1 @@
+{{ foo }}.{{ bar }}.{{ baz }}.{{ user.is_anonymous }}
diff --git a/tests/regressiontests/views/tests/__init__.py b/tests/regressiontests/views/tests/__init__.py
--- a/tests/regressiontests/views/tests/__init__.py
+++ b/tests/regressiontests/views/tests/__init__.py
@@ -7,3 +7,4 @@
 from i18n import *
 from specials import *
 from static import *
+from shortcuts import *
diff --git a/tests/regressiontests/views/tests/shortcuts.py b/tests/regressiontests/views/tests/shortcuts.py
new file mode 100644
--- /dev/null
+++ b/tests/regressiontests/views/tests/shortcuts.py
@@ -0,0 +1,42 @@
+from django.utils import unittest
+from django.test import RequestFactory
+from django.template import Template
+from django.shortcuts import render
+
+def library_view(request):
+    return render(request, 'debug/render_test.html',
+                  {'foo': 'foo', 'baz': 'baz'})
+
+def customized_context_view(request):
+    response = library_view(request)
+    response.template_context.update({'bar': 'bar', 'baz': 'spam'})
+    return response
+
+def customized_template_view(request):
+    response = library_view(request)
+    response.template_name = Template('context does not matter')
+    return response
+
+
+class RenderTest(unittest.TestCase):
+
+    def setUp(self):
+        self.factory = RequestFactory()
+        self.request = self.factory.get('/')
+
+    def test_library_view(self):
+        response = library_view(self.request)
+        self.assertEqual(response.content, 'foo..baz.True\n')
+
+    def test_customized_context(self):
+        response = customized_context_view(self.request)
+        self.assertEqual(response.content, 'foo.bar.spam.True\n')
+
+    def test_customized_template(self):
+        response = customized_template_view(self.request)
+        self.assertEqual(response.content, 'context does not matter')
+
+    def custom_status_and_content_type(self):
+        response = render(self.request, 'base.html', 'application/json', 504)
+        self.assertEqual(response.status_code, 504)
+        self.assertEqual(response['content-type', 'application/json'])
