Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

queryset-refactor: Moved the Query subclasses into their own file.

Trying to keep file lengths to something manageable.


git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@7164 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 231e735c07db00dbb6f20f4459cd9da93af4c201 1 parent 014373b
Malcolm Tredinnick authored February 27, 2008
1  django/db/models/sql/__init__.py
... ...
@@ -1,4 +1,5 @@
1 1
 from query import *
  2
+from subqueries import *
2 3
 from where import AND, OR
3 4
 
4 5
 __all__ = ['Query', 'AND', 'OR']
40  django/db/models/sql/constants.py
... ...
@@ -0,0 +1,40 @@
  1
+import re
  2
+
  3
+# Valid query types (a dictionary is used for speedy lookups).
  4
+QUERY_TERMS = dict([(x, None) for x in (
  5
+    'exact', 'iexact', 'contains', 'icontains', 'gt', 'gte', 'lt', 'lte', 'in',
  6
+    'startswith', 'istartswith', 'endswith', 'iendswith', 'range', 'year',
  7
+    'month', 'day', 'isnull', 'search', 'regex', 'iregex',
  8
+    )])
  9
+
  10
+# Size of each "chunk" for get_iterator calls.
  11
+# Larger values are slightly faster at the expense of more storage space.
  12
+GET_ITERATOR_CHUNK_SIZE = 100
  13
+
  14
+# Separator used to split filter strings apart.
  15
+LOOKUP_SEP = '__'
  16
+
  17
+# Constants to make looking up tuple values clearer.
  18
+# Join lists
  19
+TABLE_NAME = 0
  20
+RHS_ALIAS = 1
  21
+JOIN_TYPE = 2
  22
+LHS_ALIAS = 3
  23
+LHS_JOIN_COL = 4
  24
+RHS_JOIN_COL = 5
  25
+# Alias map lists
  26
+ALIAS_TABLE = 0
  27
+ALIAS_REFCOUNT = 1
  28
+ALIAS_JOIN = 2
  29
+ALIAS_NULLABLE=3
  30
+
  31
+# How many results to expect from a cursor.execute call
  32
+MULTI = 'multi'
  33
+SINGLE = 'single'
  34
+
  35
+ORDER_PATTERN = re.compile(r'\?|[-+]?\w+$')
  36
+ORDER_DIR = {
  37
+    'ASC': ('ASC', 'DESC'),
  38
+    'DESC': ('DESC', 'ASC')}
  39
+
  40
+
7  django/db/models/sql/datastructures.py
@@ -9,6 +9,13 @@ class EmptyResultSet(Exception):
9 9
 class FullResultSet(Exception):
10 10
     pass
11 11
 
  12
+class Empty(object):
  13
+    pass
  14
+
  15
+class RawValue(object):
  16
+    def __init__(self, value):
  17
+        self.value = value
  18
+
12 19
 class Aggregate(object):
13 20
     """
14 21
     Base class for all aggregate-related classes (min, max, avg, count, sum).
316  django/db/models/sql/query.py
@@ -8,69 +8,23 @@
8 8
 """
9 9
 
10 10
 import copy
11  
-import operator
12  
-import re
13 11
 
14 12
 from django.utils.tree import Node
15 13
 from django.utils.datastructures import SortedDict
16 14
 from django.dispatch import dispatcher
17 15
 from django.db.models import signals
18 16
 from django.db.models.sql.where import WhereNode, EverythingNode, AND, OR
19  
-from django.db.models.sql.datastructures import Count, Date
  17
+from django.db.models.sql.datastructures import Count
20 18
 from django.db.models.fields import FieldDoesNotExist, Field, related
21  
-from django.contrib.contenttypes import generic
22 19
 from django.core.exceptions import FieldError
23  
-from datastructures import EmptyResultSet
  20
+from datastructures import EmptyResultSet, Empty
  21
+from constants import *
24 22
 
25 23
 try:
26 24
     reversed
27 25
 except NameError:
28 26
     from django.utils.itercompat import reversed    # For python 2.3.
29 27
 
