Index: django/contrib/gis/db/models/sql/where.py
===================================================================
--- django/contrib/gis/db/models/sql/where.py	(revision 4753)
+++ django/contrib/gis/db/models/sql/where.py	(working copy)
@@ -46,6 +46,14 @@
             data, params = lvalue.process(lookup_type, params_or_value, connection)
 	    alias, col, db_type = data
             spatial_sql = connection.ops.spatial_lookup_sql(data, lookup_type, params_or_value, lvalue.field, qn)
+
+            tables = [alias]
+            if hasattr(params, 'as_sql'):
+                if hasattr(params, 'tables'):
+                    tables.extend(params.tables)
+                extra, params = params.as_sql(qn, connection)
+                spatial_sql = spatial_sql % extra
+                return spatial_sql, params, tables
             return spatial_sql, params, [alias]
         else:
             return super(GeoWhereNode, self).make_atom(child, qn, connection)
Index: django/contrib/gis/db/models/fields.py
===================================================================
--- django/contrib/gis/db/models/fields.py	(revision 4753)
+++ django/contrib/gis/db/models/fields.py	(working copy)
@@ -1,5 +1,6 @@
 from django.db.models.fields import Field
 from django.db.models.sql.expressions import SQLEvaluator
+from django.db.models.query_utils import QueryWrapper
 from django.utils.translation import ugettext_lazy as _
 from django.contrib.gis import forms
 from django.contrib.gis.db.models.proxy import GeometryProxy
@@ -148,6 +149,8 @@
         """
         if isinstance(value, SQLEvaluator):
             return value
+        elif hasattr(value, 'query'):
+            return value
         elif isinstance(value, (tuple, list)):
             geom = value[0]
             seq_value = True
@@ -234,6 +237,16 @@
                     pass
                 else:
                     params += value[1:]
+            elif hasattr(value, 'as_sql') or hasattr(value, '_as_sql'):
+                # If the value has a relabel_aliases method, it will need to
+                # be invoked before the final SQL is evaluated
+                if hasattr(value, 'relabel_aliases'):
+                    return value
+                if hasattr(value, 'as_sql'):
+                    sql, params = value.as_sql()
+                else:
+                    sql, params = value._as_sql(connection=connection)
+                subselect_sql = connection.ops.wrap_spatial_subselect(sql)
+                return QueryWrapper((subselect_sql), params, value.query.tables)
             elif isinstance(value, SQLEvaluator):
                 params = []
             else:
Index: django/contrib/gis/db/backends/spatialite/operations.py
===================================================================
--- django/contrib/gis/db/backends/spatialite/operations.py	(revision 4753)
+++ django/contrib/gis/db/backends/spatialite/operations.py	(working copy)
@@ -188,6 +188,8 @@
         """
         def transform_value(value, srid):
             return not (value is None or value.srid == srid)
+        if hasattr(value, 'query'):
+	    return '%s'
         if hasattr(value, 'expression'):
             if transform_value(value, f.srid):
                 placeholder = '%s(%%s, %s)' % (self.transform, f.srid)
Index: django/contrib/gis/db/backends/mysql/operations.py
===================================================================
--- django/contrib/gis/db/backends/mysql/operations.py	(revision 4753)
+++ django/contrib/gis/db/backends/mysql/operations.py	(working copy)
@@ -41,6 +41,8 @@
         MySQL does not support spatial transformations, there is no need to
         modify the placeholder based on the contents of the given value.
         """
+	if hasattr(value, 'query'):
+	    return '%s'
         if hasattr(value, 'expression'):
             placeholder = '%s.%s' % tuple(map(self.quote_name, value.cols[value.expression]))
         else:
Index: django/contrib/gis/db/backends/oracle/operations.py
===================================================================
--- django/contrib/gis/db/backends/oracle/operations.py	(revision 4753)
+++ django/contrib/gis/db/backends/oracle/operations.py	(working copy)
@@ -205,7 +205,8 @@
         """
         if value is None:
             return 'NULL'
