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

Reply via email to