30  
-# Valid query types (a dictionary is used for speedy lookups).
31  
-QUERY_TERMS = dict([(x, None) for x in (
32  
-    'exact', 'iexact', 'contains', 'icontains', 'gt', 'gte', 'lt', 'lte', 'in',
33  
-    'startswith', 'istartswith', 'endswith', 'iendswith', 'range', 'year',
34  
-    'month', 'day', 'isnull', 'search', 'regex', 'iregex',
35  
-    )])
36  
-
37  
-# Size of each "chunk" for get_iterator calls.
38  
-# Larger values are slightly faster at the expense of more storage space.
39  
-GET_ITERATOR_CHUNK_SIZE = 100
40  
-
41  
-# Separator used to split filter strings apart.
42  
-LOOKUP_SEP = '__'
43  
-
44  
-# Constants to make looking up tuple values clearer.
45  
-# Join lists
46  
-TABLE_NAME = 0
47  
-RHS_ALIAS = 1
48  
-JOIN_TYPE = 2
49  
-LHS_ALIAS = 3
50  
-LHS_JOIN_COL = 4
51  
-RHS_JOIN_COL = 5
52  
-# Alias map lists
53  
-ALIAS_TABLE = 0
54  
-ALIAS_REFCOUNT = 1
55  
-ALIAS_JOIN = 2
56  
-ALIAS_NULLABLE=3
57  
-
58  
-# How many results to expect from a cursor.execute call
59  
-MULTI = 'multi'
60  
-SINGLE = 'single'
61  
-
62  
-ORDER_PATTERN = re.compile(r'\?|[-+]?\w+$')
63  
-ORDER_DIR = {
64  
-    'ASC': ('ASC', 'DESC'),
65  
-    'DESC': ('DESC', 'ASC')}
66  
-
67  
-class Empty(object):
68  
-    pass
69  
-
70  
-class RawValue(object):
71  
-    def __init__(self, value):
72  
-        self.value = value
73  
-
74 28
 class Query(object):
75 29
     """
76 30
     A single SQL query.
@@ -200,6 +154,7 @@ def get_count(self):
200 154
         """
201 155
         Performs a COUNT() query using the current filter constraints.
202 156
         """
  157
+        from subqueries import CountQuery
203 158
         obj = self.clone()
204 159
         obj.clear_ordering(True)
205 160
         obj.clear_limits()
@@ -1126,269 +1081,6 @@ def execute_sql(self, result_type=MULTI):
1126 1081
         # The MULTI case.
1127 1082
         return results_iter(cursor)
1128 1083
 
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  
-
1392 1084
 def get_order_dir(field, default='ASC'):
1393 1085
     """
1394 1086
     Returns the field name and direction for an order specification. For
276  django/db/models/sql/subqueries.py
... ...
@@ -0,0 +1,276 @@
  1
+"""
  2
+Query subclasses which provide extra functionality beyond simple data retrieval.
  3
+"""
  4
+from django.contrib.contenttypes import generic
  5
+from django.core.exceptions import FieldError
  6
+from django.db.models.sql.constants import *
  7
+from django.db.models.sql.datastructures import RawValue, Date
  8
+from django.db.models.sql.query import Query
  9
+from django.db.models.sql.where import AND
  10
+
  11
+__all__ = ['DeleteQuery', 'UpdateQuery', 'InsertQuery', 'DateQuery',
  12
+        'CountQuery']
  13
+
  14
+class DeleteQuery(Query):
  15
+    """
  16
+    Delete queries are done through this class, since they are more constrained
  17
+    than general queries.
  18
+    """
  19
+    def as_sql(self):
  20
+        """
  21
+        Creates the SQL for this query. Returns the SQL string and list of
  22
+        parameters.
  23
+        """
  24
+        assert len(self.tables) == 1, \
  25
+                "Can only delete from one table at a time."
  26
+        result = ['DELETE FROM %s' % self.tables[0]]
  27
+        where, params = self.where.as_sql()
  28
+        result.append('WHERE %s' % where)
  29
+        return ' '.join(result), tuple(params)
  30
+
  31
+    def do_query(self, table, where):
  32
+        self.tables = [table]
  33
+        self.where = where
  34
+        self.execute_sql(None)
  35
+
  36
+    def delete_batch_related(self, pk_list):
  37
+        """
  38
+        Set up and execute delete queries for all the objects related to the
  39
+        primary key values in pk_list. To delete the objects themselves, use
  40
+        the delete_batch() method.
  41
+
  42
+        More than one physical query may be executed if there are a
  43
+        lot of values in pk_list.
  44
+        """
  45
+        cls = self.model
  46
+        for related in cls._meta.get_all_related_many_to_many_objects():
  47
+            if not isinstance(related.field, generic.GenericRelation):
  48
+                for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
  49
+                    where = self.where_class()
  50
+                    where.add((None, related.field.m2m_reverse_name(),
  51
+                            related.field, 'in',
  52
+                            pk_list[offset : offset+GET_ITERATOR_CHUNK_SIZE]),
  53
+                            AND)
  54
+                    self.do_query(related.field.m2m_db_table(), where)
  55
+
  56
+        for f in cls._meta.many_to_many:
  57
