#19261: Page.__getitem__ usage causes queryset to evaluate when doing method
lookups
-------------------------+-------------------------------------------------
     Reporter:  trbs     |      Owner:  nobody
         Type:  Bug      |     Status:  new
    Component:  Core     |    Version:  1.4
  (Other)                |   Keywords:  page, type_error, __getitem__,
     Severity:  Normal   |  pagination
 Triage Stage:           |  Has patch:  1
  Unreviewed             |      UI/UX:  0
Easy pickings:  1        |
-------------------------+-------------------------------------------------
 The code for Page.__getitem__ forces an evaluation (query to the database)
 of the queryset when it's actually looking up the name the name of class
 functions.

 Python: 2.7 (think 2.6 as well)
 Django: 1.4.x

 The function looks like this:
 {{{
     def __getitem__(self, index):
         # The object_list is converted to a list so that if it was a
 QuerySe    t
         # it won't be a database hit per __getitem__.
         return super(Page, self).__getitem__(self, index)
 }}}

 Now consider this template code that runs from a generic class based view:

 {{{
 {% if page_obj.has_previous %}
 ...
 {% endif %}
 }}}

 Here ```page_obj.has_previous``` generates a call to __getitem__ with
 'has_previous' as its parameter ```index```.

 This translates to:

 {{{
 return list(self.object_list)['has_previous']
 }}}

 Which raises a ```TypeError```: ```list indices must be integers, not
 unicode````.
 And as per
 http://docs.python.org/2/reference/datamodel.html#object.__getitem__ this
 is nicely ignored and __getitem__ returns the correct attribute.

 However before the type error, during list(self.object_list) the queryset
 is executed. This should not happen if it's not necessary. It defeats
 caching in some cases, the query could be expensive and is generally not
 what we would expect when calling a Page instance's methods.

 To avoid this for the (quite common) usage of __getitem__, I suggest
 something in the lines of:

 {{{
    def __getitem__(self, index):
         # The object_list is converted to a list so that if it was a
 QuerySet
         # it won't be a database hit per __getitem__.
         if isinstance(index, (int,long)):
             return list(self.object_list)[index]
         return super(Page, self).__getitem__(self, index)
 }}}

 (Maybe we don't need to check for long ? and is there maybe some other
 type that we should check ?)

 Patch does not yet includes tests for this. However I'm not really sure
 what test would actually check this. As the type error is actually
 swallowed by Python itself.

 (btw: i set easy-pickings, as finding it out was not necessarily 'easy'
 but fixing it should hopefully be)

-- 
Ticket URL: <https://code.djangoproject.com/ticket/19261>
Django <https://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 django-updates@googlegroups.com.
To unsubscribe from this group, send email to 
django-updates+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to