#11402: exists() method on QuerySets
---------------------------------------------------+------------------------
          Reporter:  Alex                          |         Owner:  nobody
            Status:  new                           |     Milestone:        
         Component:  Database layer (models, ORM)  |       Version:  1.0   
        Resolution:                                |      Keywords:        
             Stage:  Accepted                      |     Has_patch:  0     
        Needs_docs:  0                             |   Needs_tests:  0     
Needs_better_patch:  0                             |  
---------------------------------------------------+------------------------
Comment (by lukeplant):

 I agree that 'any' is better than 'exists' - it matches the Python
 builtin.

 To answer someone's objection that this should be an optimization in
 `QuerySet.__nonzero__`:

 `__nonzero__` should **not** do this optimization trick:

  * because it should be consistent with `__len__`, which simply forces
 evaluation of the !QuerySet
  * because it shouldn't second guess the user.

 We did actually have this discussion about `QuerySet.__len__()`, way back
 (I can't find it), and with hindsight I'm sure we came to the right
 conclusion.

 Consider the following two bits of code (ignoring bugs for now):

 1) Take len() of a queryset, then use its data
 {{{
 #!python
 options = Options.objects.filter(bar=baz)
 choice = None
 if len(options) > 1:
     print "Options: " + ", ".join(opt.name for opt in options)
 else:
     choice = options[0]
 }}}


 2) Take len() of a queryset, then discard its data
 {{{
 #!python
 options = Option.objects.filter(bar=baz)
 if len(options) > 1:
     print "You've got more than 1!"
 else
     print "You've got 1 or less!"
 }}}

 In `QuerySet.__len__`, it's impossible to guess which the user is doing.
 So if `__len__` does a .count() as an optimization, sometimes it will be a
 pessimization, causing an extra DB hit compared to just evaluating the
 query. Exactly the same case can be made for `__nonzero__`.

 In the face of ambiguity, refuse the temptation to guess.  We provide
 `QuerySet.count()` if the user '''knows''' that they only want the count,
 and `QuerySet.any()` if the user '''knows''' that they only want that. If
 `__len__` and `__nonzero__` tried to be 'clever', then implementing code
 #1 in an efficient way gets harder - you have to wrap with `list()`.

 Using .count() and .any() isn't so 'pure', but the point is that our
 abstraction is not perfect, and we should manage the leak as best we can.
 The easiest way is simply to add this documentation:

 >    bool() and len() and iter() force evaluation of the !QuerySet; use
 .any() or .count() if you know you don't want to do that.

 Finally, hitting the 'pessimization' trap would be very easy to do for
 template authors, and they shouldn't have to worry about that. The
 developer who writes the view code can and should worry about this, and
 has the tools to do so.

-- 
Ticket URL: <http://code.djangoproject.com/ticket/11402#comment:5>
Django <http://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to