+            w1 = self.where_class()
  58
+            if isinstance(f, generic.GenericRelation):
  59
+                from django.contrib.contenttypes.models import ContentType
  60
+                field = f.rel.to._meta.get_field(f.content_type_field_name)
  61
+                w1.add((None, field.column, field, 'exact',
  62
+                        ContentType.objects.get_for_model(cls).id), AND)
  63
+            for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
  64
+                where = self.where_class()
  65
+                where.add((None, f.m2m_column_name(), f, 'in',
  66
+                        pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]),
  67
+                        AND)
  68
+                if w1:
  69
+                    where.add(w1, AND)
  70
+                self.do_query(f.m2m_db_table(), where)
  71
+
  72
+    def delete_batch(self, pk_list):
  73
+        """
  74
+        Set up and execute delete queries for all the objects in pk_list. This
  75
+        should be called after delete_batch_related(), if necessary.
  76
+
  77
+        More than one physical query may be executed if there are a
  78
+        lot of values in pk_list.
  79
+        """
  80
+        for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
  81
+            where = self.where_class()
  82
+            field = self.model._meta.pk
  83
+            where.add((None, field.column, field, 'in',
  84
+                    pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]), AND)
  85
+            self.do_query(self.model._meta.db_table, where)
  86
+
  87
+class UpdateQuery(Query):
  88
+    """
  89
+    Represents an "update" SQL query.
  90
+    """
  91
+    def __init__(self, *args, **kwargs):
  92
+        super(UpdateQuery, self).__init__(*args, **kwargs)
  93
+        self._setup_query()
  94
+
  95
+    def _setup_query(self):
  96
+        """
  97
+        Run on initialisation and after cloning.
  98
+        """
  99
+        self.values = []
  100
+
  101
+    def as_sql(self):
  102
+        """
  103
+        Creates the SQL for this query. Returns the SQL string and list of
  104
+        parameters.
  105
+        """
  106
+        self.select_related = False
  107
+        self.pre_sql_setup()
  108
+
  109
+        if len(self.tables) != 1:
  110
+            # We can only update one table at a time, so we need to check that
  111
+            # only one alias has a nonzero refcount.
  112
+            table = None
  113
+            for alias_list in self.table_map.values():
  114
+                for alias in alias_list:
  115
+                    if self.alias_map[alias][ALIAS_REFCOUNT]:
  116
+                        if table:
  117
+                            raise FieldError('Updates can only access a single database table at a time.')
  118
+                        table = alias
  119
+        else:
  120
+            table = self.tables[0]
  121
+
  122
+        qn = self.quote_name_unless_alias
  123
+        result = ['UPDATE %s' % qn(table)]
  124
+        result.append('SET')
  125
+        values, update_params = [], []
  126
+        for name, val in self.values:
  127
+            if val is not None:
  128
+                values.append('%s = %%s' % qn(name))
  129
+                update_params.append(val)
  130
+            else:
  131
+                values.append('%s = NULL' % qn(name))
  132
+        result.append(', '.join(values))
  133
+        where, params = self.where.as_sql()
  134
+        if where:
  135
+            result.append('WHERE %s' % where)
  136
+        return ' '.join(result), tuple(update_params + params)
  137
+
  138
+    def clear_related(self, related_field, pk_list):
  139
+        """
  140
+        Set up and execute an update query that clears related entries for the
  141
+        keys in pk_list.
  142
+
  143
+        This is used by the QuerySet.delete_objects() method.
  144
+        """
  145
+        for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
  146
+            self.where = self.where_class()
  147
+            f = self.model._meta.pk
  148
+            self.where.add((None, f.column, f, 'in',
  149
+                    pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]),
  150
+                    AND)
  151
+            self.values = [(related_field.column, None)]
  152
+            self.execute_sql(None)
  153
+
  154
+    def add_update_values(self, values):
  155
+        from django.db.models.base import Model
  156
+        for name, val in values.items():
  157
+            field, model, direct, m2m = self.model._meta.get_field_by_name(name)
  158
+            if not direct or m2m:
  159
+                # Can only update non-relation fields and foreign keys.
  160
+                raise fieldError('Cannot update model field %r (only non-relations and foreign keys permitted).' % field)
  161
+            if field.rel and isinstance(val, Model):
  162
+                val = val.pk
  163
+            self.values.append((field.column, val))
  164
+
  165
+class InsertQuery(Query):
  166
+    def __init__(self, *args, **kwargs):
  167
+        super(InsertQuery, self).__init__(*args, **kwargs)
  168
+        self._setup_query()
  169
+
  170
