Skip to content

language to describe recipes for persistency of python classes, within their declarations. Independent of the database, hides it as much as technicaly possible - so same recipe / queries can be rendered into SQL/Alchemy (working), or others - RDF, key-value, google-datastore (todo). For big class-hierarchies. Has bitemporal extension, aggregatio…

License

hstanev/dbcook

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

dbcook

language to describe recipes for persistency of python classes, within their declarations. Independent of the database, hides it as much as technicaly possible - so same recipe / queries can be rendered into SQL/Alchemy (working), or others - RDF, key-value, google-datastore (todo). For big class-hierarchies. Has bitemporal extension, aggregations, polymorphic associations (for multiple inheritance), source-code-generator, etc. Even the query filters can be represented as python expressions..

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

(was at http://dbcook.sf.net)

look at dbcook/usage/example1.py as 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 .....

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.

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 it
  • 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

svilen

az at svilendobrev . com

or see me at sqlalchemy's newsgroup - sqlalchemy at googlegroups . com


Isn't it what does already Elixir?

not really. IMO 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.

while 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 a way to do it - once i get there.

more differences maybe.. try

About

language to describe recipes for persistency of python classes, within their declarations. Independent of the database, hides it as much as technicaly possible - so same recipe / queries can be rendered into SQL/Alchemy (working), or others - RDF, key-value, google-datastore (todo). For big class-hierarchies. Has bitemporal extension, aggregatio…

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published