Thread: [SQLObject] Bool value of SelectResult
SQLObject is a Python ORM.
Brought to you by:
ianbicking,
phd
From: Christopher A. <chr...@we...> - 2006-05-14 00:39:46
|
Is there a special reason why the SelectResult class does not implement __len__ or __nonzero__ methods? __len__ could be easily implemented as: def __len__(self): return self.count() thus allowing to say: res = SQLObject.select(...) if res: ... Currently you have to do: res = SQLObject.select(...) if res.count(): ... which is not so obvious. Correct me, if I am missing something. Chris |
From: Christopher A. <chr...@we...> - 2006-05-18 00:18:26
|
Is there a special reason why the SelectResult class in sresults.py does not implement __len__ or __nonzero__ methods? __len__ could be easily implemented as: def __len__(self): return self.count() thus allowing to say: res = SQLObject.select(...) if res: ... Currently you have to do: res = SQLObject.select(...) if res.count(): ... which is not so obvious. Correct me, if I am missing something. Chris |
From: Christopher A. <chr...@we...> - 2006-05-18 00:30:51
|
Christopher Arndt wrote: > Is there a special reason why the SelectResult class in sresults.py does > not implement __len__ or __nonzero__ methods? Sorry! Sent that message out again in error :-( Is the FAQ on the website going to be updated with this answer? Chris |
From: Oleg B. <ph...@ma...> - 2006-05-18 08:27:56
|
On Thu, May 18, 2006 at 01:30:41AM +0100, Christopher Arndt wrote: > Is the FAQ on the website going to be updated with this answer? The FAQ on the site was generated from SQLObject 0.7.0 documentation that didn't include the answer. SQLObject 0.7.0 is still the official stable release. Oleg. -- Oleg Broytmann http://phd.pp.ru/ ph...@ph... Programmers don't die, they just GOSUB without RETURN. |
From: Christopher A. <chr...@we...> - 2006-05-18 15:52:26
|
Oleg Broytmann wrote: > On Thu, May 18, 2006 at 01:30:41AM +0100, Christopher Arndt wrote: >>Is the FAQ on the website going to be updated with this answer? > > The FAQ on the site was generated from SQLObject 0.7.0 documentation > that didn't include the answer. SQLObject 0.7.0 is still the official > stable release. Ok, but it still applies to the 0.7 release as well. Me thinks, that one should be practical here and see the docs separately from the software. Chris |
From: Oleg B. <ph...@ma...> - 2006-05-18 15:57:27
|
On Thu, May 18, 2006 at 04:39:14PM +0100, Christopher Arndt wrote: > Oleg Broytmann wrote: > > The FAQ on the site was generated from SQLObject 0.7.0 documentation > > that didn't include the answer. SQLObject 0.7.0 is still the official > > stable release. > > Ok, but it still applies to the 0.7 release as well. Me thinks, that one > should be practical here and see the docs separately from the software. I can copy the answer to 0.7-branch, but the site will not be updated until we release 0.7.1. Ok? Oleg. -- Oleg Broytmann http://phd.pp.ru/ ph...@ph... Programmers don't die, they just GOSUB without RETURN. |
From: Christopher A. <chr...@we...> - 2006-05-18 16:36:51
|
Oleg Broytmann wrote: > I can copy the answer to 0.7-branch, Definetely. > but the site will not be updated > until we release 0.7.1. Ok? That should happen soon from what I gather so that's fine IMO. Chris |
From: Oleg B. <ph...@ma...> - 2006-05-18 16:40:43
|
On Thu, May 18, 2006 at 05:14:27PM +0100, Christopher Arndt wrote: > > but the site will not be updated > > until we release 0.7.1. Ok? > > That should happen soon from what I gather so that's fine IMO. Not neccessary. Somebody should collect the list of commits and update NEWS.txt. Oleg. -- Oleg Broytmann http://phd.pp.ru/ ph...@ph... Programmers don't die, they just GOSUB without RETURN. |
From: Oleg B. <ph...@ma...> - 2006-05-14 13:45:37
|
On Sun, May 14, 2006 at 01:39:37AM +0100, Christopher Arndt wrote: > Is there a special reason why the SelectResult class does not implement > __len__ or __nonzero__ methods? http://svn.colorstudy.com/SQLObject/docs/FAQ.txt Oleg. -- Oleg Broytmann http://phd.pp.ru/ ph...@ph... Programmers don't die, they just GOSUB without RETURN. |
From: Christopher A. <chr...@we...> - 2006-05-18 17:14:48
|
Oleg Broytmann wrote: > On Sun, May 14, 2006 at 01:39:37AM +0100, Christopher Arndt wrote: > >>Is there a special reason why the SelectResult class does not implement >>__len__ or __nonzero__ methods? > > http://svn.colorstudy.com/SQLObject/docs/FAQ.txt Ok, read that and agree that this rules out implicit SQL execution for the __len__ method. But do these concerns apply to the __nonzero__ method as well? Is this also expected to be a "fast" operation? How do other iterators handle this? I don't really see the point of the "speed" argument anyway, since the whole point of SQLObject is, that even mere attribute access can trigger SQL execution, which can take an arbitrary amount of time. Anyway, I figured out, how I can add this behaviour to SelectResult as a MixIn: class MySelectResult: def __nonzero__(self): return self.count() > 0 SQLObject.SelectResultsClass.__bases__ += (MySelectResult,) class Person(SQLObject): name = StringCol(alternateID=True, length=20) org = StringCol(length=100) if __name__ == '__main__': Person.createTable() u1 = Person(name="joe", org="doe.com") u2 = Person(name="alice", org="foo.com") u3 = Person(name="bob", org="foo.com") pl = Person.select(Person.q.org == 'foo.com') if pl: for p in pl: print p.name else: print "No records found." |
From: Oleg B. <ph...@ma...> - 2006-05-18 17:29:12
|
On Thu, May 18, 2006 at 05:51:47PM +0100, Christopher Arndt wrote: > But do these concerns apply to the __nonzero__ method as well? Is this > also expected to be a "fast" operation? It is. Python calls __nonzero__() in a boolean context. In the absence of __nonzero__() python calls __len__(). > How do other iterators handle this? You seldom use iterators in a boolean context. But people write if MyTable.select(): all the time... Oleg. -- Oleg Broytmann http://phd.pp.ru/ ph...@ph... Programmers don't die, they just GOSUB without RETURN. |
From: Christopher A. <chr...@we...> - 2006-05-18 18:08:36
|
Oleg Broytmann wrote: > You seldom use iterators in a boolean context. But people write > > if MyTable.select(): > > all the time... I was mometarily confused now, because I was sure i came accross situations where this did not work. Now try this: class MySelectResult: def __nonzero__(self): return self.count() > 0 # uncomment this, to make it work correctly #SQLObject.SelectResultsClass.__bases__ += (MySelectResult,) class Person(SQLObject): name = StringCol(alternateID=True, length=20) org = StringCol(length=100) if __name__ == '__main__': Person.createTable() u1 = Person(name="joe", org="doe.com") u2 = Person(name="alice", org="foo.com") u3 = Person(name="bob", org="foo.com") pl = Person.select(AND(Person.q.name == 'joe', Person.q.org == 'foo.com')) if pl: print "Records found:" for p in pl: print p.name else: print "No records found." Without my custom __nonzero__ method bool(pl) will yield True even though list(pl) == [] Chris |
From: Oleg B. <ph...@ma...> - 2006-05-18 18:14:14
|
On Thu, May 18, 2006 at 07:08:21PM +0100, Christopher Arndt wrote: > Without my custom __nonzero__ method bool(pl) will yield True even > though list(pl) == [] Yes, an instance of SelectResult class always evaluates to True. So one must write if list(MyTable.select()): We can argue if this is good or bad... Oleg. -- Oleg Broytmann http://phd.pp.ru/ ph...@ph... Programmers don't die, they just GOSUB without RETURN. |
From: Oleg B. <ph...@ma...> - 2006-05-18 18:45:19
|
On Thu, May 18, 2006 at 10:14:08PM +0400, Oleg Broytmann wrote: > if list(MyTable.select()): Oh, BTW - list() calls __nonzero__(), so list(.select()) results in two queries, and this is exactly the point to have neither __nonzero__() nor __len__(). Oleg. -- Oleg Broytmann http://phd.pp.ru/ ph...@ph... Programmers don't die, they just GOSUB without RETURN. |
From: Oleg B. <ph...@ma...> - 2006-05-18 18:58:35
|
On Thu, May 18, 2006 at 10:45:11PM +0400, Oleg Broytmann wrote: > Oh, BTW - list() calls __nonzero__(), so list(.select()) results in two > queries, and this is exactly the point to have neither __nonzero__() nor > __len__(). Ah, no - list() only calls __len__(). So I think it's possible to add __nonzero__(). Oleg. -- Oleg Broytmann http://phd.pp.ru/ ph...@ph... Programmers don't die, they just GOSUB without RETURN. |
From: Christopher A. <chr...@we...> - 2006-05-18 19:30:39
|
Oleg Broytmann wrote: > Ah, no - list() only calls __len__(). So I think it's possible to add > __nonzero__(). That would be great, because that would go along with the principle of the least suprise! Chris |
From: Ian B. <ia...@co...> - 2006-05-18 21:17:14
|
Oleg Broytmann wrote: > On Thu, May 18, 2006 at 10:45:11PM +0400, Oleg Broytmann wrote: > >> Oh, BTW - list() calls __nonzero__(), so list(.select()) results in two >>queries, and this is exactly the point to have neither __nonzero__() nor >>__len__(). > > > Ah, no - list() only calls __len__(). So I think it's possible to add > __nonzero__(). FWIW, I think list() no longer calls __len__() (it calls __size__() or something), but only in some Python versions. I guess it was considered a bug (for good reason). I'm still not sure if it's a good idea to implement __len__() (maybe conditional on the Python version, which is also icky). Implementing __nonzero__ is probably for the best, since if it is always true that leads to buggy code no one notices. And I if it raises an exception it is incredibly annoying (cgi.FieldStorage raises an exception, and I hate it). One design decision for SO2 is if .select() should be iterable at all, or if it should require .execute() or something to be called on it. (For backward compatibility at least you'd be able to iterate with a warning.) I think in some ways it confuses things, since .select() represents a query that could be run, not an actual database operation. Also there's the issue with lazy instantiation of objects, which doesn't really work very well, and so now a list is created, but you only get access to the iterator of the list. Though I suppose it would be safe to load the result rows and instantiate the SQLObject instances lazily; it's just keeping the cursor open that causes problems. -- Ian Bicking / ia...@co... / http://blog.ianbicking.org |
From: Oleg B. <ph...@ma...> - 2006-05-18 19:26:24
|
On Thu, May 18, 2006 at 05:51:47PM +0100, Christopher Arndt wrote: > class MySelectResult: > def __nonzero__(self): > return self.count() > 0 > > SQLObject.SelectResultsClass.__bases__ += (MySelectResult,) > > class Person(SQLObject): > name = StringCol(alternateID=True, length=20) > org = StringCol(length=100) > > if __name__ == '__main__': > Person.createTable() > u1 = Person(name="joe", org="doe.com") > u2 = Person(name="alice", org="foo.com") > u3 = Person(name="bob", org="foo.com") > > pl = Person.select(Person.q.org == 'foo.com') > if pl: > for p in pl: > print p.name > else: > print "No records found." You ran two query. Is it really neccessary? Why not just pl = list(Person.select(Person.q.org == 'foo.com')) if pl: for p in pl: without __nonzero__() i.e. without SELECT COUNT()? Oleg. -- Oleg Broytmann http://phd.pp.ru/ ph...@ph... Programmers don't die, they just GOSUB without RETURN. |
From: Christopher A. <chr...@we...> - 2006-05-18 20:09:02
|
Oleg Broytmann wrote: > On Thu, May 18, 2006 at 05:51:47PM +0100, Christopher Arndt wrote: > >>class MySelectResult: >> def __nonzero__(self): >> return self.count() > 0 >> >>SQLObject.SelectResultsClass.__bases__ += (MySelectResult,) >> >>class Person(SQLObject): >> name = StringCol(alternateID=True, length=20) >> org = StringCol(length=100) >> >>if __name__ == '__main__': >> Person.createTable() >> u1 = Person(name="joe", org="doe.com") >> u2 = Person(name="alice", org="foo.com") >> u3 = Person(name="bob", org="foo.com") >> >> pl = Person.select(Person.q.org == 'foo.com') >> if pl: >> for p in pl: >> print p.name >> else: >> print "No records found." > > > You ran two query. Is it really neccessary? Why not just Ok, this probably not the best example. But sometimes you just want to access the first (or the last) item of the result set. res = Person.select(...) if res: return res[0].someAttr else: return some_default Chris |
From: Matt G. <sql...@ma...> - 2006-05-18 21:14:43
|
On Thu, 2006-05-18 at 21:02 +0100, Christopher Arndt wrote: > Oleg Broytmann wrote: > > On Thu, May 18, 2006 at 05:51:47PM +0100, Christopher Arndt wrote: > > > >>class MySelectResult: > >> def __nonzero__(self): > >> return self.count() > 0 > >> > >>SQLObject.SelectResultsClass.__bases__ += (MySelectResult,) > >> > >>class Person(SQLObject): > >> name = StringCol(alternateID=True, length=20) > >> org = StringCol(length=100) > >> > >>if __name__ == '__main__': > >> Person.createTable() > >> u1 = Person(name="joe", org="doe.com") > >> u2 = Person(name="alice", org="foo.com") > >> u3 = Person(name="bob", org="foo.com") > >> > >> pl = Person.select(Person.q.org == 'foo.com') > >> if pl: > >> for p in pl: > >> print p.name > >> else: > >> print "No records found." > > > > > > You ran two query. Is it really neccessary? Why not just > > Ok, this probably not the best example. But sometimes you just want to > access the first (or the last) item of the result set. > > res = Person.select(...) > if res: > return res[0].someAttr > else: > return some_default If you're only getting 1 record the call to count() may actually be more costly than selecting the record, so you should still avoid it. You could probably do this a few ways, but catching the IndexError seems the cleanest to me: try: res = Person.select()[0] except IndexError: return default else: return res.someAttr It might save people confusion if bool(SelectResults) Just Worked(TM), but I think the extra database trip will always be unnecessary. -- Matt Good <sql...@ma...> |
From: Ian B. <ia...@co...> - 2006-05-18 21:20:36
|
Matt Good wrote: > If you're only getting 1 record the call to count() may actually be more > costly than selecting the record, so you should still avoid it. You > could probably do this a few ways, but catching the IndexError seems the > cleanest to me: > > try: > res = Person.select()[0] > except IndexError: > return default > else: > return res.someAttr > > It might save people confusion if bool(SelectResults) Just Worked(TM), > but I think the extra database trip will always be unnecessary. I think this is only in the trunk, but there you can do: obj = Person.select().getOne(default) This also test that there isn't more than one object, which is a test that is often left out but also generally expected. If you don't pass in a default you get an exception if there is no object. I don't think that exactly fits the original idea, though, where list(Person.select()[:1]) is probably the best/fastest way to get the first item if one exists. -- Ian Bicking / ia...@co... / http://blog.ianbicking.org |
From: Oleg B. <ph...@ma...> - 2006-05-19 07:37:49
|
On Thu, May 18, 2006 at 04:17:48PM -0500, Ian Bicking wrote: > I don't think that exactly fits the original idea, though, where > list(Person.select()[:1]) is probably the best/fastest way to get the > first item if one exists. Well, it seems it'd be best for SQLObject to implement neither __len__ nor __nonzero__. Please somebody write a FAQ entry summarizing the thread; add code snippets. Oleg. -- Oleg Broytmann http://phd.pp.ru/ ph...@ph... Programmers don't die, they just GOSUB without RETURN. |
From: Ian B. <ia...@co...> - 2006-05-19 15:15:21
|
Oleg Broytmann wrote: > On Thu, May 18, 2006 at 04:17:48PM -0500, Ian Bicking wrote: > >>I don't think that exactly fits the original idea, though, where >>list(Person.select()[:1]) is probably the best/fastest way to get the >>first item if one exists. > > > Well, it seems it'd be best for SQLObject to implement neither __len__ > nor __nonzero__. Please somebody write a FAQ entry summarizing the thread; > add code snippets. It inherits __nonzero__ already (so SelectResults are always true), but does not implement __len__. Like I said, I really dislike those very few instances where you might encounter objects you can't test an object for truth, so I'm not very positive about that resolution. But I don't know, that's really the only way to keep people from introducing bugs into their code, since it's so natural (but probably incorrect) to test a SelectResult for truthfulness. -- Ian Bicking / ia...@co... / http://blog.ianbicking.org |
From: Oleg B. <ph...@ph...> - 2006-05-19 15:27:13
|
On Fri, May 19, 2006 at 10:12:23AM -0500, Ian Bicking wrote: > It inherits __nonzero__ already (so SelectResults are always true), but > does not implement __len__. Like I said, I really dislike those very > few instances where you might encounter objects you can't test an object > for truth, so I'm not very positive about that resolution. But I don't > know, that's really the only way to keep people from introducing bugs > into their code, since it's so natural (but probably incorrect) to test > a SelectResult for truthfulness. We have to choose among the following possibilities: 1) implement __nonzero__() via count() and warn users that it's slow; 2) completely forbid __nonzero__() by raising an exception; 3) allow users to choose; write some classes that a user use to choose the desired behavior. Oleg. -- Oleg Broytmann http://phd.pp.ru/ ph...@ph... Programmers don't die, they just GOSUB without RETURN. |
From: Matt G. <sql...@ma...> - 2006-05-18 20:21:04
|
On Thu, 2006-05-18 at 23:26 +0400, Oleg Broytmann wrote: > On Thu, May 18, 2006 at 05:51:47PM +0100, Christopher Arndt wrote: > > class MySelectResult: > > def __nonzero__(self): > > return self.count() > 0 > > > > SQLObject.SelectResultsClass.__bases__ += (MySelectResult,) > > > > class Person(SQLObject): > > name = StringCol(alternateID=True, length=20) > > org = StringCol(length=100) > > > > if __name__ == '__main__': > > Person.createTable() > > u1 = Person(name="joe", org="doe.com") > > u2 = Person(name="alice", org="foo.com") > > u3 = Person(name="bob", org="foo.com") > > > > pl = Person.select(Person.q.org == 'foo.com') > > if pl: > > for p in pl: > > print p.name > > else: > > print "No records found." > > You ran two query. Is it really neccessary? Why not just > > pl = list(Person.select(Person.q.org == 'foo.com')) > if pl: > for p in pl: > > without __nonzero__() i.e. without SELECT COUNT()? This seems like a good place for a for/else: for p in Person.select(Person.q.org == 'foo.com'): print p.name else: print "No records found." -- Matt Good <sql...@ma...> |