Django

Code

Changeset 7164

Show
Ignore:
Timestamp:
02/26/08 20:15:41 (11 months ago)
Author:
mtredinnick
Message:

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

Trying to keep file lengths to something manageable.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/queryset-refactor/django/db/models/sql/datastructures.py

    r6967 r7164  
    99class FullResultSet(Exception): 
    1010    pass 
     11 
     12class Empty(object): 
     13    pass 
     14 
     15class RawValue(object): 
     16    def __init__(self, value): 
     17        self.value = value 
    1118 
    1219class Aggregate(object): 
  • django/branches/queryset-refactor/django/db/models/sql/__init__.py

    r6116 r7164  
    11from query import * 
     2from subqueries import * 
    23from where import AND, OR 
    34 
  • django/branches/queryset-refactor/django/db/models/sql/query.py

    r7163 r7164  
    99 
    1010import copy 
    11 import operator 
    12 import re 
    1311 
    1412from django.utils.tree import Node 
     
    1715from django.db.models import signals 
    1816from django.db.models.sql.where import WhereNode, EverythingNode, AND, OR 
    19 from django.db.models.sql.datastructures import Count, Date 
     17from django.db.models.sql.datastructures import Count 
    2018from django.db.models.fields import FieldDoesNotExist, Field, related 
    21 from django.contrib.contenttypes import generic 
    2219from django.core.exceptions import FieldError 
    23 from datastructures import EmptyResultSet 
     20from datastructures import EmptyResultSet, Empty 
     21from constants import * 
    2422 
    2523try: 
     
    2725except NameError: 
    2826    from django.utils.itercompat import reversed    # For python 2.3. 
    29  
    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 
    7327 
    7428class Query(object): 
     
    201155        Performs a COUNT() query using the current filter constraints. 
    202156        """ 
     157        from subqueries import CountQuery 
    203158        obj = self.clone() 
    204159        obj.clear_ordering(True) 
     
    11271082        return results_iter(cursor) 
    11281083 
    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  
    13921084def get_order_dir(field, default='ASC'): 
    13931085    """