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.

Reply via email to