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 [email protected].
> To unsubscribe from this group, send email to
> [email protected].
> 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 [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/sqlalchemy?hl=en.