On Jan 13, 2014, at 1:19 PM, Alessandro Molina <[email protected]> wrote:
> Just noticed that SQLAlchemy 0.9 broke a LazyForeignKey class that was > provided by TurboGears tgext.pluggable to make possible to declare foreign > keys to models not yet defined at the time the ForeignKey was declared. > > The main use was something like: > > class User(DeclarativeBase): > __tablename__ = 'users' > > registration_id = Column(Integer, LazyForeignKey(lambda: > app_model.Registration.uid)) > > where app_model.Registration was available only at runtime and not when User > class was declared. > Also app_model.Registration table name was not known (as it dynamically > generated to avoid collision between tables of multiple apps plugged at > runtime), so it was not possible to declare the foreign key as a string with > the "tablename.column_name" syntax. > > LazyForeignKey was simply implemented as a subclass of ForeignKey which > provided a custom _colspec property which would resolve the given function > whenever it was first accessed: > https://github.com/TurboGears/tgext.pluggable/blob/master/tgext/pluggable/sqla/relations.py#L4 > > This approach doesn't work anymore in SQLA 0.9 as _colspec gets accessed by > the ForeignKey constructor itself, calling the lambda at Column declaration > time. > > It seems to me that adapting the LazyForeignKey class to SQLA 0.9 would > require messing a lot more with SQLAlchemy internal code, which is something > I would like to avoid. > > What would be the suggested way to achieve the same feature on SQLA 0.9? Well if the table name were known, then you wouldn’t need such a feature in the first place as you can just put a string name in ForeignKey(). However, as this seems to be a case where a table + declared class is to eventually refer to some other Table whose name is not known yet, the ForeignKey() should not exist on the Column at all until that other Table is available. A simple call to User.__table__.append_constraint(ForeignKeyConstraint(<now you know what to put here>)) at the time this information is known would be the cleanest. to get at this you’d need to use events. First a way to make an XYZ() object that can be placed inside of Column and know about it: from sqlalchemy.schema import SchemaItem from sqlalchemy import event class MyThing(SchemaItem): def _set_parent(self, parent): pass @event.listens_for(MyThing, "after_parent_attach") def my_thing_attached(target, parent): print("Attaching target %s to parent %s", target, parent) from sqlalchemy import Table, MetaData, Column, Integer Table('t', MetaData(), Column('x', Integer, MyThing())) Then you want to register your LazyForeignKey object in a registry that will allow the callable to be invoked once your “table setup” system has completed - you’d call the callable, construct a ForeignKeyConstraint, then append it to the constraints of the parent column.table. the only private API in use here is the _set_parent() override. If I change that API it would be something simple.
signature.asc
Description: Message signed with OpenPGP using GPGMail
