[Update Oct 2006: here is another serving of SqlSoup. Update 2: SqlSoup documentation is now part of the SQLAlchemy wiki.]
Ian Bicking wrote in Towards PHP that a successful Python PHP-killer (as Spyce aspires to be) will need to include a simple data-access tool that Just Works.
I had two thoughts:
SqlSoup is NOT an ORM per se, although naturally it plays nicely with SqlAlchemy. SqlSoup inspects your database and reflects its contents with no other work required; in particular, no model definitions are necessary.
Here's what SqlSoup looks like, given pyweboff-ish tables of users, books, and loans (SQL to set up this example is included in the test code, but I won't repeat it here):
Modifying objects is also intuitive:
To finish covering the basics, let's insert a new loan, then delete it:
Ian Bicking wrote in Towards PHP that a successful Python PHP-killer (as Spyce aspires to be) will need to include a simple data-access tool that Just Works.
I had two thoughts:
- He's absolutely right
- I could do this with SqlAlchemy in an afternoon
SqlSoup is NOT an ORM per se, although naturally it plays nicely with SqlAlchemy. SqlSoup inspects your database and reflects its contents with no other work required; in particular, no model definitions are necessary.
Here's what SqlSoup looks like, given pyweboff-ish tables of users, books, and loans (SQL to set up this example is included in the test code, but I won't repeat it here):
>>> from sqlalchemy.ext.sqlsoup import SqlSoup >>> db = SqlSoup('sqlite:///:memory:') >>> users = db.users.select() >>> users.sort() >>> users [Class_Users(name='Bhargan Basepair',email='basepair@example.edu',password='basepair',classname=None,admin=1), Class_Users(name='Joe Student',email='student@example.edu',password='student',classname=None,admin=0)]Of course, letting the database do the sort is better (".c" is short for ".columns"):
>>> db.users.select(order_by=[db.users.c.name])Field access is intuitive:
>>> users[0].email u'basepair@example.edu'Of course, you don't want to load all users very often. Let's add a WHERE clause. Let's also switch the order_by to DESC while we're at it.
>>> from sqlalchemy import or_, and_, desc >>> where = or_(db.users.c.name=='Bhargan Basepair', db.users.c.email=='student@example.edu') >>> db.users.select(where, order_by=[desc(db.users.c.name)]) [MappedUsers(name='Joe Student',email='student@example.edu',password='student',classname=None,admin=0), MappedUsers(name='Bhargan Basepair',email='basepair@example.edu',password='basepair',classname=None,admin=1)]You can also use the select...by methods if you're querying on a single column. This allows using keyword arguments as column names:
>>> db.users.selectone_by(name='Bhargan Basepair') MappedUsers(name='Bhargan Basepair',email='basepair@example.edu',password='basepair',classname=None,admin=1)All the SqlAlchemy mapper select variants (select, select_by, selectone, selectone_by, selectfirst, selectfirst_by) are available. See the SqlAlchemy documentation (sql construction and data mapping) for details.
Modifying objects is also intuitive:
>>> user = _ >>> user.email = 'basepair+nospam@example.edu' >>> db.flush()(SqlSoup leverages the sophisticated SqlAlchemy unit-of-work code, so multiple updates to a single object will be turned into a single UPDATE statement when you flush.)
To finish covering the basics, let's insert a new loan, then delete it:
>>> book_id = db.books.selectfirst(db.books.c.title=='Regional Variation in Moss').id >>> db.loans.insert(book_id=book_id, user_name=user.name) MappedLoans(book_id=2,user_name='Bhargan Basepair',loan_date=None) >>> db.flush()
>>> loan = db.loans.selectone_by(book_id=2, user_name='Bhargan Basepair') >>> db.delete(loan) >>> db.flush()
Comments
After all, what you do with ORM is relational queries, just written other way. So, the question is: why write it other way when we already have SQL?
One problem I've found when using dbapi2 is that it is a tiny bit inconvenient to create CRUD queries because of the different ordering for different queries (e.g. update vs. insert) and to decode and access the results nicely. So I wrote a simple module that eases that task and provides an ORM-like interface, but without any of the magic. It's simple, and nothing happens behind your back, no caching, no nothing.
Check it out, released it last week:
http://furius.ca/antiorm/
That doesn't, however, mean that raw SQL is always or even often the most convenient way to access your database from Python.
It's true that many if not most ORM tools are designed by people who don't understand databases well, and hence get a lot of things wrong. SqlAlchemy isn't one of these. Remember that in a few years when hand-coding your sql gets old. :)
When you're browsing round a database checking out scheme's etc SQL is the go, but ORMish things like these make writing little python scripts which update some fields here and there heaps easier.
Also just noting: came across a little bug (i think), when using a mySQL db and the table has a mediumint field (which is mysql specific i think), sqlsoup says that the table doesn't exist... though this might be a problem further up the line (MySQLDB python interface), i'm not sure. Make all your mediumints normal ints and everything works!
Yeah, a lot of mysql weird-isms aren't supported yet by SA and hence SqlSoup. If you create a ticket with a test case at sqlalchemy.org/trac/, we'll fix it.
Put bluntly, when I think about that API function that does that, I'm getting headaches. Trying to imagine doing this API method in plain SQL (aka string manipulation in Python) is beyond my scale for pain.
yacc
when I add "db._metadata.reflect()" it seem to do the essential part...
Maybe the doc is out-of-date?
File "build/bdist.linux-i686/egg/sqlalchemy/ext/sqlsoup.py", line 422, in class_for_table
TypeError: type() argument 1 must be string, not unicode
I'm using it on the net but always have a lot of sleep connection that appear... Thanks a lot for answering... and sorry for my bad english!
Briefly, sqlsoup uses the SA connection pool, so it's normal to see idle connections in a long-running process.
If you need more details, you should ask on the SA mailing list.