https://launchpad.net/txpostgres
It uses the async features of psycopg2, so it would be a good starting
point for a Tornado driver.
Another option (that I have used) is ngx_postgres. It works well, but
requires making your queries via HTTP which brings its own slew of
excitement:
https://github.com/FRiCKLE/ngx_postgres/
Regards,
Cliff
I'm not really familiar with this.
Thanks for the help!
I'm going to take a look at it this evening.
Just create a gist on gist.github.com or create a public repository
(github, bitbucket, etc). Then, link to them from the tornado wiki.
Not ultra-sexy but very low cost of entry and available today.
-j
--
The Christian ideal has not been tried and found wanting;
it has been found difficult and left untried – G. K. Chesterton
I was making reference to trying to map relational datasets onto a
RESTful interface. If you're comfortable doing that (I've found using
lots of server-side views to be key) then it's mostly just a discovery
process. I know for me it took lots and lots of refactoring, but
overall I was fairly happy with the result.
Regards,
Cliff
Here's the code: http://pastebin.com/JAng2yRU. Would be cool if people
can test it. :) You only need to start a PostgreSQL database and
change the settings in the script.
Regards,
Frank
An example is included. I think everything works now. Feedback is
welcome. Have fun.
Regards,
Frank
How do you handle unit tests with a database that needs to be
configured? And what should I exactly test?
Regards,
Frank
It's only a wrapper for Psycopg so it's obvious that you have to write
SQL. ;) You want some kind of ORM?
I was thinking of adding some kind of query chain
[https://github.com/FSX/momoko/issues#issue/3], but I haven't had time
for this yet. The idea is that you only need one callback that is
called once the last query has finished. And maybe optional callbacks
in between. Not sure about his yet.
Regards,
Frank Smit
Just my two cents: having used SQLobject and SQLAlchemy, I'd take the
plain SQL API any day. I know a lot of people like ORMs, but I prefer
the simplicity of plain SQL. ORMs do make some tedious things simple,
but the added mental cost of the abstraction isn't worth it.
> I was thinking of adding some kind of query chain
> [https://github.com/FSX/momoko/issues#issue/3], but I haven't had time
> for this yet. The idea is that you only need one callback that is
> called once the last query has finished. And maybe optional callbacks
> in between. Not sure about his yet.
I wrote a little bit of code that simply acts as an accumulator for
multiple queries. It's not a chain like described in the link above,
but it does unroll the async callback sequence a bit, which makes things
nicer if you have to do multiple queries for a single page.
class Aggregator (object):
'''used as a callback that accumulates results of multiple
separate callbacks and finishes when they are all accounted for
'''
def __init__ (self, finish, required):
'''finish is a callback function to be invoked when all required callbacks are done
required is a list of names of callbacks (strings)
'''
self.finish = finish
self.required = required
self.values = { }
def __call__ (self, which, values):
self.values [which] = values
if set (self.required) == set (self.values.keys ()):
self.finish (self.values)
def __add__ (self, required):
self.required.append (required)
return self
The above class gets used like so (seriously simplified from working
code, so it's certainly broken).
class RequestHandler (BaseHandler):
@tornado.web.asynchronous
def get (self, ...):
def render (values):
# values is a dictionary, where the keys are the same passed to Aggregator.__init__
# and the values are the query results.
# ... do some stuff to values, render a template, etc
self.write (...)
self.finish ()
views = {
'query1': 'select * from foo',
'query2': 'select * from bar'
}
ag = Aggregator (render, views.keys ())
for key, query in views.items ():
# theoretical call to some async db api
db.execute (query, callback=functools.partial (ag, key))
Hope that makes sense and I didn't screw it up too badly in the
simplification. Basically it allows me to have a single callback (an
Aggregator object) that only calls render() once all the queries have
completed.
Regards,
Cliff
I added a simple implementation of your aggregator, Cliff. I called it
BatchQuery, since I don't know what aggregator means. :) Here's the
commit: https://github.com/FSX/momoko/commit/43fa5fe095c99210ecf4bce6da4d0e84ec2460f2
The pool cleaner is also fixed. When more than one connections needed
to be removed a IndexError popped up.
Goodnight,
Frank
One issue I see is that it doesn't appear possible to know which queries
you are seeing the results of by the time you reach the callback. For
instance, if the queries were for a blog, and one query represented
"article by id", and another were for "comments by post", you'd want to
have them named in some way so as to be able to easily reference them
later (this is one reason I used a dict rather than a sequence).
Regards,
Cliff
Regards,
Frank
This BatchQuery implementation looks really cool! Often when I have a series of statements to run, I will want to make sure they run in a specific order. Since dictionaries are unordered, maybe the list idea was better. The return could still be a dict, with the key being the sequence the query was in the original list; or just use the dict in the collect method, then sort it and return a tuple of results back.
Just a thought.
Andrew
If you need them run in a specific order, then async is not the way to
go. Rather you should simply use the standard sync API or chain your
queries via callbacks.
Regards,
Cliff
I am planning to make a chain. This is a bit more complicated than BatchQuery.
Regards,
Frank
Regards,
Frank
FYI
hasattr(link, '__call__')
could just be
callable (link)
Cliff
I'll look into ORM functionality for 0.3.0 if it's needed.
Executing queries before a request method function (get/post/...). It
also not easy. I've now modified _execute in the request handler so it
executes a query and then executes prepare and get in a callback
(example: http://pastebin.com/kWdsag9N).
Anyone got suggestions to make it nicer to work with non-blocking code?
Regards,
Frank
This will make lots of things easier. But both Swirl and Adisp don't
look like their maintained at the moment.
Here is my attempt:
https://github.com/FSX/momoko/commit/6172f54779540647c72e1b306dd276362f7759e2
After a brief look at the underlying adisp framework that Frank uses
in his library, I found it to be excellent: it makes asynchronous
programming a lot easier since you don't need to pass callbacks
around. I know other people have been looking at doing a similar
thing.
It even handles issuing multiple asynchronous requests in parallel,
all with a very easy syntax.
Ovidiu
And maybe some feedback in this? :) https://github.com/FSX/momoko/issues/6
Thanks for your work on this.
Regards,
Cliff
I've been trying to remove the Poller class and not add/remove the
connection from the IOLoop each time a connection or cursor is
created.
See commit: https://github.com/FSX/momoko/commit/62de6a2a80f63004dac6072296bdb1ca778b6f7f
I'm kinda stuck now, because I'm not able to get a cursor back.
Creating connections seems to be working fine. This is the error I
get:
Traceback (most recent call last):
File "/home/frank/Code/momoko/py2env/lib/python2.7/site-packages/tornado-2.1.1git-py2.7.egg/tornado/stack_context.py",
line 183, in wrapped
callback(*args, **kwargs)
File "/home/frank/Code/momoko/py2env/lib/python2.7/site-packages/momoko/pools.py",
line 301, in _handle_events
state = self._conn.poll()
InterfaceError: the asynchronous cursor has disappeared
Is there someone who knows more about this error?
The exception you are getting was introduced at the same time that async
cursors were made weakrefs. This is probably a clue.
Cliff
I'm looking for a solution to the case where the PostgreSQL server is
restarted. It doesn't appear that there's any handling of this type of
error. The entire Tornado app must be restarted to recover.
Regards,
Cliff