Michael Bayer wrote:
On Apr 6, 2006, at 11:37 PM, Daniel Miller (said that he preferred):
t = Table('mytable', columns...)
something.get_connection().execute(t.select())
see i dont like that syntax at all. if im writing a program that just
connects to a single database, i dont want to have to worry about
opening connections everywhere, passing them around
Well you won't have to worry about opening connections everywhere and passing
them around--that's very easy to take care of by using the thread-local pattern
in your application. The point is, your application decides to use the
thread-local pattern, and it's not built into the core of the ORM
library/framework/whateveryoucallit.
....I dont think its
unreasonable that SA does some scope management. and of course you
could say to everyone "well just write wrappers" for all that, but then
we get into that whole __getattr__ thing that is so inefficient.
plus youll have an endless parade of people complaining that "SQLAlchemy is
too hard" / "SQLAlchemy requires you to write a whole framework around
it just to use it" etc. it has to be very very simple for simple
things...it doesnt want to force verbosity when its not really
required. what we're doing now is adding better capabilities for
verbosity for those that prefer/require it.
Yes, but SA should not impose extra overhead on those people who do not wish to
use its scope management. For example, here's a better implementation of Query
(no session lookup overhead) and ThreadLocalQuery (adds lookup mechanism for
those who need it). There's no reason to implement the thread-local thing
unless it's needed:
class Query(object):
def __init__(self, mapper, session, ...)
self.mapper = mapper
self.session = session # simple attribute
...
...
class ThreadLocalQuery(Query):
def __init__(self, mapper, ...)
super(ThreadLocalQuery, self).__init__(mapper, None, ...)
def _get_session(self):
return objectstore.get_session()
session = property(_get_session)
the whole thing is not "magic", its just objects that know how to bind
themselves to a resource to get something done. so now we're making all
the binding optional.
# table bound to an engine
t = Table('mytable', engine, ...)
t.select().execute()
# table not bound to anything
t = Table('mytable', None, ...)
I know I may be contradicting what I said before, but doesn't the table need to
have a schema-like object so it knows how to relate to other tables in the same
database? This was a problem with SQLObject because all tables were placed in a
global registry, which made it difficult to have two different table
definitions with the same name. A Schema would act as a scope within which all
table definitions must have unique names.
# execute via using()
t.select().using(engine).execute()
# ...or with the keyword argument, sure...
t.select(engine=engine).execute()
# but when you do that, you really have this:
s = t.select(engine=engine)
# s is bound to an engine ! theres that magic again !
so anyway, heres the part youre not going to like (or....the next part
you wont like...)...this is just the idea I have so far but havent
completed it. the engine youre passing around to using() is now just an
AbstractEngine (seeking a better name)...its just a thing that knows how
to compile statements and execute them.
That's part of the problem. AbstractEngine is doing too much if it knows how to
compile statements _and_ how to execute statements (those are separate
concerns). I'm suggesting that the engine should only know how to compile
statements and how to create connections for a given database. The engine
should have no knowledge of the lifecycle of a connection (i.e. it does not
maintain an internal connection instance). The Session should manage the
connection lifecycle, not the engine. This is how Hibernate does it.
which can be....the SQLEngine
we pass around, which knows how to pull from the pool and execute
statements....or....a ConnectionProxy that represents just one specific
connection context ! well geez why more of this rubbery API stuff ?
because say it only took the connection and tried to be more rigid about
argument types, like a Java API would do it. then you will see,
endlessly throughout an application:
table.select(conn = engine.connect()).execute()
or even:
conn = engine.connect()
try:
conn.execute(table.select())
conn.execute(table.update(...))
finally:
conn.close()
which at first glance may seem like, hey thats a great, traditional
API!
It _is_ a great traditional API, and there's a reason that it's done that way.
It's extensible and reusable because the connection API is orthogonal to the
SQL API. This will definitely be needed if you ever want to have other
frameworks built on top of SA's SQL API.
but then remember that we're doing Python, not J2EE....then you
might see it as just relentlessly and heartlessly verbose. What you
have up there is raw JDBC with a little bit of Python niceness, or just
some thin layer over DBAPI. It is either a total pain in the ass to
write verbosity like that all over an app, or you are forced to write
your own frameworks each time
No! this "framework" that you're talking about can be included with SQLAlchemy, it's just
a different layer that does resource management "the right way". In other words, in a way
that can be extended or even totally reimplemented to suit the needs of a particular environment.
What I'm trying to get across is that SA combines all of these concerns into one monolithic layer
that does SQL construction, connection management, and transaction management all in the same layer
with a nice SQL API wrapped around it. I'd like to be able to use the nice SQL API without using
all the built-in resource management stuff. IMHO, SA is really trying to do too much here by
including resource management code in it's engine interface.
to relegate the repetetive detail of
patterns like the above to some central hidden-away location. Well I
still dont think SA is a "framework" (since we know what scorn that will
attract) but geez it has to do more than that example above.
Of course it does, just allow me to use the parts I want and don't force me to
use the parts I don't want.
everyone
whos advanced will just write a wrapper around the connect()s and
probably the select()/execute() methods so that it just pulls in an
engine associated with the table, and everyone whos not advanced will
just blog and complain that SA is way too hard to use and unnecessarily
verbose. its just not Python to me !
Yes. And it's fine if a table needs a Schema as its container. Just so
the schema doesn't tie the table to a particular engine. Although to
complete the analogy between Table and Column for Schema and Table it
should be constructed like this:
schema = Schema('myschema',
Table('table_1', columns...),
Table('table_2', columns...),
...
)
I have a similar reaction to this....its completely consistent with
itself but not really practical. the current schema construction is
modeled after the typical experience working directly with a database
command line. there is a Table() syntax with column specs inside of it,
but schema selection is usually just determined by your initial
connection, or maybe a single "use" statement. theres a certain "feel"
its trying to maintain. you could force people to get to their tables
via "myschema.t.tablename" but at that point youre just beating them
over the head with how "perfectly consistent" the whole thing is.
OK I'll admit, it may make more sense to pass a Schema object to the
constructor of Table. I suppose that's fine since there's no higher-level
object that will combine a group of schemas. Although we might want to think of
using a different name since it will be confusing to do this:
t = Table("mytable", schema, schema="public", ...)
Those two schema parameters are different things, and we need them both.
I think i feel the need to provide somewhat flexible interfaces to these
things since its the way I would want to use it ! basically if theres
any structures I know i will write every single time id build an
application with SA, like objects that can be bound to different kinds
of "execution contextual" objects, then Im going to make those
structures part of the library to start with.
anyway, while we still arent seeing quite the same vision yet, the whole
"de-enginification" of everything is underway which is a lot closer to
what youve suggested, all the binding and scoping you dislike will be
optional and less emphasized....and once thats complete, we can visit
again APIs that are still too ambiguous, and we will have more ability
to hone it down even more, including things like using() vs. keyword
arguments, connections vs. engines, and all that stuff.
Good. I feel like we're getting somewhere. There's just a few of these things I
think are very important to have done correctly in the core of SQLAlchemy. I'm
not suggesting that they be forced on people using SA every day, but they're
definitely going to bite us later if we don't do it right now.
You have no idea how long it took me to write all this. I've had to start over
many times because there's so much about what your saying that is almost
correct, or correct in a different context than I'm thinking. I really think my
suggestions will make SA more useful in the long run because it will be a more
general building-block library and less of a fill-in-the-blanks type framework.
~ Daniel
-------------------------------------------------------
This SF.Net email is sponsored by xPML, a groundbreaking scripting language
that extends applications into web and mobile media. Attend the live webcast
and join the prime developer group breaking into this new coding territory!
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=110944&bid=241720&dat=121642
_______________________________________________
Sqlalchemy-users mailing list
Sqlalchemy-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/sqlalchemy-users