Ticket #3283: empty_queryset_3.patch
File empty_queryset_3.patch, 7.9 KB (added by , 18 years ago) |
---|
-
django/db/models/manager.py
1 from django.db.models.query import QuerySet 1 from django.db.models.query import QuerySet, EmptyQuerySet 2 2 from django.dispatch import dispatcher 3 3 from django.db.models import signals 4 4 from django.db.models.fields import FieldDoesNotExist … … 41 41 ####################### 42 42 # PROXIES TO QUERYSET # 43 43 ####################### 44 45 def get_empty_query_set(self): 46 return EmptyQuerySet(self.model) 44 47 45 48 def get_query_set(self): 46 49 """Returns a new QuerySet object. Subclasses can override this method 47 50 to easily customise the behaviour of the Manager. 48 51 """ 49 52 return QuerySet(self.model) 53 54 def none(self): 55 return self.get_empty_query_set() 50 56 51 57 def all(self): 52 58 return self.get_query_set() -
django/db/models/query.py
25 25 # Larger values are slightly faster at the expense of more storage space. 26 26 GET_ITERATOR_CHUNK_SIZE = 100 27 27 28 class EmptyResultSet(Exception): 29 pass 30 28 31 #################### 29 32 # HELPER FUNCTIONS # 30 33 #################### … … 168 171 extra_select = self._select.items() 169 172 170 173 cursor = connection.cursor() 171 select, sql, params = self._get_sql_clause() 174 175 try: 176 select, sql, params = self._get_sql_clause() 177 except EmptyResultSet: 178 raise StopIteration 179 172 180 cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) 173 181 fill_cache = self._select_related 174 182 index_end = len(self.model._meta.fields) … … 192 200 counter._offset = None 193 201 counter._limit = None 194 202 counter._select_related = False 195 select, sql, params = counter._get_sql_clause() 203 204 try: 205 select, sql, params = counter._get_sql_clause() 206 except EmptyResultSet: 207 return 0 208 196 209 cursor = connection.cursor() 197 210 if self._distinct: 198 211 id_col = "%s.%s" % (backend.quote_name(self.model._meta.db_table), … … 523 536 field_names = [f.attname for f in self.model._meta.fields] 524 537 525 538 cursor = connection.cursor() 526 select, sql, params = self._get_sql_clause() 539 540 try: 541 select, sql, params = self._get_sql_clause() 542 except EmptyResultSet: 543 raise StopIteration 544 527 545 select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns] 528 546 cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) 529 547 while 1: … … 545 563 if self._field.null: 546 564 self._where.append('%s.%s IS NOT NULL' % \ 547 565 (backend.quote_name(self.model._meta.db_table), backend.quote_name(self._field.column))) 548 select, sql, params = self._get_sql_clause() 566 567 try: 568 select, sql, params = self._get_sql_clause() 569 except EmptyResultSet: 570 raise StopIteration 571 549 572 sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1 %s' % \ 550 573 (backend.get_date_trunc_sql(self._kind, '%s.%s' % (backend.quote_name(self.model._meta.db_table), 551 574 backend.quote_name(self._field.column))), sql, self._order) … … 562 585 c._kind = self._kind 563 586 c._order = self._order 564 587 return c 588 589 class EmptyQuerySet(QuerySet): 590 def __init__(self, model=None): 591 super(EmptyQuerySet,self).__init__(model) 592 self._result_cache = [] 593 594 def iterator(self): 595 raise StopIteration 596 597 def count(self): 598 return 0 599 600 def delete(self): 601 pass 565 602 603 def _clone(self, klass=None, **kwargs): 604 c = super(EmptyQuerySet, self)._clone(klass, **kwargs) 605 c._result_cache = [] 606 return c 607 566 608 class QOperator(object): 567 609 "Base class for QAnd and QOr" 568 610 def __init__(self, *args): … … 571 613 def get_sql(self, opts): 572 614 joins, where, params = SortedDict(), [], [] 573 615 for val in self.args: 574 joins2, where2, params2 = val.get_sql(opts) 575 joins.update(joins2) 576 where.extend(where2) 577 params.extend(params2) 616 try: 617 joins2, where2, params2 = val.get_sql(opts) 618 joins.update(joins2) 619 where.extend(where2) 620 params.extend(params2) 621 except EmptyResultSet: 622 if not isinstance(self,QOr): 623 raise EmptyResultSet 578 624 if where: 579 625 return joins, ['(%s)' % self.operator.join(where)], params 580 626 return joins, [], params … … 628 674 self.q = q 629 675 630 676 def get_sql(self, opts): 631 joins, where, params = self.q.get_sql(opts) 632 where2 = ['(NOT (%s))' % " AND ".join(where)] 677 try: 678 joins, where, params = self.q.get_sql(opts) 679 where2 = ['(NOT (%s))' % " AND ".join(where)] 680 except EmptyResultSet: 681 return SortedDict(), [], [] 633 682 return joins, where2, params 634 683 635 684 def get_where_clause(lookup_type, table_prefix, field_name, value): … … 645 694 if in_string: 646 695 return '%s%s IN (%s)' % (table_prefix, field_name, in_string) 647 696 else: 648 # Most backends do not accept an empty string inside the IN 649 # expression, i.e. cannot do "WHERE ... IN ()". Since there are 650 # also some backends that do not accept "WHERE false", we instead 651 # use an expression that always evaluates to False. 652 return '0=1' 697 raise EmptyResultSet 653 698 elif lookup_type == 'range': 654 699 return '%s%s BETWEEN %%s AND %%s' % (table_prefix, field_name) 655 700 elif lookup_type in ('year', 'month', 'day'): -
docs/db-api.txt
525 525 [datetime.datetime(2005, 3, 20), datetime.datetime(2005, 2, 20)] 526 526 >>> Entry.objects.filter(headline__contains='Lennon').dates('pub_date', 'day') 527 527 [datetime.datetime(2005, 3, 20)] 528 529 ``none()`` 530 ~~~~~~~~~~ 528 531 532 Returns an ``EmptyQuerySet`` -- a ``QuerySet`` that always evaluates to 533 an empty list. This can be used in cases where you know that you should 534 return an empty result set and your caller is expecting a ``QuerySet`` 535 object (instead of returning an empty list, for example.) 536 537 Examples:: 538 539 >>> Entry.objects.none() 540 [] 541 529 542 ``select_related()`` 530 543 ~~~~~~~~~~~~~~~~~~~~ 531 544 -
tests/modeltests/lookup/models.py
191 191 >>> Article.objects.filter(headline__contains='\\') 192 192 [<Article: Article with \ backslash>] 193 193 194 # none() returns an EmptyQuerySet that behaves like any other QuerySet object 195 >>> Article.objects.none() 196 [] 197 >>> Article.objects.none().filter(headline__startswith='Article') 198 [] 199 >>> Article.objects.none().count() 200 0 201 202 # using __in with an empty list should return an empty query set 203 >>> Article.objects.filter(id__in=[]) 204 [] 205 206 >>> Article.objects.exclude(id__in=[]) 207 [<Article: Article with \ backslash>, <Article: Article% with percent sign>, <Article: Article_ with underscore>, <Article: Article 5>, <Article: Article 6>, <Article: Article 4>, <Article: Article 2>, <Article: Article 3>, <Article: Article 7>, <Article: Article 1>] 208 194 209 """}