Changeset 1884
- Timestamp:
- 01/09/06 05:01:38 (3 years ago)
- Files:
-
- django/branches/magic-removal/django/db/models/manager.py (modified) (10 diffs)
- django/branches/magic-removal/django/db/models/query.py (modified) (1 diff)
- django/branches/magic-removal/docs/db-api.txt (modified) (1 diff)
- django/branches/magic-removal/tests/modeltests/lookup/models.py (modified) (1 diff)
- django/branches/magic-removal/tests/modeltests/or_lookups/models.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/branches/magic-removal/django/db/models/manager.py
r1835 r1884 6 6 from django.dispatch import dispatcher 7 7 from django.db.models import signals 8 from django.utils.datastructures import SortedDict 8 9 9 10 # Size of each "chunk" for get_iterator calls. … … 48 49 klass._default_manager = self 49 50 50 def _get_sql_clause(self, * *kwargs):51 def _get_sql_clause(self, *args, **kwargs): 51 52 def quote_only_if_word(word): 52 53 if ' ' in word: … … 60 61 select = ["%s.%s" % (backend.quote_name(opts.db_table), backend.quote_name(f.column)) for f in opts.fields] 61 62 tables = (kwargs.get('tables') and [quote_only_if_word(t) for t in kwargs['tables']] or []) 63 joins = SortedDict() 62 64 where = kwargs.get('where') and kwargs['where'][:] or [] 63 65 params = kwargs.get('params') and kwargs['params'][:] or [] 64 66 67 # Convert all the args into SQL. 68 table_count = 0 69 for arg in args: 70 # check that the provided argument is a Query (i.e., it has a get_sql method) 71 if not hasattr(arg, 'get_sql'): 72 raise TypeError, "'%s' is not a valid query argument" % str(arg) 73 74 tables2, joins2, where2, params2 = arg.get_sql(opts) 75 tables.extend(tables2) 76 joins.update(joins2) 77 where.extend(where2) 78 params.extend(params2) 79 80 65 81 # Convert the kwargs into SQL. 66 tables2, joins , where2, params2 = parse_lookup(kwargs.items(), opts)82 tables2, joins2, where2, params2 = parse_lookup(kwargs.items(), opts) 67 83 tables.extend(tables2) 84 joins.update(joins2) 68 85 where.extend(where2) 69 86 params.extend(params2) … … 130 147 return select, " ".join(sql), params 131 148 132 def get_iterator(self, * *kwargs):149 def get_iterator(self, *args, **kwargs): 133 150 # kwargs['select'] is a dictionary, and dictionaries' key order is 134 151 # undefined, so we convert it to a list of tuples internally. … … 136 153 137 154 cursor = connection.cursor() 138 select, sql, params = self._get_sql_clause(* *kwargs)155 select, sql, params = self._get_sql_clause(*args, **kwargs) 139 156 cursor.execute("SELECT " + (kwargs.get('distinct') and "DISTINCT " or "") + ",".join(select) + sql, params) 140 157 fill_cache = kwargs.get('select_related') … … 153 170 yield obj 154 171 155 def get_list(self, * *kwargs):156 return list(self.get_iterator(* *kwargs))157 158 def get_count(self, * *kwargs):172 def get_list(self, *args, **kwargs): 173 return list(self.get_iterator(*args, **kwargs)) 174 175 def get_count(self, *args, **kwargs): 159 176 kwargs['order_by'] = [] 160 177 kwargs['offset'] = None 161 178 kwargs['limit'] = None 162 179 kwargs['select_related'] = False 163 _, sql, params = self._get_sql_clause(* *kwargs)180 _, sql, params = self._get_sql_clause(*args, **kwargs) 164 181 cursor = connection.cursor() 165 182 cursor.execute("SELECT COUNT(*)" + sql, params) 166 183 return cursor.fetchone()[0] 167 184 168 def get_object(self, * *kwargs):169 obj_list = self.get_list(* *kwargs)185 def get_object(self, *args, **kwargs): 186 obj_list = self.get_list(*args, **kwargs) 170 187 if len(obj_list) < 1: 171 188 raise self.klass.DoesNotExist, "%s does not exist for %s" % (self.klass._meta.object_name, kwargs) … … 174 191 175 192 def get_in_bulk(self, *args, **kwargs): 176 id_list = args and args[0] or kwargs.get('id_list', []) 177 assert id_list != [], "get_in_bulk() cannot be passed an empty list." 193 # Separate any list arguments: the first list will be used as the id list; subsequent 194 # lists will be ignored. 195 id_args = filter(lambda arg: isinstance(arg, list), args) 196 # Separate any non-list arguments: these are assumed to be query arguments 197 sql_args = filter(lambda arg: not isinstance(arg, list), args) 198 199 id_list = id_args and id_args[0] or kwargs.get('id_list', []) 200 assert id_list != [], "get_in_bulk() cannot be passed an empty ID list." 178 201 kwargs['where'] = ["%s.%s IN (%s)" % (backend.quote_name(self.klass._meta.db_table), backend.quote_name(self.klass._meta.pk.column), ",".join(['%s'] * len(id_list)))] 179 202 kwargs['params'] = id_list 180 obj_list = self.get_list(* *kwargs)203 obj_list = self.get_list(*sql_args, **kwargs) 181 204 return dict([(getattr(o, self.klass._meta.pk.attname), o) for o in obj_list]) 182 205 183 def get_values_iterator(self, * *kwargs):206 def get_values_iterator(self, *args, **kwargs): 184 207 # select_related and select aren't supported in get_values(). 185 208 kwargs['select_related'] = False … … 193 216 194 217 cursor = connection.cursor() 195 _, sql, params = self._get_sql_clause(* *kwargs)218 _, sql, params = self._get_sql_clause(*args, **kwargs) 196 219 select = ['%s.%s' % (backend.quote_name(self.klass._meta.db_table), backend.quote_name(f)) for f in fields] 197 220 cursor.execute("SELECT " + (kwargs.get('distinct') and "DISTINCT " or "") + ",".join(select) + sql, params) … … 203 226 yield dict(zip(fields, row)) 204 227 205 def get_values(self, * *kwargs):206 return list(self.get_values_iterator(* *kwargs))207 208 def __get_latest(self, * *kwargs):228 def get_values(self, *args, **kwargs): 229 return list(self.get_values_iterator(*args, **kwargs)) 230 231 def __get_latest(self, *args, **kwargs): 209 232 kwargs['order_by'] = ('-' + self.klass._meta.get_latest_by,) 210 233 kwargs['limit'] = 1 211 return self.get_object(* *kwargs)234 return self.get_object(*args, **kwargs) 212 235 213 236 def __get_date_list(self, field, *args, **kwargs): 237 # Separate any string arguments: the first will be used as the kind 238 kind_args = filter(lambda arg: isinstance(arg, str), args) 239 # Separate any non-list arguments: these are assumed to be query arguments 240 sql_args = filter(lambda arg: not isinstance(arg, str), args) 241 214 242 from django.db.backends.util import typecast_timestamp 215 kind = args and args[0] or kwargs['kind']243 kind = kind_args and kind_args[0] or kwargs.get(['kind'],"") 216 244 assert kind in ("month", "year", "day"), "'kind' must be one of 'year', 'month' or 'day'." 217 245 order = 'ASC' … … 224 252 kwargs.setdefault('where', []).append('%s.%s IS NOT NULL' % \ 225 253 (backend.quote_name(self.klass._meta.db_table), backend.quote_name(field.column))) 226 select, sql, params = self._get_sql_clause(* *kwargs)254 select, sql, params = self._get_sql_clause(*sql_args, **kwargs) 227 255 sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1 %s' % \ 228 256 (backend.get_date_trunc_sql(kind, '%s.%s' % (backend.quote_name(self.klass._meta.db_table), django/branches/magic-removal/django/db/models/query.py
r1856 r1884 199 199 pass 200 200 elif kwarg == 'complex': 201 if not hasattr(value, 'get_sql'): 202 raise TypeError, "'%s' is not a valid query argument" % str(arg) 201 203 tables2, joins2, where2, params2 = value.get_sql(opts) 202 204 tables.extend(tables2) django/branches/magic-removal/docs/db-api.txt
r1873 r1884 225 225 **New in Django development version.** 226 226 227 By default, multiple lookups are "AND"ed together. If you'd like to use ``OR`` 228 statements in your queries, use the ``complex`` lookup type. 229 230 ``complex`` takes an expression of clauses, each of which is an instance of 231 ``django.core.meta.Q``. ``Q`` takes an arbitrary number of keyword arguments in 232 the standard Django lookup format. And you can use Python's "and" (``&``) and 233 "or" (``|``) operators to combine ``Q`` instances. For example:: 234 235 from django.core.meta import Q 236 polls.get_object(complex=(Q(question__startswith='Who') | Q(question__startswith='What'))) 237 238 The ``|`` symbol signifies an "OR", so this (roughly) translates into:: 239 240 SELECT * FROM polls 241 WHERE question LIKE 'Who%' OR question LIKE 'What%'; 242 243 You can use ``&`` and ``|`` operators together, and use parenthetical grouping. 244 Example:: 245 246 polls.get_object(complex=(Q(question__startswith='Who') & (Q(pub_date__exact=date(2005, 5, 2)) | Q(pub_date__exact=date(2005, 5, 6)))) 247 248 This roughly translates into:: 249 250 SELECT * FROM polls 251 WHERE question LIKE 'Who%' 252 AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06'); 227 By default, keyword argument queries are "AND"ed together. If you have more complex query 228 requirements (for example, you need to include an ``OR`` statement in your query), you need 229 to use ``Q`` objects. 230 231 A ``Q`` object is an instance of ``django.core.meta.Q``, used to encapsulate a collection of 232 keyword arguments. These keyword arguments are specified in the same way as keyword arguments to 233 the basic lookup functions like get_object() and get_list(). For example:: 234 235 Q(question__startswith='What') 236 237 ``Q`` objects can be combined using the ``&`` and ``|`` operators. When an operator is used on two 238 ``Q`` objects, it yields a new ``Q`` object. For example the statement:: 239 240 Q(question__startswith='Who') | Q(question__startswith='What') 241 242 ... yields a single ``Q`` object that represents the "OR" of two "question__startswith" queries, equivalent to the SQL WHERE clause:: 243 244 ... WHERE question LIKE 'Who%' OR question LIKE 'What%' 245 246 You can compose statements of arbitrary complexity by combining ``Q`` objects with the ``&`` and ``|`` operators. Parenthetical grouping can also be used. 247 248 One or more ``Q`` objects can then provided as arguments to the lookup functions. If multiple 249 ``Q`` object arguments are provided to a lookup function, they will be "AND"ed together. 250 For example:: 251 252 polls.get_object( 253 Q(question__startswith='Who'), 254 Q(pub_date__exact=date(2005, 5, 2)) | Q(pub_date__exact=date(2005, 5, 6)) 255 ) 256 257 ... roughly translates into the SQL:: 258 259 SELECT * from polls WHERE question LIKE 'Who%' 260 AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06') 261 262 If necessary, lookup functions can mix the use of ``Q`` objects and keyword arguments. All arguments 263 provided to a lookup function (be they keyword argument or ``Q`` object) are "AND"ed together. 264 However, if a ``Q`` object is provided, it must precede the definition of any keyword arguments. 265 For example:: 266 267 polls.get_object( 268 Q(pub_date__exact=date(2005, 5, 2)) | Q(pub_date__exact=date(2005, 5, 6)), 269 question__startswith='Who') 270 271 ... would be a valid query, equivalent to the previous example; but:: 272 273 # INVALID QUERY 274 polls.get_object( 275 question__startswith='Who', 276 Q(pub_date__exact=date(2005, 5, 2)) | Q(pub_date__exact=date(2005, 5, 6))) 277 278 ... would not be valid. 279 280 A ``Q`` objects can also be provided to the ``complex`` keyword argument. For example:: 281 282 polls.get_object( 283 complex=Q(question__startswith='Who') & 284 (Q(pub_date__exact=date(2005, 5, 2)) | 285 Q(pub_date__exact=date(2005, 5, 6)) 286 ) 287 ) 253 288 254 289 See the `OR lookups examples page`_ for more examples. django/branches/magic-removal/tests/modeltests/lookup/models.py
r1835 r1884 69 69 Traceback (most recent call last): 70 70 ... 71 AssertionError: get_in_bulk() cannot be passed an empty list.71 AssertionError: get_in_bulk() cannot be passed an empty ID list. 72 72 73 73 # get_values() is just like get_list(), except it returns a list of django/branches/magic-removal/tests/modeltests/or_lookups/models.py
r1754 r1884 4 4 To perform an OR lookup, or a lookup that combines ANDs and ORs, use the 5 5 ``complex`` keyword argument, and pass it an expression of clauses using the 6 variable ``django.db.models.Q`` .6 variable ``django.db.models.Q`` (or any object with a get_sql method). 7 7 """ 8 8 … … 55 55 [Hello, Goodbye, Hello and goodbye] 56 56 57 # Queries can use Q objects as args 58 >>> Article.objects.get_list(Q(headline__startswith='Hello')) 59 [Hello, Hello and goodbye] 60 61 # Q arg objects are ANDed 62 >>> Article.objects.get_list(Q(headline__startswith='Hello'), Q(headline__contains='bye')) 63 [Hello and goodbye] 64 65 # Q arg AND order is irrelevant 66 >>> Article.objects.get_list(Q(headline__contains='bye'), headline__startswith='Hello') 67 [Hello and goodbye] 68 69 # QOrs are ok, as they ultimately resolve to a Q 70 >>> Article.objects.get_list(Q(headline__contains='Hello') | Q(headline__contains='bye')) 71 [Hello, Goodbye, Hello and goodbye] 72 73 # Try some arg queries with operations other than get_list 74 >>> Article.objects.get_object(Q(headline__startswith='Hello'), Q(headline__contains='bye')) 75 Hello and goodbye 76 77 >>> Article.objects.get_count(Q(headline__startswith='Hello') | Q(headline__contains='bye')) 78 3 79 80 >>> Article.objects.get_values(Q(headline__startswith='Hello'), Q(headline__contains='bye')) 81 [{'headline': 'Hello and goodbye', 'pub_date': datetime.datetime(2005, 11, 29, 0, 0), 'id': 3}] 82 83 >>> Article.objects.get_in_bulk([1,2], Q(headline__startswith='Hello')) 84 {1: Hello} 85 57 86 """
