diff --git a/django/http/__init__.py b/django/http/__init__.py
index 60a92d5..c48be9f 100644
--- a/django/http/__init__.py
+++ b/django/http/__init__.py
@@ -1,6 +1,7 @@
 from __future__ import absolute_import
 
 import datetime
+import hashlib
 import os
 import re
 import sys
@@ -575,6 +576,7 @@ class HttpResponse(object):
                     self._charset)
         self.content = content
         self.cookies = SimpleCookie()
+        self.not_modified_checked = False
         if status:
             self.status_code = status
 
@@ -682,6 +684,13 @@ class HttpResponse(object):
         self.set_cookie(key, max_age=0, path=path, domain=domain,
                         expires='Thu, 01-Jan-1970 00:00:00 GMT')
 
+    def set_etag(self):
+        """
+        Compute the ETag header (if not already set)
+        """
+        if settings.USE_ETAGS and not self.has_header('ETag'):
+            self['ETag'] = '"%s"' % hashlib.md5(self.content).hexdigest()
+
     def _get_content(self):
         if self.has_header('Content-Encoding'):
             return ''.join([str(e) for e in self._container])
diff --git a/django/middleware/common.py b/django/middleware/common.py
index d894ec8..abe97c8 100644
--- a/django/middleware/common.py
+++ b/django/middleware/common.py
@@ -1,4 +1,3 @@
-import hashlib
 import re
 
 from django.conf import settings
@@ -109,17 +108,13 @@ class CommonMiddleware(object):
                 return response
 
         # Use ETags, if requested.
-        if settings.USE_ETAGS:
-            if response.has_header('ETag'):
-                etag = response['ETag']
-            else:
-                etag = '"%s"' % hashlib.md5(response.content).hexdigest()
-            if response.status_code >= 200 and response.status_code < 300 and request.META.get('HTTP_IF_NONE_MATCH') == etag:
+        if settings.USE_ETAGS and response.not_modified_checked is not True:
+            response.set_etag()
+            if response.status_code >= 200 and response.status_code < 300 and \
+                    request.META.get('HTTP_IF_NONE_MATCH') == response['ETag']:
                 cookies = response.cookies
                 response = http.HttpResponseNotModified()
                 response.cookies = cookies
-            else:
-                response['ETag'] = etag
 
         return response
 
diff --git a/django/middleware/http.py b/django/middleware/http.py
index 86e46ce..97c805b 100644
--- a/django/middleware/http.py
+++ b/django/middleware/http.py
@@ -13,6 +13,10 @@ class ConditionalGetMiddleware(object):
         if not response.has_header('Content-Length'):
             response['Content-Length'] = str(len(response.content))
 
+        if response.status_code == 304 or response.not_modified_checked:
+            # Do not check Not Modified again
+            return response
+
         if response.has_header('ETag'):
             if_none_match = request.META.get('HTTP_IF_NONE_MATCH')
             if if_none_match == response['ETag']:
diff --git a/django/template/response.py b/django/template/response.py
index bb0b9cb..6ed7509 100644
--- a/django/template/response.py
+++ b/django/template/response.py
@@ -132,6 +132,12 @@ class SimpleTemplateResponse(HttpResponse):
 
     content = property(_get_content, _set_content)
 
+    def set_etag(self):
+        if not self._is_rendered:
+            self.add_post_render_callback(super(SimpleTemplateResponse, self).set_etag.__func__)
+        else:
+            super(SimpleTemplateResponse, self).set_etag()
+
 
 class TemplateResponse(SimpleTemplateResponse):
     rendering_attrs = SimpleTemplateResponse.rendering_attrs + \
diff --git a/django/utils/cache.py b/django/utils/cache.py
index 7ef3680..6603bfa 100644
--- a/django/utils/cache.py
+++ b/django/utils/cache.py
@@ -93,10 +93,6 @@ def get_max_age(response):
         except (ValueError, TypeError):
             pass
 
