Index: django/db/models/sql/query.py
===================================================================
--- django/db/models/sql/query.py	(revisión: 9787)
+++ django/db/models/sql/query.py	(copia de trabajo)
@@ -19,6 +19,7 @@
 from django.db.models.query_utils import select_related_descend
 from django.db.models.sql import aggregates as base_aggregates_module
 from django.db.models.sql.where import WhereNode, Constraint, EverythingNode, AND, OR
+from django.core.cache import cache
 from django.core.exceptions import FieldError
 from datastructures import EmptyResultSet, Empty, MultiJoin
 from constants import *
@@ -90,6 +91,10 @@
         self.extra_params = ()
         self.extra_order_by = ()
 
+        # Cached queryset atribute. None means no cached
+        self.cache_timeout = None
+        self.cache_key = None
+
     def __str__(self):
         """
         Returns the query as a string of SQL with the parameter values
@@ -190,6 +195,8 @@
         obj.extra_where = self.extra_where
         obj.extra_params = self.extra_params
         obj.extra_order_by = self.extra_order_by
+        obj.cache_timeout = self.cache_timeout
+        obj.cache_key = self.cache_key
         if self.filter_is_sticky and self.used_aliases:
             obj.used_aliases = self.used_aliases.copy()
         else:
@@ -237,7 +244,33 @@
         """
         resolve_columns = hasattr(self, 'resolve_columns')
         fields = None
-        for rows in self.execute_sql(MULTI):
+        if self.cache_timeout is not None:
+            # Check cache for stored objects from an exactly equal query
+            k = str(self)
+            try:
+                import hashlib
+            except ImportError:
+                import sha
+                k = sha.new(k).hexdigest()
+            else:
+                k = hashlib.sha1(k).hexdigest()
+
+            self.cache_key = k
+
+            if cache.has_key(k) and cache.get(k):
+                sql_result = cache.get(k, [])
+            else:
+                cache.set(k, [i for i in self.execute_sql(MULTI)], self.cache_timeout)
+                sql_result = cache.get(k, [])
+                # register this cache key for allowing later invalidation
+                model_cache_key = self.model._meta.cache_key
+                cache_register = cache.get(model_cache_key, [])
+                cache_register.append(k)
+                cache.set(model_cache_key, cache_register)
+        else:
+            sql_result = self.execute_sql(MULTI)
+
+        for rows in sql_result:
             for row in rows:
                 if resolve_columns:
                     if fields is None:
Index: django/db/models/manager.py
===================================================================
--- django/db/models/manager.py	(revisión: 9787)
+++ django/db/models/manager.py	(copia de trabajo)
@@ -3,6 +3,7 @@
 from django.db.models.query import QuerySet, EmptyQuerySet, insert_query
 from django.db.models import signals
 from django.db.models.fields import FieldDoesNotExist
+from django.core.cache import cache
 
 def ensure_default_manager(sender, **kwargs):
     cls = sender
@@ -140,6 +141,16 @@
     def reverse(self, *args, **kwargs):
         return self.get_query_set().reverse(*args, **kwargs)
 
+    def cache(self, timeout=20):
+        return self.get_query_set().cache(timeout=timeout)
+
+    def flush_cache(self):
+        model_cache_key = self.model._meta.cache_key
+        cache_register = cache.get(model_cache_key, [])
+        for cached_queryset_key in cache_register:
+            cache.delete(cached_queryset_key)
+        cache.delete(model_cache_key)
+
     def _insert(self, values, **kwargs):
         return insert_query(self.model, values, **kwargs)
 
Index: django/db/models/options.py
===================================================================
--- django/db/models/options.py	(revisión: 9787)
+++ django/db/models/options.py	(copia de trabajo)
@@ -44,6 +44,7 @@
         self.abstract = False
         self.parents = SortedDict()
         self.duplicate_targets = {}
+        self.cache_key = None
         # Managers that have been inherited from abstract base classes. These
         # are passed onto any children.
         self.abstract_managers = []
@@ -58,6 +59,7 @@
         self.object_name = cls.__name__
         self.module_name = self.object_name.lower()
         self.verbose_name = get_verbose_name(self.object_name)
+        self.cache_key = 'modelcache-%s.%s' % (self.app_label, self.module_name)
 
         # Next, apply any overridden values from 'class Meta'.
         if self.meta:
Index: django/db/models/query.py
===================================================================
--- django/db/models/query.py	(revisión: 9787)
+++ django/db/models/query.py	(copia de trabajo)
@@ -640,6 +640,16 @@
         clone.query.standard_ordering = not clone.query.standard_ordering
         return clone
 
+    def cache(self, timeout=20):
+        """
+        Forces the current queryset to check if a exact equal query has been
+        stored in the cache. The expiration time is the seconds in 'timeout'
+        variable.
+        """
+        clone = self._clone()
+        clone.query.cache_timeout = timeout
+        return clone
+
     ###################
     # PRIVATE METHODS #
     ###################
