5
5
from django .db .models .constants import LOOKUP_SEP
6
6
from django .db .models .sql import compiler
7
7
from django .db .models .sql .constants import GET_ITERATOR_CHUNK_SIZE , MULTI
8
+ from django .utils .functional import cached_property
8
9
9
10
from .base import Cursor
10
11
from .query import MongoQuery , wrap_database_errors
@@ -82,7 +83,15 @@ def _make_result(self, entity, columns, converters, tuple_expected=False):
82
83
result = []
83
84
for name , col in columns :
84
85
field = col .field
85
- value = entity .get (name , NOT_PROVIDED )
86
+ column_alias = getattr (col , "alias" , None )
87
+ obj = (
88
+ # Use the related object...
89
+ entity .get (column_alias , {})
90
+ # ...if this column refers to an object for select_related().
91
+ if column_alias is not None and column_alias != self .collection_name
92
+ else entity
93
+ )
94
+ value = obj .get (name , NOT_PROVIDED )
86
95
if value is NOT_PROVIDED :
87
96
value = field .get_default ()
88
97
elif converters :
@@ -110,10 +119,6 @@ def check_query(self):
110
119
raise NotSupportedError ("QuerySet.distinct() is not supported on MongoDB." )
111
120
if self .query .extra :
112
121
raise NotSupportedError ("QuerySet.extra() is not supported on MongoDB." )
113
- if self .query .select_related :
114
- raise NotSupportedError ("QuerySet.select_related() is not supported on MongoDB." )
115
- if len ([a for a in self .query .alias_map if self .query .alias_refcount [a ]]) > 1 :
116
- raise NotSupportedError ("Queries with multiple tables are not supported on MongoDB." )
117
122
if any (
118
123
isinstance (a , Aggregate ) and not isinstance (a , Count )
119
124
for a in self .query .annotations .values ()
@@ -147,6 +152,7 @@ def build_query(self, columns=None):
147
152
self .check_query ()
148
153
self .setup_query ()
149
154
query = self .query_class (self , columns )
155
+ query .lookup_pipeline = self .get_lookup_pipeline ()
150
156
try :
151
157
query .mongo_query = {"$expr" : self .query .where .as_mql (self , self .connection )}
152
158
except FullResultSet :
@@ -163,9 +169,17 @@ def get_columns(self):
163
169
columns = (
164
170
self .get_default_columns (select_mask ) if self .query .default_cols else self .query .select
165
171
)
172
+ # Populate QuerySet.select_related() data.
173
+ related_columns = []
174
+ if self .query .select_related :
175
+ self .get_related_selections (related_columns , select_mask )
176
+ if related_columns :
177
+ related_columns , _ = zip (* related_columns , strict = True )
178
+
166
179
annotation_idx = 1
167
- result = []
168
- for column in columns :
180
+
181
+ def project_field (column ):
182
+ nonlocal annotation_idx
169
183
if hasattr (column , "target" ):
170
184
# column is a Col.
171
185
target = column .target .column
@@ -174,8 +188,13 @@ def get_columns(self):
174
188
# name for $proj.
175
189
target = f"__annotation{ annotation_idx } "
176
190
annotation_idx += 1
177
- result .append ((target , column ))
178
- return tuple (result ) + tuple (self .query .annotation_select .items ())
191
+ return target , column
192
+
193
+ return (
194
+ tuple (map (project_field , columns ))
195
+ + tuple (self .query .annotation_select .items ())
196
+ + tuple (map (project_field , related_columns ))
197
+ )
179
198
180
199
def _get_ordering (self ):
181
200
"""
@@ -212,8 +231,20 @@ def _get_ordering(self):
212
231
field_ordering .append ((opts .get_field (name ), ascending ))
213
232
return field_ordering
214
233
234
+ @cached_property
235
+ def collection_name (self ):
236
+ return self .query .get_meta ().db_table
237
+
215
238
def get_collection (self ):
216
- return self .connection .get_collection (self .query .get_meta ().db_table )
239
+ return self .connection .get_collection (self .collection_name )
240
+
241
+ def get_lookup_pipeline (self ):
242
+ result = []
243
+ for alias in tuple (self .query .alias_map ):
244
+ if not self .query .alias_refcount [alias ] or self .collection_name == alias :
245
+ continue
246
+ result += self .query .alias_map [alias ].as_mql (self , self .connection )
247
+ return result
217
248
218
249
219
250
class SQLInsertCompiler (SQLCompiler ):
0 commit comments