+    def _setup_query(self):
  171
+        """
  172
+        Run on initialisation and after cloning.
  173
+        """
  174
+        self.columns = []
  175
+        self.values = []
  176
+
  177
+    def as_sql(self):
  178
+        self.select_related = False
  179
+        self.pre_sql_setup()
  180
+        qn = self.quote_name_unless_alias
  181
+        result = ['INSERT INTO %s' % qn(self.tables[0])]
  182
+        result.append('(%s)' % ', '.join([qn(c) for c in self.columns]))
  183
+        result.append('VALUES (')
  184
+        params = []
  185
+        first = True
  186
+        for value in self.values:
  187
+            prefix = not first and ', ' or ''
  188
+            if isinstance(value, RawValue):
  189
+                result.append('%s%s' % (prefix, value.value))
  190
+            else:
  191
+                result.append('%s%%s' % prefix)
  192
+                params.append(value)
  193
+            first = False
  194
+        result.append(')')
  195
+        return ' '.join(result), tuple(params)
  196
+
  197
+    def execute_sql(self, return_id=False):
  198
+        cursor = super(InsertQuery, self).execute_sql(None)
  199
+        if return_id:
  200
+            return self.connection.ops.last_insert_id(cursor, self.tables[0],
  201
+                    self.model._meta.pk.column)
  202
+
  203
+    def insert_values(self, insert_values, raw_values=False):
  204
+        """
  205
+        Set up the insert query from the 'insert_values' dictionary. The
  206
+        dictionary gives the model field names and their target values.
  207
+
  208
+        If 'raw_values' is True, the values in the 'insert_values' dictionary
  209
+        are inserted directly into the query, rather than passed as SQL
  210
+        parameters. This provides a way to insert NULL and DEFAULT keywords
  211
+        into the query, for example.
  212
+        """
  213
+        func = lambda x: self.model._meta.get_field_by_name(x)[0].column
  214
+        # keys() and values() return items in the same order, providing the
  215
+        # dictionary hasn't changed between calls. So these lines work as
  216
+        # intended.
  217
+        for name in insert_values:
  218
+            if name == 'pk':
  219
+                name = self.model._meta.pk.name
  220
+            self.columns.append(func(name))
  221
+        if raw_values:
  222
+            self.values.extend([RawValue(v) for v in insert_values.values()])
  223
+        else:
  224
+            self.values.extend(insert_values.values())
  225
+
  226
+class DateQuery(Query):
  227
+    """
  228
+    A DateQuery is a normal query, except that it specifically selects a single
  229
+    date field. This requires some special handling when converting the results
  230
+    back to Python objects, so we put it in a separate class.
  231
+    """
  232
+    def results_iter(self):
  233
+        """
  234
+        Returns an iterator over the results from executing this query.
  235
+        """
  236
+        resolve_columns = hasattr(self, 'resolve_columns')
  237
+        if resolve_columns:
  238
+            from django.db.models.fields import DateTimeField
  239
+            fields = [DateTimeField()]
  240
+        else:
  241
+            from django.db.backends.util import typecast_timestamp
  242
+            needs_string_cast = self.connection.features.needs_datetime_string_cast
  243
+
  244
+        for rows in self.execute_sql(MULTI):
  245
+            for row in rows:
  246
+                date = row[0]
  247
+                if resolve_columns:
  248
+                    date = self.resolve_columns([date], fields)[0]
  249
+                elif needs_string_cast:
  250
+                    date = typecast_timestamp(str(date))
  251
+                yield date
  252
+
  253
+    def add_date_select(self, column, lookup_type, order='ASC'):
  254
+        """
  255
+        Converts the query into a date extraction query.
  256
+        """
  257
+        alias = self.join((None, self.model._meta.db_table, None, None))
  258
+        select = Date((alias, column), lookup_type,
  259
+                self.connection.ops.date_trunc_sql)
  260
+        self.select = [select]
  261
+        self.distinct = True
  262
+        self.order_by = order == 'ASC' and [1] or [-1]
  263
+
  264
+class CountQuery(Query):
  265
+    """
  266
+    A CountQuery knows how to take a normal query which would select over
  267
+    multiple distinct columns and turn it into SQL that can be used on a
  268
+    variety of backends (it requires a select in the FROM clause).
  269
+    """
  270
+    def get_from_clause(self):
  271
+        result, params = self._query.as_sql()
  272
+        return ['(%s) AS A1' % result], params
  273
+
  274
+    def get_ordering(self):
  275
+        return ()
  276
+

0 notes on commit 231e735

Please sign in to comment.
Something went wrong with that request. Please try again.