From eeb10d5f2c5375ec28b7761f9ea0fd4f1d2e4bd1 Mon Sep 17 00:00:00 2001 From: Luke Plant Date: Wed, 9 Dec 2009 16:07:21 +0000 Subject: [PATCH] Optimised use of 'in' operator on QuerySet using an explicit __contains__ method. Without this change, use of 'in' on a QuerySet resulted in ._result_cache being fully populated, which sometimes is unnecessary work. git-svn-id: http://code.djangoproject.com/svn/django/trunk@11803 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/db/models/query.py | 30 ++++++++++++++++++++++++++++++ tests/modeltests/basic/models.py | 8 ++++++++ 2 files changed, 38 insertions(+) diff --git a/django/db/models/query.py b/django/db/models/query.py index 36949ac390..6a16ce1b33 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -107,6 +107,36 @@ class QuerySet(object): return False return True + def __contains__(self, val): + # The 'in' operator works without this method, due to __iter__. This + # implementation exists only to shortcut the creation of Model + # instances, by bailing out early if we find a matching element. + pos = 0 + if self._result_cache is not None: + if val in self._result_cache: + return True + elif self._iter is None: + # iterator is exhausted, so we have our answer + return False + # remember not to check these again: + pos = len(self._result_cache) + else: + # We need to start filling the result cache out. The following + # ensures that self._iter is not None and self._result_cache is not + # None + it = iter(self) + + # Carry on, one result at a time. + while True: + if len(self._result_cache) <= pos: + self._fill_cache(num=1) + if self._iter is None: + # we ran out of items + return False + if self._result_cache[pos] == val: + return True + pos += 1 + def __getitem__(self, k): """ Retrieves an item or slice from the set of results. diff --git a/tests/modeltests/basic/models.py b/tests/modeltests/basic/models.py index 2dbde58256..34bfc6a83e 100644 --- a/tests/modeltests/basic/models.py +++ b/tests/modeltests/basic/models.py @@ -211,6 +211,14 @@ True >>> Article.objects.get(id__exact=8) == Article.objects.get(id__exact=7) False +# You can use 'in' to test for membership... +>>> a8 in Article.objects.all() +True + +# ... but there will often be more efficient ways if that is all you need: +>>> Article.objects.filter(id=a8.id).exists() +True + # dates() returns a list of available dates of the given scope for the given field. >>> Article.objects.dates('pub_date', 'year') [datetime.datetime(2005, 1, 1, 0, 0)]