Fixed #11402: added a `QuerySet.exists()` method. Thanks, Alex Gaynor.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@11646 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Jacob Kaplan-Moss 2009-10-24 00:28:39 +00:00
parent 9f70783b14
commit b79702b2de
6 changed files with 31 additions and 28 deletions

View File

@ -3,11 +3,6 @@ import types
import sys
import os
from itertools import izip
try:
set
except NameError:
from sets import Set as set # Python 2.3 fallback.
import django.db.models.manager # Imported to register signal handler.
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, FieldError
from django.db.models.fields import AutoField, FieldDoesNotExist
@ -22,7 +17,6 @@ from django.utils.functional import curry
from django.utils.encoding import smart_str, force_unicode, smart_unicode
from django.conf import settings
class ModelBase(type):
"""
Metaclass for all models.
@ -236,7 +230,6 @@ class ModelBase(type):
signals.class_prepared.send(sender=cls)
class Model(object):
__metaclass__ = ModelBase
_deferred = False
@ -467,7 +460,7 @@ class Model(object):
if pk_set:
# Determine whether a record with the primary key already exists.
if (force_update or (not force_insert and
manager.filter(pk=pk_val).extra(select={'a': 1}).values('a').order_by())):
manager.filter(pk=pk_val).exists())):
# It does already exist, so do an UPDATE.
if force_update or non_pks:
values = [(f, None, (raw and getattr(self, f.attname) or f.pre_save(self, False))) for f in non_pks]

View File

@ -1,5 +1,4 @@
import copy
from django.db.models.query import QuerySet, EmptyQuerySet, insert_query
from django.db.models import signals
from django.db.models.fields import FieldDoesNotExist
@ -173,6 +172,9 @@ class Manager(object):
def only(self, *args, **kwargs):
return self.get_query_set().only(*args, **kwargs)
def exists(self, *args, **kwargs):
return self.get_query_ste().exists(*args, **kwargs)
def _insert(self, values, **kwargs):
return insert_query(self.model, values, **kwargs)

View File

@ -2,20 +2,13 @@
The main QuerySet implementation. This provides the public API for the ORM.
"""
try:
set
except NameError:
from sets import Set as set # Python 2.3 fallback
from copy import deepcopy
from django.db import connection, transaction, IntegrityError
from django.db.models.aggregates import Aggregate
from django.db.models.fields import DateField
from django.db.models.query_utils import Q, select_related_descend, CollectedObjects, CyclicDependency, deferred_class_factory
from django.db.models import signals, sql
# Used to control how many objects are worked with at once in some cases (e.g.
# when deleting objects).
CHUNK_SIZE = 100
@ -444,6 +437,11 @@ class QuerySet(object):
return query.execute_sql(None)
_update.alters_data = True
def exists(self):
if self._result_cache is None:
return self.query.has_results()
return bool(self._result_cache)
##################################################
# PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS #
##################################################

View File

@ -8,7 +8,6 @@ all about the internals of models in order to get the information it needs.
"""
from copy import deepcopy
from django.utils.tree import Node
from django.utils.datastructures import SortedDict
from django.utils.encoding import force_unicode
@ -24,11 +23,6 @@ from django.core.exceptions import FieldError
from datastructures import EmptyResultSet, Empty, MultiJoin
from constants import *
try:
set
except NameError:
from sets import Set as set # Python 2.3 fallback
__all__ = ['Query', 'BaseQuery']
class BaseQuery(object):
@ -384,6 +378,15 @@ class BaseQuery(object):
return number
def has_results(self):
q = self.clone()
q.add_extra({'a': 1}, None, None, None, None, None)
q.add_fields(())
q.set_extra_mask(('a',))
q.set_aggregate_mask(())
q.clear_ordering()
return bool(q.execute_sql())
def as_sql(self, with_limits=True, with_col_aliases=False):
"""
Creates the SQL for this query. Returns the SQL string and list of

View File

@ -319,9 +319,7 @@ class BaseModelForm(BaseForm):
if self.instance.pk is not None:
qs = qs.exclude(pk=self.instance.pk)
# This cute trick with extra/values is the most efficient way to
# tell if a particular query returns any results.
if qs.extra(select={'a': 1}).values('a').order_by():
if qs.exists():
if len(unique_check) == 1:
self._errors[unique_check[0]] = ErrorList([self.unique_error_message(unique_check)])
else:
@ -354,9 +352,7 @@ class BaseModelForm(BaseForm):
if self.instance.pk is not None:
qs = qs.exclude(pk=self.instance.pk)
# This cute trick with extra/values is the most efficient way to
# tell if a particular query returns any results.
if qs.extra(select={'a': 1}).values('a').order_by():
if qs.exists():
self._errors[field] = ErrorList([
self.date_error_message(lookup_type, field, unique_for)
])

View File

@ -1114,6 +1114,17 @@ Aggregation <topics-db-aggregation>`.
.. _field-lookups:
``exists()``
~~~~~~~~~~~~
.. versionadded:: 1.2
Returns ``True`` if the :class:`QuerySet` contains any results, and ``False``
if not. This tries to perform the query in the simplest and fastest way
possible, but it *does* execute nearly the same query. This means that calling
:meth:`QuerySet.exists()` is faster that ``bool(some_query_set)``, but not by
a large degree.
Field lookups
-------------