Joseph Kocherhans wrote:

> Ian Bicking has kept len() out of SQLObject result sets even though it
> seems really intuitive to use. Here's a rundown of what I remember
> about his argument: __len__ would run "count (*)" against the db. I
> think iter() calls len() implicitly for performance reasons, so you'd
> be running a useless count(*) every time you started iterating over a
> Query object.

OK, I think I have tracked this down - or a related issue.  This is
relevant whether we go with __len__ or not.

If you have this view code (updated for the new syntax):
   context['articles'] = Article.objects   # or Article.objects.all()

And this template (exactly as before the syntax change):

{% if articles %}
   {% for article in articles %}
      .. {{ article }}
   {% endfor %}
{% else %}
   Sorry, no articles
{% endif %}


With the new syntax method, you never get the 'Sorry no articles',
because Article.objects evaluates to true.  This would be a bug with
the new syntax.  However, if Article.objects or Articles.objects.all()
has a __len__ method as I proposed, then bool() will call it and return
false if the answer is zero.  So the above code would work, but do two
db lookups, one for a count and the other for a list, which is also
different from before, and is a performance bug.

(NB I think this is 2.3/2.4 related - apparently the behaviour of
bool(iter([])) changed.)

One way around it would be to add __nonzero__ to the Query instance (I
think), which would get and cache the list of objects, ready to be
iterated.  This would be slightly subtle behaviour, but something like
this needs to be done.  So you would have:

bool(Article.objects)  # retrieves list
iter(Article.objects)   # retrieves list
len(Article.objects)   # does count(*)

Luke

Reply via email to