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 (:-)