All pastes #935955 Raw Edit

Someone

public text v1 · immutable
·published 2008-03-10 00:57 UTC
rendered paste body
from sqlalchemy import *
from sqlalchemy.orm import *

from sqlalchemy.orm.interfaces import MapperProperty
from sqlalchemy.orm.properties import PropertyLoader
from sqlalchemy import exceptions
    
class Thingy(type):
    def __init__(cls, classname, bases, dict_):
        if '_thingy_class_registry' in cls.__dict__:
            return type.__init__(cls, classname, bases, dict_)
        
        cls._thingy_class_registry[classname] = cls
        tablename = cls.__tablename__
        our_stuff = {}
        for k in dict_:
            value = dict_[k]
            if not isinstance(value, (Column, MapperProperty)):
                continue
            prop = cls.__deferred_relation(value)
            our_stuff[k] = prop
        cls.__table__ = Table(tablename, cls.metadata, *[
            c for c in our_stuff.values() if isinstance(c, Column)
        ])
        cls.mapper = mapper(cls, cls.__table__, properties=our_stuff)
        return type.__init__(cls, classname, bases, dict_)
    
    def __deferred_relation(cls, prop):
        if isinstance(prop, PropertyLoader) and isinstance(prop.argument, basestring):
            arg = prop.argument
            def return_cls():
                return cls._thingy_class_registry[arg]
            prop.argument = return_cls
            
        return prop
        
    def __setattr__(cls, key, value):
        if isinstance(value, Column) and hasattr(cls, 'mapper'):
            raise exceptions.InvalidRequestError("Can't configure new Columns on an already-completed class")
        elif isinstance(value, MapperProperty) and hasattr(cls, 'mapper'):
            cls.mapper.add_property(key, cls.__deferred_relation(value))
        else:
            type.__setattr__(cls, key, value)
        
def declarative_base(engine=None, metadata=None):
    lcl_metadata = metadata or MetaData()
    class Base(object):
        __metaclass__ = Thingy
        metadata = lcl_metadata
        if engine:
            metadata.bind = engine
        _thingy_class_registry = {}
        def __init__(self, **kwargs):
            for k in kwargs:
                setattr(self, k, kwargs[k])
    return Base

if __name__ == '__main__':
    Base = declarative_base(create_engine('sqlite://', echo=True))
    
    class User(Base):
        __tablename__ = 'users'
    
        id = Column('id', Integer, primary_key=True)
        name = Column('name', String(50))
        addresses = relation("Address", backref="user")
        
    class Address(Base):
        __tablename__ = 'addresses'

        id = Column('id', Integer, primary_key=True)
        email = Column('email', String(50))
        user_id = Column('user_id', Integer, ForeignKey('users.id'))

    class Keyword(Base):
        __tablename__ = 'keywords'
        id = Column('id', Integer, primary_key=True)
        name = Column('name', String(50))
    
    user_keywords = Table('user_keywords', Base.metadata, 
        Column('user_id', Integer, ForeignKey('users.id')), 
        Column('keyword_id', Integer, ForeignKey('keywords.id'))
    )
    User.keywords = relation(Keyword, secondary=user_keywords, backref='users')
        
    Base.metadata.create_all()

    sess = create_session()

    u1 = User(name='ed', 
        addresses=[Address(email='eds email')],
        keywords = [Keyword(name='one'), Keyword(name='two')]
        )

    sess.save(u1)
    sess.flush()
    
    print sess.query(User).options(eagerload('addresses')).all()