I'm playing with the idea of writing an object-relational mapper
(O/RM) library for Chicken. My personal goal for the O/RM would be to
auto-generate a domain-specific syntax for an application, mapped onto
the actual, relational schema of the database, and encapsulating the
low-level database API. Theoretically, an O/RM can make it possible to
switch an application between different back-end databases, but I
think the free syntax and high-level functions are the real win.
Before getting too ahead of myself (i.e. before writing any actual
code!) I thought I'd post a sample syntax/API to the list for any
feedback. Fancy macros aren't my forte, and I may be creating a
(define-schema) syntax that's unncessarily hard to parse.
Is anyone else interested in this kind of thing? I'd love to hear your feedback.
I've written the beginnings of something like Ruby's activerecord for Chicken and Postgres, so I have an interest in this. I read the Django tutorial you linked, and I'm not impressed. It's trying to take over "CREATE TABLE" from SQL, which is a pointless exercise in NIHism, and an example of the trait that so many people dislike about frameworks - that they take over your code.
In your snippet, you've even gone so far as to hide char, varchar, timestamp, or whatever, and renamed them "string" and "date-time". This is the sort of thing that makes sense when you're trying to write something for non-technical users who don't want to learn SQL, like, say, if you work for a newspaper and you want journalists to be able to write webpages with your framework (which is Django's origin). Is that your audience? For a typical Chicken hacker, this is just added complexity. What's more, I trust Postgresql, and I see no need to deal with bugs in reimplementations of parts of it.
Take a look at this: http://www.aaronsw.com/weblog/rewritingreddit
(the part about Django) and
before following Django's example too closely. In my experience, trying to imitate something from other languages too closely in Scheme generally turns out to be a mistake.
Anyway, if your tables are already defined in SQL, you can get all of this information from Postgres. Here's a little snippet from my embryonic ORM which returns the name, type, and default value of every column in a table:
(define (get-table-metainfo table-name)
(pg:query-tuples (conc "SELECT a.attname, format_type(a.atttypid, a.atttypmod), z.adsrc "
" FROM (pg_class c INNER JOIN pg_attribute a ON
c.oid = a.attrelid "
" AND c.relname = '" table-name "' AND a.attnum > 0) LEFT OUTER JOIN pg_attrdef z ON "
"c.oid = z.adrelid AND z.adnum = a.attnum ORDER BY
This does have some problems, because, for example, if a column is defined with SERIAL PRIMARY KEY, the default is listed as "nextval('public.users_id_seq'::text
)" instead of an integer. My (half-assed) solution was to have the ORM get the real value of these fields upon saving the object the first time:
(define user (make-active-record 'users))
(user 'id) => "nextval('
(user 'id) => 154
This isn't perfect, but you have a lot of the same problems if you declare the tables in Scheme anyway, because the real problem is that you don't know the value of something like "nextval('
public.users_id_seq'::text)" until you ask the database.
If you insist on building something Django-like, please build it on top of something that does less, and make it optional.
Also, just in case you didn't notice (I didn't announce it), I recently added more detailed error handling to the postgresql egg for the purpose of making it easier to write an ORM. Now you can do something like
(condition-case (pg:query-tuples "INSERT INTO users (id) VALUES(132)" db-conn)
(e (exn postgresql) (case ((condition-property-accessor 'postgresql 'error-code) e)
((not-null-violation) "No NULLs for you!")
((unique-violation) "You've already registered!")
instead of checking every condition in Scheme, which will make your life a lot easier, and will work the first time.
And, in case you were worried about duplication of effort, I've put my ORM on the back burner. Not being especially fond of OO, I'm writing something more like CL-SQL's functional SQL interface instead, though I would gladly use an ORM instead if one existed. I really just don't want to manually conc strings together, and have to remember to escape SQL strings, which I will forget and then some 1337 Hax0r will drop my tables...