Opened 18 years ago
Closed 17 years ago
#2430 closed defect (fixed)
QuerySet should be evaluated when used in boolean tests
Reported by: | James Bennett | Owned by: | nobody |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | dev |
Severity: | normal | Keywords: | qs-rf-fixed |
Cc: | ferringb@…, harish.mallipeddi@… | Triage Stage: | Accepted |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
Currently, a QuerySet is not evaluated when being used in boolean contexts (e.g., doing if SomeModel.objects.all():
); this leads to counterintuitive behavior, because it will always resolve to a True value regardless of whether it would return any objects when "actually" evaluated.
Adding a __nonzero__
method to QuerySet which does the evaluation would solve this.
Attachments (1)
Change History (14)
comment:1 by , 18 years ago
Status: | new → assigned |
---|
comment:2 by , 18 years ago
comment:3 by , 18 years ago
Resolution: | → fixed |
---|---|
Status: | assigned → closed |
Well, then I count it as "has been fixed."
comment:4 by , 18 years ago
Resolution: | fixed |
---|---|
Status: | closed → reopened |
Triage Stage: | Unreviewed → Accepted |
This has not been fixed, because evaluating it in the above context calls len()
to be called on the QuerySet. This is a very bad idea if you have, say, 100K rows in the result set (it reads them all into memory). We do need to implement a proper __nonzero___
method that only looks for the existence of one row without consuming it (and this is only mildly hard because the rowcount attribute on a database cursor in SQLite always returns -1).
Reopening. I'll fix this one day. It's something that is needed.
comment:5 by , 18 years ago
Well ... what you describe is actually a different from what has been reported.
Anyway, I'd propose to turn this into a query like:
select exists(select * from ...)
Unfortunately, this might turn out to be backend specific, too. For example, oracle doesn't like select queries without a table, IIRC. With mysql and postgresql, the above syntax works.
comment:6 by , 18 years ago
Using an extra SQL query is not the way to go here; what if the query is time consuming?. We already have the result set. We just need to read the first row (in the cases where "rowcount" is not honoured on cursors) and if we get data back, cache it and remember to add it to future retrievals. A little fiddly, but not impossible and imposes no extra database load.
comment:7 by , 17 years ago
Cc: | added |
---|
comment:8 by , 17 years ago
Has patch: | set |
---|
I just submitted a simple patch which essentially adds a nonzero method to the QuerySet class. Inside the nonzero method, I fetch the first item via getitem call. This is either answered from the result cache (if any) or would result in a SELECT query with the LIMIT set to 1.
comment:9 by , 17 years ago
Cc: | added |
---|
comment:10 by , 17 years ago
Keywords: | qs-rf added |
---|
comment:11 by , 17 years ago
(In [7030]) queryset-refactor: Converted the queryset iterator to be a real iterator and
only populate the result cache on demand. We actually populate the result cache
100 elements at a time, rather than one at a time for efficiency, but this is a
real win when the resultset contains 10,000 objects for example.
This also provides an efficient boolean (nonzero) test that doesn't use up
a lot of memory if you don't read all the results.
comment:12 by , 17 years ago
Keywords: | qs-rf-fixed added; qs-rf removed |
---|
comment:13 by , 17 years ago
Resolution: | → fixed |
---|---|
Status: | reopened → closed |
(In [7477]) Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
I tried it out and this seems to have changed, at least under python 2.4. Can you test again, please?