-
+        if hasattr(value, 'query'):
+	    return '%s'
         def transform_value(val, srid):
             return val.srid != srid
 
Index: django/contrib/gis/db/backends/postgis/operations.py
===================================================================
--- django/contrib/gis/db/backends/postgis/operations.py	(revision 4753)
+++ django/contrib/gis/db/backends/postgis/operations.py	(working copy)
@@ -387,7 +387,7 @@
         SRID of the field.  Specifically, this routine will substitute in the
         ST_Transform() function call.
         """
-        if value is None or value.srid == f.srid:
+        if value is None or hasattr(value, 'query') or value.srid == f.srid:
             placeholder = '%s'
         else:
             # Adding Transform() to the SQL placeholder.
Index: backends/base.py
===================================================================
--- backends/base.py	(revision 4356)
+++ backends/base.py	(working copy)
@@ -131,6 +131,9 @@
     def spatial_ref_sys(self):
         raise NotImplementedError
 
+    def wrap_spatial_subselect(self, subselect_sql):
+        return '(%s)' % subselect_sql
+
 class SpatialRefSysMixin(object):
     """
     The SpatialRefSysMixin is a class used by the database-dependent
Index: backends/postgis/func.sql
===================================================================
--- backends/postgis/func.sql	(revision 0)
+++ backends/postgis/func.sql	(revision 0)
@@ -0,0 +1,10 @@
+/* function that will immediately execute subselect that returns spatial geometry */
+create or replace function execute_spatial_subselect(a_query text)
+returns geometry as
+$$
+  DECLARE poly geometry;
+  BEGIN
+    EXECUTE a_query INTO poly;
+    RETURN poly;
+  END
+$$ LANGUAGE 'plpgsql' IMMUTABLE;
Index: backends/postgis/operations.py
===================================================================
--- backends/postgis/operations.py	(revision 4754)
+++ backends/postgis/operations.py	(working copy)
@@ -589,3 +589,6 @@
     def spatial_ref_sys(self):
         from django.contrib.gis.db.backends.postgis.models import SpatialRefSys
         return SpatialRefSys
+
+    def wrap_spatial_subselect(self, subselect_sql):
+        # use dollar quoting to prevent problems with quoted params
+        return '(execute_spatial_subselect($$%s$$))' % subselect_sql
Index: tests/modeltests/spatial_subquery/__init__.py
===================================================================
Index: tests/modeltests/spatial_subquery/models.py
===================================================================
--- tests/modeltests/spatial_subquery/models.py	(revision 0)
+++ tests/modeltests/spatial_subquery/models.py	(revision 4754)
@@ -0,0 +1,35 @@
+"""
+Tests for spatial subqueries
+"""
+
+from django.contrib.gis.db import models
+
+class Address(models.Model):
+    city = models.CharField(max_length=50)
+    location = models.PointField(srid=4326)
+    objects = models.GeoManager()
+    def __unicode__(self):
+        return u"%s" % (self.city)
+
+class District(models.Model):
+    name = models.CharField(max_length=50)
+    area = models.MultiPolygonField(srid=4326)
+    def __unicode__(self):
+        return u"%s" % (self.name)
+
+__test__ = {'API_TESTS':"""
+
+>>> qset = Address.objects.filter(location__within = District.objects.filter(name='Boston area').values('area'))
+>>> str(qset.query)
+'SELECT "spatial_subquery_address"."id", "spatial_subquery_address"."city", "spatial_subquery_address"."location" FROM "spatial_subquery_address" WHERE ST_Within("spatial_subquery_address"."location", (execute_spatial_subselect($$SELECT U0."area" FROM "spatial_subquery_district" U0 WHERE U0."name" = Boston area $$)))'
+
+>>> list(qset)
+[]
+
+>>> qset.query.tables	# check presence of both tables for johnny cache patch
+['spatial_subquery_address']
+
+>>> qset.query.where.result_tables # also QueryWrapper tables should be present
+['spatial_subquery_address', 'spatial_subquery_district']
+
+"""}
