| 1129 | | class DeleteQuery(Query): |
|---|
| 1130 | | """ |
|---|
| 1131 | | Delete queries are done through this class, since they are more constrained |
|---|
| 1132 | | than general queries. |
|---|
| 1133 | | """ |
|---|
| 1134 | | def as_sql(self): |
|---|
| 1135 | | """ |
|---|
| 1136 | | Creates the SQL for this query. Returns the SQL string and list of |
|---|
| 1137 | | parameters. |
|---|
| 1138 | | """ |
|---|
| 1139 | | assert len(self.tables) == 1, \ |
|---|
| 1140 | | "Can only delete from one table at a time." |
|---|
| 1141 | | result = ['DELETE FROM %s' % self.tables[0]] |
|---|
| 1142 | | where, params = self.where.as_sql() |
|---|
| 1143 | | result.append('WHERE %s' % where) |
|---|
| 1144 | | return ' '.join(result), tuple(params) |
|---|
| 1145 | | |
|---|
| 1146 | | def do_query(self, table, where): |
|---|
| 1147 | | self.tables = [table] |
|---|
| 1148 | | self.where = where |
|---|
| 1149 | | self.execute_sql(None) |
|---|
| 1150 | | |
|---|
| 1151 | | def delete_batch_related(self, pk_list): |
|---|
| 1152 | | """ |
|---|
| 1153 | | Set up and execute delete queries for all the objects related to the |
|---|
| 1154 | | primary key values in pk_list. To delete the objects themselves, use |
|---|
| 1155 | | the delete_batch() method. |
|---|
| 1156 | | |
|---|
| 1157 | | More than one physical query may be executed if there are a |
|---|
| 1158 | | lot of values in pk_list. |
|---|
| 1159 | | """ |
|---|
| 1160 | | cls = self.model |
|---|
| 1161 | | for related in cls._meta.get_all_related_many_to_many_objects(): |
|---|
| 1162 | | if not isinstance(related.field, generic.GenericRelation): |
|---|
| 1163 | | for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): |
|---|
| 1164 | | where = self.where_class() |
|---|
| 1165 | | where.add((None, related.field.m2m_reverse_name(), |
|---|
| 1166 | | related.field, 'in', |
|---|
| 1167 | | pk_list[offset : offset+GET_ITERATOR_CHUNK_SIZE]), |
|---|
| 1168 | | AND) |
|---|
| 1169 | | self.do_query(related.field.m2m_db_table(), where) |
|---|
| 1170 | | |
|---|
| 1171 | | for f in cls._meta.many_to_many: |
|---|
| 1172 | | w1 = self.where_class() |
|---|
| 1173 | | if isinstance(f, generic.GenericRelation): |
|---|
| 1174 | | from django.contrib.contenttypes.models import ContentType |
|---|
| 1175 | | field = f.rel.to._meta.get_field(f.content_type_field_name) |
|---|
| 1176 | | w1.add((None, field.column, field, 'exact', |
|---|
| 1177 | | ContentType.objects.get_for_model(cls).id), AND) |
|---|
| 1178 | | for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): |
|---|
| 1179 | | where = self.where_class() |
|---|
| 1180 | | where.add((None, f.m2m_column_name(), f, 'in', |
|---|
| 1181 | | pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]), |
|---|
| 1182 | | AND) |
|---|
| 1183 | | if w1: |
|---|
| 1184 | | where.add(w1, AND) |
|---|
| 1185 | | self.do_query(f.m2m_db_table(), where) |
|---|
| 1186 | | |
|---|
| 1187 | | def delete_batch(self, pk_list): |
|---|
| 1188 | | """ |
|---|
| 1189 | | Set up and execute delete queries for all the objects in pk_list. This |
|---|
| 1190 | | should be called after delete_batch_related(), if necessary. |
|---|
| 1191 | | |
|---|
| 1192 | | More than one physical query may be executed if there are a |
|---|
| 1193 | | lot of values in pk_list. |
|---|
| 1194 | | """ |
|---|
| 1195 | | for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): |
|---|
| 1196 | | where = self.where_class() |
|---|
| 1197 | | field = self.model._meta.pk |
|---|
| 1198 | | where.add((None, field.column, field, 'in', |
|---|
| 1199 | | pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]), AND) |
|---|
| 1200 | | self.do_query(self.model._meta.db_table, where) |
|---|
| 1201 | | |
|---|
| 1202 | | class UpdateQuery(Query): |
|---|
| 1203 | | """ |
|---|
| 1204 | | Represents an "update" SQL query. |
|---|
| 1205 | | """ |
|---|
| 1206 | | def __init__(self, *args, **kwargs): |
|---|
| 1207 | | super(UpdateQuery, self).__init__(*args, **kwargs) |
|---|
| 1208 | | self._setup_query() |
|---|
| 1209 | | |
|---|
| 1210 | | def _setup_query(self): |
|---|
| 1211 | | """ |
|---|
| 1212 | | Run on initialisation and after cloning. |
|---|
| 1213 | | """ |
|---|
| 1214 | | self.values = [] |
|---|
| 1215 | | |
|---|
| 1216 | | def as_sql(self): |
|---|
| 1217 | | """ |
|---|
| 1218 | | Creates the SQL for this query. Returns the SQL string and list of |
|---|
| 1219 | | parameters. |
|---|
| 1220 | | """ |
|---|
| 1221 | | self.select_related = False |
|---|
| 1222 | | self.pre_sql_setup() |
|---|
| 1223 | | |
|---|
| 1224 | | if len(self.tables) != 1: |
|---|
| 1225 | | # We can only update one table at a time, so we need to check that |
|---|
| 1226 | | # only one alias has a nonzero refcount. |
|---|
| 1227 | | table = None |
|---|
| 1228 | | for alias_list in self.table_map.values(): |
|---|
| 1229 | | for alias in alias_list: |
|---|
| 1230 | | if self.alias_map[alias][ALIAS_REFCOUNT]: |
|---|
| 1231 | | if table: |
|---|
| 1232 | | raise FieldError('Updates can only access a single database table at a time.') |
|---|
| 1233 | | table = alias |
|---|
| 1234 | | else: |
|---|
| 1235 | | table = self.tables[0] |
|---|
| 1236 | | |
|---|
| 1237 | | qn = self.quote_name_unless_alias |
|---|
| 1238 | | result = ['UPDATE %s' % qn(table)] |
|---|
| 1239 | | result.append('SET') |
|---|
| 1240 | | values, update_params = [], [] |
|---|
| 1241 | | for name, val in self.values: |
|---|
| 1242 | | if val is not None: |
|---|
| 1243 | | values.append('%s = %%s' % qn(name)) |
|---|
| 1244 | | update_params.append(val) |
|---|
| 1245 | | else: |
|---|
| 1246 | | values.append('%s = NULL' % qn(name)) |
|---|
| 1247 | | result.append(', '.join(values)) |
|---|
| 1248 | | where, params = self.where.as_sql() |
|---|
| 1249 | | if where: |
|---|
| 1250 | | result.append('WHERE %s' % where) |
|---|
| 1251 | | return ' '.join(result), tuple(update_params + params) |
|---|
| 1252 | | |
|---|
| 1253 | | def clear_related(self, related_field, pk_list): |
|---|
| 1254 | | """ |
|---|
| 1255 | | Set up and execute an update query that clears related entries for the |
|---|
| 1256 | | keys in pk_list. |
|---|
| 1257 | | |
|---|
| 1258 | | This is used by the QuerySet.delete_objects() method. |
|---|
| 1259 | | """ |
|---|
| 1260 | | for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): |
|---|
| 1261 | | self.where = self.where_class() |
|---|
| 1262 | | f = self.model._meta.pk |
|---|
| 1263 | | self.where.add((None, f.column, f, 'in', |
|---|
| 1264 | | pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]), |
|---|
| 1265 | | AND) |
|---|
| 1266 | | self.values = [(related_field.column, None)] |
|---|
| 1267 | | self.execute_sql(None) |
|---|
| 1268 | | |
|---|
| 1269 | | def add_update_values(self, values): |
|---|
| 1270 | | from django.db.models.base import Model |
|---|
| 1271 | | for name, val in values.items(): |
|---|
| 1272 | | field, model, direct, m2m = self.model._meta.get_field_by_name(name) |
|---|
| 1273 | | if not direct or m2m: |
|---|
| 1274 | | # Can only update non-relation fields and foreign keys. |
|---|
| 1275 | | raise fieldError('Cannot update model field %r (only non-relations and foreign keys permitted).' % field) |
|---|
| 1276 | | if field.rel and isinstance(val, Model): |
|---|
| 1277 | | val = val.pk |
|---|
| 1278 | | self.values.append((field.column, val)) |
|---|
| 1279 | | |
|---|
| 1280 | | class InsertQuery(Query): |
|---|
| 1281 | | def __init__(self, *args, **kwargs): |
|---|
| 1282 | | super(InsertQuery, self).__init__(*args, **kwargs) |
|---|
| 1283 | | self._setup_query() |
|---|
| 1284 | | |
|---|
| 1285 | | def _setup_query(self): |
|---|
| 1286 | | """ |
|---|
| 1287 | | Run on initialisation and after cloning. |
|---|
| 1288 | | """ |
|---|
| 1289 | | self.columns = [] |
|---|
| 1290 | | self.values = [] |
|---|
| 1291 | | |
|---|
| 1292 | | def as_sql(self): |
|---|
| 1293 | | self.select_related = False |
|---|
| 1294 | | self.pre_sql_setup() |
|---|
| 1295 | | qn = self.quote_name_unless_alias |
|---|
| 1296 | | result = ['INSERT INTO %s' % qn(self.tables[0])] |
|---|
| 1297 | | result.append('(%s)' % ', '.join([qn(c) for c in self.columns])) |
|---|
| 1298 | | result.append('VALUES (') |
|---|
| 1299 | | params = [] |
|---|
| 1300 | | first = True |
|---|
| 1301 | | for value in self.values: |
|---|
| 1302 | | prefix = not first and ', ' or '' |
|---|
| 1303 | | if isinstance(value, RawValue): |
|---|
| 1304 | | result.append('%s%s' % (prefix, value.value)) |
|---|
| 1305 | | else: |
|---|
| 1306 | | result.append('%s%%s' % prefix) |
|---|
| 1307 | | params.append(value) |
|---|
| 1308 | | first = False |
|---|
| 1309 | | result.append(')') |
|---|
| 1310 | | return ' '.join(result), tuple(params) |
|---|
| 1311 | | |
|---|
| 1312 | | def execute_sql(self, return_id=False): |
|---|
| 1313 | | cursor = super(InsertQuery, self).execute_sql(None) |
|---|
| 1314 | | if return_id: |
|---|
| 1315 | | return self.connection.ops.last_insert_id(cursor, self.tables[0], |
|---|
| 1316 | | self.model._meta.pk.column) |
|---|
| 1317 | | |
|---|
| 1318 | | def insert_values(self, insert_values, raw_values=False): |
|---|
| 1319 | | """ |
|---|
| 1320 | | Set up the insert query from the 'insert_values' dictionary. The |
|---|
| 1321 | | dictionary gives the model field names and their target values. |
|---|
| 1322 | | |
|---|
| 1323 | | If 'raw_values' is True, the values in the 'insert_values' dictionary |
|---|
| 1324 | | are inserted directly into the query, rather than passed as SQL |
|---|
| 1325 | | parameters. This provides a way to insert NULL and DEFAULT keywords |
|---|
| 1326 | | into the query, for example. |
|---|
| 1327 | | """ |
|---|
| 1328 | | func = lambda x: self.model._meta.get_field_by_name(x)[0].column |
|---|
| 1329 | | # keys() and values() return items in the same order, providing the |
|---|
| 1330 | | # dictionary hasn't changed between calls. So these lines work as |
|---|
| 1331 | | # intended. |
|---|
| 1332 | | for name in insert_values: |
|---|
| 1333 | | if name == 'pk': |
|---|
| 1334 | | name = self.model._meta.pk.name |
|---|
| 1335 | | self.columns.append(func(name)) |
|---|
| 1336 | | if raw_values: |
|---|
| 1337 | | self.values.extend([RawValue(v) for v in insert_values.values()]) |
|---|
| 1338 | | else: |
|---|
| 1339 | | self.values.extend(insert_values.values()) |
|---|
| 1340 | | |
|---|
| 1341 | | class DateQuery(Query): |
|---|
| 1342 | | """ |
|---|
| 1343 | | A DateQuery is a normal query, except that it specifically selects a single |
|---|
| 1344 | | date field. This requires some special handling when converting the results |
|---|
| 1345 | | back to Python objects, so we put it in a separate class. |
|---|
| 1346 | | """ |
|---|
| 1347 | | def results_iter(self): |
|---|
| 1348 | | """ |
|---|
| 1349 | | Returns an iterator over the results from executing this query. |
|---|
| 1350 | | """ |
|---|
| 1351 | | resolve_columns = hasattr(self, 'resolve_columns') |
|---|
| 1352 | | if resolve_columns: |
|---|
| 1353 | | from django.db.models.fields import DateTimeField |
|---|
| 1354 | | fields = [DateTimeField()] |
|---|
| 1355 | | else: |
|---|
| 1356 | | from django.db.backends.util import typecast_timestamp |
|---|
| 1357 | | needs_string_cast = self.connection.features.needs_datetime_string_cast |
|---|
| 1358 | | |
|---|
| 1359 | | for rows in self.execute_sql(MULTI): |
|---|
| 1360 | | for row in rows: |
|---|
| 1361 | | date = row[0] |
|---|
| 1362 | | if resolve_columns: |
|---|
| 1363 | | date = self.resolve_columns([date], fields)[0] |
|---|
| 1364 | | elif needs_string_cast: |
|---|
| 1365 | | date = typecast_timestamp(str(date)) |
|---|
| 1366 | | yield date |
|---|
| 1367 | | |
|---|
| 1368 | | def add_date_select(self, column, lookup_type, order='ASC'): |
|---|
| 1369 | | """ |
|---|
| 1370 | | Converts the query into a date extraction query. |
|---|
| 1371 | | """ |
|---|
| 1372 | | alias = self.join((None, self.model._meta.db_table, None, None)) |
|---|
| 1373 | | select = Date((alias, column), lookup_type, |
|---|
| 1374 | | self.connection.ops.date_trunc_sql) |
|---|
| 1375 | | self.select = [select] |
|---|
| 1376 | | self.distinct = True |
|---|
| 1377 | | self.order_by = order == 'ASC' and [1] or [-1] |
|---|
| 1378 | | |
|---|
| 1379 | | class CountQuery(Query): |
|---|
| 1380 | | """ |
|---|
| 1381 | | A CountQuery knows how to take a normal query which would select over |
|---|
| 1382 | | multiple distinct columns and turn it into SQL that can be used on a |
|---|
| 1383 | | variety of backends (it requires a select in the FROM clause). |
|---|
| 1384 | | """ |
|---|
| 1385 | | def get_from_clause(self): |
|---|
| 1386 | | result, params = self._query.as_sql() |
|---|
| 1387 | | return ['(%s) AS A1' % result], params |
|---|
| 1388 | | |
|---|
| 1389 | | def get_ordering(self): |
|---|
| 1390 | | return () |
|---|
| 1391 | | |
|---|