g'day.

This is dbcook, a framework to cook databases from recipes, written 
as python declarations. Eventually the result may be edible (;-)

repository: svn co https://dbcook.svn.sf.net/svnroot/dbcook/trunk

http://dbcook.sf.net

If u're interested, have a look, dbcook/usage/example1.py might be a start. 
The directory-structure is still in flux. 
To use it, the internal dbcook/ has to be accessible somehow (PYTHONPATH or else).

Licensed under MIT-license. Dependencies:
 * SQLAlchemy, v0.5 or v0.4 series. Support for v0.3 and early-v0.4 is dropped at rev259
 * kjbuckets (from gadfly) for graph-arithmetics



Here a short description what it probably is, and can do:

A framework for declarative abstract mapping of object hierarchies/relations into 
a (relational or not) database, completely hiding the DB where possible.

The "language/syntax" itself is DB-backend independent.
Currently the available builder is over SQLAlchemy as backend
(so it looks like another wrapping declarative translator).
Eventual future backends may include RDFalchemy or other non-SQL data storages.

Usage cases/levels:
 * DB-definition - completely hides/automates the table/ column/ key/ constraint/
    mapper/ whatever creation. The user can control certain characteristics of the way
    the mapping happens, mostly related to hierarchy and relations between objects
    (subclasses, instances, leafs, uniqueness etc).
 * generate a source of equivalent plain SQLAlchemy-calls to build the DB-definition -
    very useful for testing and/or generating routine mappings, with or without
    actualy using dbcook afterwards
 * use plain SQLAlchemy once the definition is done - e.g. session.query( class)..
 * dbcook.expressions can partialy abstract the query generation, converting from plain 
	python functions/expressions (of objects' attributes) into backend/SQL clauses:
    ``lambda self: (self.friend.manager.age < 40) & self.name.endswith('a')``
 * writing own reflectors (that walk the declared classes and
    extracts info from them), eventualy allowing different "language/syntax"

Work started somewhen in october 2006. Now it can handle: 
 * data column types - the actual mapping is separate from object declaration
 * reference columns - plain, forward-declared, self-referential -> foreign keys
 * automatic solving of cyclical references/ dependencies
    (putting proper alter_table / post_update)
 * class inheritance, class inclusion (inheritance without
    database mapping of the base), and virtual classes (those never
    have instances). More in mapcontext._Base
 * polymorphism - giving 3 kinds of queries for each mapped class:
    all, base-only, subclasses only
 * any combination of table-inheritance-types within the tree
    (concrete/joined/no-single-yet) - defined localy or hierarchicaly;
    beware that sql-polymorphism only works with joined-table for now
 * associations (many-to-many) - implicit and explicit
 * collections (one-to-many) 
 * dbcook.usage.samanager is a correct context-like keeper of all db-related SQLAlchemy things in
    one place, on destroy() will try hard to clear _all_ side-effects of its existence,
    allowing use of same objects within several/different subsequent DB-mappings.


Example declarations:

import dbcook.usage.plainwrap as o2r
class Text( o2r.Type): pass
class Int( o2r.Type): pass

class Address( o2r.Base):
	place = Text()

class Person( o2r.Base):
	name = Text()
	age  = Int()
	address = o2r.Type4Reference( Address)
	friend  = o2r.Type4Reference( 'Person')

class Employee( Person):
	job = Text()

#build it
o2r.Builder( metadata, locals(), { Text: sqlalchemy.String } )

...
for p in expression.query1(
            lambda self: self.friend.friend.friend.age > 24,
            klas=Person, session=session) ):
	print p

..... thats it. and no mentioning of tables, columns etc whatsoever .....


So far it is just a library. There is no single way to use it. The directory
usage/ has 2 possible reflectors (that walk the declared classes and extract 
info from them), one is just plain python (plainwrap.py), another is for
my static_type/ framework, for makeing staticly-declared structs 
(a-la java/C++ semantics) in python - do mail if interested.

Certain pieces are (almost) independent and are usable as is without all the
rest: expression.py, usage/hack*py, sa_generator.py - all SQLAlchemy related.

The tests/ directory contains 2 types of tests - sa/ which proves that the way
SA is used in dbcook is correct/working, and mapper/other which check whether
the cooker does right thing as result. It still uses makefile to run all stuff.

Some todo's, long- or short- term:
 - some documentation, translate all in both languages
 - tests for all the stuff, systematically in most of combinations
 - expressions.joins need rethinking (upgraded to SA 0.4 ok)
 - generate sql-server-side functions, triggers etc
 - single-table inheritance
 - concrete-polymorphism: polymunion of (type,id) key - SA doesnot fully do it
 - other reflectors/"syntaxes" - e.g. elixir-like 
 - sub-structures as value (contained IN the parent class, not a reference 
     to elsewhere, but still used as x.y; think C memory layout)
 - user-notion types for reflector, e.g. aggregators are like a relation
 - caching tables/columns - e.g. aggregators
 - autoload / reverse-engineering / upgrade / migration (see misc/metadata/)

see dbcook/misc/readme.txt for some usable recipes.

have fun

svil 
svilen_dobrev at users . sourceforge . net
az at svilendobrev . com
or see me at sqlalchemy's newsgroup - sqlalchemy at googlegroups . com

===================

> Isn't it what does already Elixir?
not really. Frankly, i dont know much elixir, just some impressions.
elixir is sort of syntax sugar over SA, with very little 
decision-making inside. It leaves all the decisions - the routine 
ones too - to the programmer. At least thats how i got it.

dbcook hides / automates _everything_ possible - the very concept of 
existing of relational SQL underneath is seen only by side-effects, 
e.g. the DB_inheritance types "concrete-", "joined-", "single-" 
table. It decides things like where to break cyclical references with 
alter_table/post_update; makes up the polymorphic inheritances - 
whatever the tree, etc.

Of course this is only the declaration/creation part (model-definition - 
building the DB); after that it can cover only small/simple part of the 
queries (model usage) - possibilities there are endless.
That's why there is plain SA underneath, once the "python function over 
objects converted into SA-expression over tables" path gets too 
narrow - or just ends.

dbcook does not have assign_mapper-like things, putting query methods 
on the objects. it leaves all that to you. Although one day there 
will be a usage-case/example of some way to do it - once i get there.

more differences maybe - no idea, someone has to have time to try both (:-)