Michael, Interesting stuff, The first part I had "almost" covered, I did not have the _constructor part. It wil be part of something more complex…
Thankx, Martijn On Jan 30, 2012, at 17:46 , Michael Bayer wrote: > > On Jan 30, 2012, at 9:49 AM, Martijn Moeling wrote: > >> sorry I have to get back on this. I renamed all Columns in my application >> definitions to MyColumn a while back. and everything worked. >> Now that I'm starting to use functionality of MyColumn. (The reason I needed >> this) I run into some trouble. > > > so we can skip the easy "_constructor" thing and implement the rest of > Column. If you read the first error message you get with a straight subclass: > > TypeError: Could not create a copy of this <class '__main__.MySpecialColumn'> > object. Ensure the class includes a _constructor() attribute or method which > accepts the standard Column constructor arguments, or references the Column > class itself. Original error: __init__() takes exactly 2 arguments (8 given) > > > So Column goes through a lot of trouble to try to diagnose what's going on. > It's telling us to create a method called _constructor(), that accepts the > standard arguments that Column does. The return value is our modified column: > > class MySpecialColumn(Column): > x = 0 > y = 0 > def __init__(self, type_, **options): > filtered_options = {} > for name, option in options.items(): > if hasattr(self, name): > setattr(self, name, option) > else: > filtered_options[name] = option > > Column.__init__(self, type_, **filtered_options) > > def _constructor(self, name, type_, **kw): > kw['x'] = self.x > kw['y'] = self.y > col = MySpecialColumn(type_, **kw) > col.name = name > return col > > >> >> At the other end, I might need to introspect the objects, I have tried but I >> have trouble in relating to X or Y since the Name Column is an >> InstrumentedAttribute. > > Yeah to get at them directly you'd need to say: > > assert MyClass.value.property.columns[0].x == 5 > > if you want MyClass.value.x == 5, you need to tack that on when it gets > instrumented: > > @event.listens_for(Base, 'attribute_instrument') > def configure_listener(class_, key, inst): > if isinstance(inst.property, ColumnProperty) and \ > isinstance(inst.property.columns[0], MySpecialColumn): > inst.x = inst.property.columns[0].x > inst.y = inst.property.columns[0].y > > >> >> >> One other thing. I can get Columns by iterating over self.__table__.Columns. >> I can get Foreign keys using: >> inspector.get_foreign_keys(SomeClass.__tablename__) > > I'd note those are not at all equivalent operations, in that inspector is > going to go out to the database in order to get the FK information. > Assuming your table metadata has it configured, you can get it locally by > iterating through SomeClass.__table__.foreign_keys: > > for fk in MyClass.__table__.foreign_keys: > print fk.parent, fk.column > > > if you want the full constraint object, which I'd recommend if you have > composite FKs in use: > > for const in MyClass.__table__.constraints: > if isinstance(const, ForeignKeyConstraint): > for element in const.elements: > print element.parent, element.column > > >> >> But Now I need to access the relation objects defined in my classes and >> Introspect them. > > relationship is not the same as a foreign key, see below... > > There's a ticket for 0.8 that would attempt to provide more accessors for > these things, including things like mapper.relationships and stuff like that. > Feel free to add in things we should consider on > http://www.sqlalchemy.org/trac/ticket/2208. > > an example with everything happening, including in a tricky from_self() query: > > from sqlalchemy import create_engine, Column, Integer, ForeignKey, > ForeignKeyConstraint > from sqlalchemy.orm import Session, configure_mappers, relationship > from sqlalchemy.ext.declarative import declarative_base > from sqlalchemy import event > from sqlalchemy.orm.properties import ColumnProperty, RelationshipProperty > > class MySpecialColumn(Column): > x = 0 > y = 0 > def __init__(self, type_, **options): > filtered_options = {} > for name, option in options.items(): > if hasattr(self, name): > setattr(self, name, option) > else: > filtered_options[name] = option > > Column.__init__(self, type_, **filtered_options) > > def _constructor(self, name, type_, **kw): > kw['x'] = self.x > kw['y'] = self.y > col = MySpecialColumn(type_, **kw) > col.name = name > return col > > Base= declarative_base() > > @event.listens_for(Base, 'attribute_instrument') > def configure_listener(class_, key, inst): > if isinstance(inst.property, ColumnProperty) and \ > isinstance(inst.property.columns[0], MySpecialColumn): > inst.x = inst.property.columns[0].x > inst.y = inst.property.columns[0].y > > class MyClass(Base): > __tablename__ = "sometable" > > id = Column(Integer, primary_key=True) > foo_id = Column(Integer, ForeignKey('foo.id')) > value = MySpecialColumn(Integer, nullable=False, x=5, y=12) > myfoo = relationship("MyFoo") > > class MyFoo(Base): > __tablename__ = 'foo' > id = Column(Integer, primary_key=True) > > > assert MyClass.value.property.columns[0].x == 5 > > # configure mappers now to fire off > # the instrumentation event ahead > # of time, just so we can see value.x > configure_mappers() > assert MyClass.value.x == 5 > > # foreign keys > > for fk in MyClass.__table__.foreign_keys: > print fk.parent, fk.column > > for const in MyClass.__table__.constraints: > if isinstance(const, ForeignKeyConstraint): > for element in const.elements: > print element.parent, element.column > > # relationships > for prop in MyClass.__mapper__.iterate_properties: > if isinstance(prop, RelationshipProperty): > print "relationship:", prop.key, prop.mapper.class_, > > e = create_engine("sqlite://", echo=True) > Base.metadata.create_all(e) > > s = Session(e) > s.add_all([ > MyClass(value=12), MyClass(value=18) > ]) > s.commit() > > # from_self() here creates a subquery and exercises the > # column copying > print s.query(MyClass).filter( > MyClass.value.between(MyClass.value.x, MyClass.value.y) > ).from_self().all() > > -- > You received this message because you are subscribed to the Google Groups > "sqlalchemy" group. > To post to this group, send email to sqlalchemy@googlegroups.com. > To unsubscribe from this group, send email to > sqlalchemy+unsubscr...@googlegroups.com. > For more options, visit this group at > http://groups.google.com/group/sqlalchemy?hl=en. > -- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To post to this group, send email to sqlalchemy@googlegroups.com. To unsubscribe from this group, send email to sqlalchemy+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/sqlalchemy?hl=en.