Code

Ticket #14483: spatial_subquery.diff

File spatial_subquery.diff, 6.5 KB (added by milosu, 3 years ago)
Line 
1Index: django/contrib/gis/db/models/sql/where.py
2===================================================================
3--- django/contrib/gis/db/models/sql/where.py   (revision 4753)
4+++ django/contrib/gis/db/models/sql/where.py   (working copy)
5@@ -46,6 +46,14 @@
6             data, params = lvalue.process(lookup_type, params_or_value, connection)
7            alias, col, db_type = data
8             spatial_sql = connection.ops.spatial_lookup_sql(data, lookup_type, params_or_value, lvalue.field, qn)
9+
10+            tables = [alias]
11+            if hasattr(params, 'as_sql'):
12+                if hasattr(params, 'tables'):
13+                    tables.extend(params.tables)
14+                extra, params = params.as_sql(qn, connection)
15+                spatial_sql = spatial_sql % extra
16+                return spatial_sql, params, tables
17             return spatial_sql, params, [alias]
18         else:
19             return super(GeoWhereNode, self).make_atom(child, qn, connection)
20Index: django/contrib/gis/db/models/fields.py
21===================================================================
22--- django/contrib/gis/db/models/fields.py      (revision 4753)
23+++ django/contrib/gis/db/models/fields.py      (working copy)
24@@ -1,5 +1,6 @@
25 from django.db.models.fields import Field
26 from django.db.models.sql.expressions import SQLEvaluator
27+from django.db.models.query_utils import QueryWrapper
28 from django.utils.translation import ugettext_lazy as _
29 from django.contrib.gis import forms
30 from django.contrib.gis.db.models.proxy import GeometryProxy
31@@ -148,6 +149,8 @@
32         """
33         if isinstance(value, SQLEvaluator):
34             return value
35+        elif hasattr(value, 'query'):
36+            return value
37         elif isinstance(value, (tuple, list)):
38             geom = value[0]
39             seq_value = True
40@@ -234,6 +237,16 @@
41                     pass
42                 else:
43                     params += value[1:]
44+            elif hasattr(value, 'as_sql') or hasattr(value, '_as_sql'):
45+                # If the value has a relabel_aliases method, it will need to
46+                # be invoked before the final SQL is evaluated
47+                if hasattr(value, 'relabel_aliases'):
48+                    return value
49+                if hasattr(value, 'as_sql'):
50+                    sql, params = value.as_sql()
51+                else:
52+                    sql, params = value._as_sql(connection=connection)
53+                return QueryWrapper(('(%s)' % sql), params, value.query.tables)
54             elif isinstance(value, SQLEvaluator):
55                 params = []
56             else:
57Index: django/contrib/gis/db/backends/spatialite/operations.py
58===================================================================
59--- django/contrib/gis/db/backends/spatialite/operations.py     (revision 4753)
60+++ django/contrib/gis/db/backends/spatialite/operations.py     (working copy)
61@@ -188,6 +188,8 @@
62         """
63         def transform_value(value, srid):
64             return not (value is None or value.srid == srid)
65+        if hasattr(value, 'query'):
66+           return '%s'
67         if hasattr(value, 'expression'):
68             if transform_value(value, f.srid):
69                 placeholder = '%s(%%s, %s)' % (self.transform, f.srid)
70Index: django/contrib/gis/db/backends/mysql/operations.py
71===================================================================
72--- django/contrib/gis/db/backends/mysql/operations.py  (revision 4753)
73+++ django/contrib/gis/db/backends/mysql/operations.py  (working copy)
74@@ -41,6 +41,8 @@
75         MySQL does not support spatial transformations, there is no need to
76         modify the placeholder based on the contents of the given value.
77         """
78+       if hasattr(value, 'query'):
79+           return '%s'
80         if hasattr(value, 'expression'):
81             placeholder = '%s.%s' % tuple(map(self.quote_name, value.cols[value.expression]))
82         else:
83Index: django/contrib/gis/db/backends/oracle/operations.py
84===================================================================
85--- django/contrib/gis/db/backends/oracle/operations.py (revision 4753)
86+++ django/contrib/gis/db/backends/oracle/operations.py (working copy)
87@@ -205,7 +205,8 @@
88         """
89         if value is None:
90             return 'NULL'
91-
92+        if hasattr(value, 'query'):
93+           return '%s'
94         def transform_value(val, srid):
95             return val.srid != srid
96 
97Index: django/contrib/gis/db/backends/postgis/operations.py
98===================================================================
99--- django/contrib/gis/db/backends/postgis/operations.py        (revision 4753)
100+++ django/contrib/gis/db/backends/postgis/operations.py        (working copy)
101@@ -387,7 +387,7 @@
102         SRID of the field.  Specifically, this routine will substitute in the
103         ST_Transform() function call.
104         """
105-        if value is None or value.srid == f.srid:
106+        if value is None or hasattr(value, 'query') or value.srid == f.srid:
107             placeholder = '%s'
108         else:
109             # Adding Transform() to the SQL placeholder.
110Index: tests/modeltests/spatial_subquery/__init__.py
111===================================================================
112Index: tests/modeltests/spatial_subquery/models.py
113===================================================================
114--- tests/modeltests/spatial_subquery/models.py (revision 0)
115+++ tests/modeltests/spatial_subquery/models.py (revision 4754)
116@@ -0,0 +1,35 @@
117+"""
118+Tests for spatial subqueries
119+"""
120+
121+from django.contrib.gis.db import models
122+
123+class Address(models.Model):
124+    city = models.CharField(max_length=50)
125+    location = models.PointField(srid=4326)
126+    objects = models.GeoManager()
127+    def __unicode__(self):
128+        return u"%s" % (self.city)
129+
130+class District(models.Model):
131+    name = models.CharField(max_length=50)
132+    area = models.MultiPolygonField(srid=4326)
133+    def __unicode__(self):
134+        return u"%s" % (self.name)
135+
136+__test__ = {'API_TESTS':"""
137+
138+>>> qset = Address.objects.filter(location__within = District.objects.filter(name='Boston area').values('area'))
139+>>> str(qset.query)
140+'SELECT "spatial_subquery_address"."id", "spatial_subquery_address"."city", "spatial_subquery_address"."location" FROM "spatial_subquery_address" WHERE ST_Within("spatial_subquery_address"."location", (SELECT U0."area" FROM "spatial_subquery_district" U0 WHERE U0."name" = Boston area ))'
141+
142+>>> list(qset)
143+[]
144+
145+>>> qset.query.tables  # check presence of both tables for johnny cache patch
146+['spatial_subquery_address']
147+
148+>>> qset.query.where.result_tables # also QueryWrapper tables should be present
149+['spatial_subquery_address', 'spatial_subquery_district']
150+
151+"""}