-def _set_response_etag(response):
-    response['ETag'] = '"%s"' % hashlib.md5(response.content).hexdigest()
-    return response
-
 def patch_response_headers(response, cache_timeout=None):
     """
     Adds some useful headers to the given HttpResponse object:
@@ -111,11 +107,7 @@ def patch_response_headers(response, cache_timeout=None):
         cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS
     if cache_timeout < 0:
         cache_timeout = 0 # Can't have max-age negative
-    if settings.USE_ETAGS and not response.has_header('ETag'):
-        if hasattr(response, 'render') and callable(response.render):
-            response.add_post_render_callback(_set_response_etag)
-        else:
-            response = _set_response_etag(response)
+    response.set_etag()
     if not response.has_header('Last-Modified'):
         response['Last-Modified'] = http_date()
     if not response.has_header('Expires'):
diff --git a/django/views/decorators/http.py b/django/views/decorators/http.py
index d5c4bff..37a9943 100644
--- a/django/views/decorators/http.py
+++ b/django/views/decorators/http.py
@@ -138,7 +138,7 @@ def condition(etag_func=None, last_modified_func=None):
                         }
                     )
                     response = HttpResponse(status=412)
-                elif (not if_none_match and request.method == "GET" and
+                elif (not (if_none_match and res_etag) and request.method == "GET" and
                         res_last_modified and if_modified_since and
                         res_last_modified <= if_modified_since):
                     response = HttpResponseNotModified()
@@ -151,6 +151,8 @@ def condition(etag_func=None, last_modified_func=None):
                 response['Last-Modified'] = http_date(res_last_modified)
             if res_etag and not response.has_header('ETag'):
                 response['ETag'] = quote_etag(res_etag)
+            # Flag to signal middlewares to not check again
+            response.not_modified_checked = True
 
             return response
 
diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt
index c0ae73e..338f4a5 100644
--- a/docs/ref/request-response.txt
+++ b/docs/ref/request-response.txt
@@ -707,6 +707,12 @@ Methods
     values you used in ``set_cookie()`` -- otherwise the cookie may not be
     deleted.
 
+.. method:: HttpResponse.set_etag()
+
+    .. versionadded:: 1.5
+
+    Compute and set the ETag header from the response content.
+
 .. method:: HttpResponse.write(content)
 
     This method makes an :class:`HttpResponse` instance a file-like object.
diff --git a/docs/topics/conditional-view-processing.txt b/docs/topics/conditional-view-processing.txt
index 1979e89..5e570d1 100644
--- a/docs/topics/conditional-view-processing.txt
+++ b/docs/topics/conditional-view-processing.txt
@@ -91,6 +91,16 @@ for your front page view::
     def front_page(request, blog_id):
         ...
 
+.. admonition:: Relation with the USE_ETAGS setting
+
+    Using the ``condition`` decorator on a view is not dependant on the
+    :setting:`USE_ETAGS` setting. However, if a response has been generated by
+    a view decorated with the ``condition`` decorator, the ETag and Last-Modified
+    headers will not be checked a second time in
+    :class:`~django.middleware.common.CommonMiddleware` or
+    :class:`~django.middleware.http.ConditionalGetMiddleware`.
+
+
 Shortcuts for only computing one value
 ======================================
 
diff --git a/tests/regressiontests/conditional_processing/models.py b/tests/regressiontests/conditional_processing/models.py
index f7f48bc..4739ba6 100644
--- a/tests/regressiontests/conditional_processing/models.py
+++ b/tests/regressiontests/conditional_processing/models.py
@@ -2,6 +2,7 @@
 from datetime import datetime
 
 from django.test import TestCase
+from django.test.utils import override_settings
 from django.utils import unittest
 from django.utils.http import parse_etags, quote_etag, parse_http_date
 
@@ -84,6 +85,10 @@ class ConditionalGet(TestCase):
         self.client.defaults['HTTP_IF_NONE_MATCH'] = '"%s"' % EXPIRED_ETAG
         response = self.client.get('/condition/')
         self.assertFullResponse(response)
+        # If-None-Match header should not disturb condition on last_modify only
+        # See #14722
+        response = self.client.get('/condition/last_modified/')
+        self.assertNotModified(response)
 
     def testSingleCondition1(self):
         self.client.defaults['HTTP_IF_MODIFIED_SINCE'] = LAST_MODIFIED_STR
@@ -128,6 +133,9 @@ class ConditionalGet(TestCase):
         response = self.client.get('/condition/etag/')
         self.assertFullResponse(response, check_last_modified=False)
 
+ConditionalGetWithUseEtags = override_settings(
+    USE_ETAGS = True,
+)(ConditionalGet)
 
 class ETagProcessing(unittest.TestCase):
     def testParsing(self):
