Django

Code

Changeset 4394

Show
Ignore:
Timestamp:
01/22/07 20:11:08 (1 year ago)
Author:
adrian
Message:

Fixed #3283 -- Added support for empty QuerySets? via none() method. Thanks for the patch, medhat

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/db/models/manager.py

    r4265 r4394  
    1 from django.db.models.query import QuerySet 
     1from django.db.models.query import QuerySet, EmptyQuerySet 
    22from django.dispatch import dispatcher 
    33from django.db.models import signals 
     
    4242    # PROXIES TO QUERYSET # 
    4343    ####################### 
     44     
     45    def get_empty_query_set(self): 
     46        return EmptyQuerySet(self.model) 
    4447 
    4548    def get_query_set(self): 
     
    4851        """ 
    4952        return QuerySet(self.model) 
     53     
     54    def none(self): 
     55        return self.get_empty_query_set() 
    5056 
    5157    def all(self): 
  • django/trunk/django/db/models/query.py

    r4283 r4394  
    2525# Larger values are slightly faster at the expense of more storage space. 
    2626GET_ITERATOR_CHUNK_SIZE = 100 
     27 
     28class EmptyResultSet(Exception): 
     29    pass 
    2730 
    2831#################### 
     
    169172 
    170173        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             
    172180        cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) 
    173181        fill_cache = self._select_related 
     
    193201        counter._limit = None 
    194202        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             
    196209        cursor = connection.cursor() 
    197210        if self._distinct: 
     
    524537 
    525538        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         
    527545        select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns] 
    528546        cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) 
     
    546564            self._where.append('%s.%s IS NOT NULL' % \ 
    547565                (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         
    549572        sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1 %s' % \ 
    550573            (backend.get_date_trunc_sql(self._kind, '%s.%s' % (backend.quote_name(self.model._meta.db_table), 
     
    563586        c._order = self._order 
    564587        return c 
     588     
     589class 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 
     602 
     603    def _clone(self, klass=None, **kwargs): 
     604        c = super(EmptyQuerySet, self)._clone(klass, **kwargs) 
     605        c._result_cache = [] 
     606        return c 
    565607 
    566608class QOperator(object): 
     
    572614        joins, where, params = SortedDict(), [], [] 
    573615        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 
    578624        if where: 
    579625            return joins, ['(%s)' % self.operator.join(where)], params 
     
    629675 
    630676    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(), [], [] 
    633682        return joins, where2, params 
    634683 
     
    646695            return '%s%s IN (%s)' % (table_prefix, field_name, in_string) 
    647696        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 
    653698    elif lookup_type == 'range': 
    654699        return '%s%s BETWEEN %%s AND %%s' % (table_prefix, field_name) 
  • django/trunk/docs/db-api.txt

    r4281 r4394  
    526526    >>> Entry.objects.filter(headline__contains='Lennon').dates('pub_date', 'day') 
    527527    [datetime.datetime(2005, 3, 20)] 
     528     
     529``none()`` 
     530~~~~~~~~~~ 
     531 
     532**New in Django development version** 
     533 
     534Returns an ``EmptyQuerySet`` -- a ``QuerySet`` that always evaluates to  
     535an empty list. This can be used in cases where you know that you should 
     536return an empty result set and your caller is expecting a ``QuerySet`` 
     537object (instead of returning an empty list, for example.) 
     538 
     539Examples:: 
     540     
     541    >>> Entry.objects.none() 
     542    [] 
    528543 
    529544``select_related()`` 
  • django/trunk/tests/modeltests/lookup/models.py

    r3661 r4394  
    192192[<Article: Article with \ backslash>] 
    193193 
     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() 
     2000 
     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 
    194209"""}