Relationship loading for transient (or detached) objects

Issue #2372 resolved
Former user created an issue

Use case for loading transient objects

  • Your server framework environment only ever has one session. (For example, TurboGears framework, using ScopedSession.) Or you can readily determine to which session a transient instance would belong.
  • You build up simple or complex sqlalchemy transient instances with related instances and collections all fleshed out before actually adding the instance (with its relations) to the database.
  • In our case, this happens because the objects are being build up client side, in memory, with the desirable side effect that the server side can be stateless. The client keeps a copy of this transient object. While the object and its relationships are being built up (Order with Customer and OrderLines with their Products, etc.) the client often serializes the object (with its relationships) and sends it to the server, who deserializes it, translates it into transient objects that it can work with (applying business logic, loading relationships, etc), and sends those changes back to the client.
  • Only when the client side user is happy to save the changes does he click "Save" and then, this time after the server has deserialized and reconstructed these transient objects, it actually adds them to the session (merges) and they become pending/persistent objects.

Discussion

It is often the case that a related record exists in the database that would be useful to inspect (without the restriction that it be part of the session yet). * Examples: * The ZipCode records with their relationships containing tax information, shipping information, etc. are already in the database. You are building a customer instance where the zipcode foreign key is known.
* My new customer instance would fail if I tried to add it to the database at the moment because I haven't even provided all the required (non nullable) fields. Still, given that I know my session, I should be able to load this transient customer's zipcode related object and all its related collections so I can calculate tax/shipping as soon as I know the zipcode. * An OrderLine already knows the productid, the foreign key to the Product object the server would like to load, which must already exist in the database. From there, we can find prices, related items, substitutions, etc naturally through the transient object's already defined relationships:

line = OrderLine()
line.productid = u'SKU3433'
# "lazy" load the product from the database for further business logic
line.product
  • You are writing an Order that does not yet exist in the database, but you know the store it is being written from. Your server logic needs to reference that Store's instance 'order.store' regardless of whether this order is transient and not yet saved or persistent and being modified. Either way, you would like to refer to the related object through the Order mapped relationship 'store'.
  • It is extremely beneficial for the server code to be able to reference relationships and apply business logic, agnostic of whether the instance is currently transient or pending or persistent (or detached for some cases). (The record's ''session status'' rarely affects ''business logic'' needed.)
  • This methodology has worked quite well for us for nearly a year of live use.

Detached instance relationship loading

  • E.g. you still need to calculate changes, run business logic, etc for a record that you are forbidden to change in the database (think archived records which can't change for accounting reasons) so you expunge() it, calculate the deltas you need and record them elsewhere.

See also

Comments (21)

  1. Mike Bayer repo owner

    At the core of all these use cases is that you're passing primary key values into foreign key attributes directly. I never have this use case because I never think in terms of foreign key attributes - that's a database implementation detail. If I have an Order where I need it to have a ZipCode from the DB, a Product from the DB, a Store, I load them, then attach them to the object - that's how the state gets set up and this is the way the ORM was designed to work. These are also all many-to-ones from the Order, so even if I was passing around primary keys, I'd do something like:

    class Order(Base):
        def set_order_id(self, order_id):
            self.order = Session.query(Order).get(order_id)
    

    that is, I'd be letting the UOW do what it does.

    You also said you're serializing the whole thing, i.e. with the relationships, between client and server, so it seems OK that you have collections etc. already fleshed out.

    So the next part of the rationale here (are you able to edit the description above ? I can give you permission if needed), why the need to reason about your objects in terms of identifiers, rather than as their actual modeled geometry? Are you at least using natural primary keys ?

  2. Former user Account Deleted

    I do remember you telling me the design of the sqlalchemy was always with the intention that you'd set objects, not the keys. I can appreciate that (and sometimes that fits more naturally, so we do that as well). Be that as it is, why not support this use case as well, despite not having foreseen it? (Admittedly one reason is #1939, which I've implemented on my own satisfactorily for our purposes). * My main coworker (the one who found sqlalchemy) and I both "expected" this as a reasonable thing the orm should be able to/might be able to deal with. I gotta assume we aren't the only ones who would hold this as a reasonable expectation or assumption.

    But besides the fact that we just started out with the assumption it would work, to more directly answer as to why we are often setting the foreign keys: * Often these are chosen by the end user in the form of a dropdown (for state/providence, store location, shipping type, etc.) or other control like a text box for zipcode. So it naturally already just "fits" there (for many cases, at least), that the combo box or text input ''is'' the foreign key for a related object and the client sets it for us (remember, it will be serialized and shipped to the server, deserialized with the key already in place). * We ''could'' look these up as you suggest and set the object, but why? I've already told the orm ''how'' to look it up for me. Much easier to let the orm do this for you...it's already been specified in the mapper. At my previous employment I would get so frustrated that certain joins between certain related tables were sometimes more than a single column primary key join and I was irritated that engineers wouldn't always join them quite the same, potentially causing the wrong data to be fetched. The idea of the mapper being a single point of definition was ''very'' appealing to me for this reason. There is one semi "sacred" place where joins between tables are defined and only one place! It's a beautiful thing...engineers can't mess up the join ever again. But the set_order_id example above is asking me to, in effect, redefine the query that has already been defined in the mapper. Far better (in my opinion) is this:

    o=SomethingRelatedToOrder()
    o.order_id = orderprimarykey
    o.order # mapper already knows the query
    
    • It is less code (well, code and logic is in the orm), so it is easier
    • Where possible, I don't want the same join definition that is already defined in the mapper being duplicated... asking for problems
    • This use case already pretty much works perfectly (except for #1939 and maybe other issues I've not encountered)
    • Performance bonus: in many cases (those where you don't need to reference the related object) it is more efficient to just set the foreign key (fewer database round trips... a big deal when your webserver and database server are not near each other).
    • In addition to all the above, there is another use case I didn't spell out in the ticket Description above which doesn't fit your assertion ''"At the core of all these use cases is that you're passing primary key values into foreign key attributes directly."''
    • Recall that in our use case, the entire client object (and its related objects and collections) is serialized and shipped to the server and unpacked. This means everything is now transient, even objects that truly do exist in the database (those that, if merge()ed now, would yield a persistent instead of pending instance). For example, an order line's Product related item was at one point sent to the client and now has made the return trip back to the server; now it is transient, but I want to reference something like this: "order.lines0.product.''related_items''" ... Here I want the related_items collection to load from a transient product object, but it has nothing to do with whether I've set the "line[0](0).productid = id" versus "line[0](0).product = object"... either way, I'd like to load this relationship for a transient object.

    P.S. No, I can't edit ticket description or add myself to cc

    P.P.S. I won't be offended if you don't believe this is a useful feature to support for the sqlalchemy project. :) In our project it has been extremely nice (and it's been working for a year, day in and day out, at many live retail stores), so our project will continue to use it in this most extraordinarily useful way even if you don't feel it has a place in standard sqlalchemy... I won't be upset.

  3. Mike Bayer repo owner

    Replying to kentbower:

    I do remember you telling me the design of the sqlalchemy was always with the intention that you'd set objects, not the keys. I can appreciate that (and sometimes that fits more naturally, so we do that as well). Be that as it is, why not support this use case as well, despite not having foreseen it?

    I would love to ! But I need to "see" it, not just "foresee" it ! :)

    (Admittedly one reason is #1939, which I've implemented on my own satisfactorily for our purposes).

    • My main coworker (the one who found sqlalchemy) and I both "expected" this as a reasonable thing the orm should be able to/might be able to deal with. I gotta assume we aren't the only ones who would hold this as a reasonable expectation or assumption.

    It is a frequent assumption and is likely one that I had when I started. But I soon came to see it as somewhat naive, in that the objects you're putting into the Session are really part of a very limited set of state transitions. The object you deal with is either representative of database state, or it is not. When I really came to appreciate that distinction, cases like non-ORM objects that "sort of" act like them fell away. That is not to say exceptions cannot be made. But what is the distinction between behaviors:

    • If I say x = SomeObject(id=5), then I say, "x.somename", does the database emit a SELECT statement for "somename" against SomeObject? It doesn't, because SomeObject is transient or pending - "id=5" is the future id of the object.

    • If I say x = SomeObject(id=5), "x.somename = 'newname'", does it emit an UPDATE statement ? It does not - because thanks to our very simple and concise state model, we know that "id=5" is again a future identifier.

    In my view, I am constantly seeking to ensure the ORM treats column-attributes and related object/collection attributes as consistently as possible. The feature request here is basically asking us to "leak" this very consistent model, by treating relationships differently than local attributes. I can do that, but I really would like to know the exact boundary. Because what if someone then says, "Well I said SomeObject(id=5), it loads someobject.somerelated, but then doesn't load someobject.somecolumn? that's inconsistent!" Why are related objects and local columns different ? I really just need an ironclad justification, because this will come up. Unless you think SomeObject(id=5).somecolumn should also emit a SELECT. Then we'd have a lot more work to do...

    • Often these are chosen by the end user in the form of a dropdown (for state/providence, store location, shipping type, etc.) or other control like a text box for zipcode. So it naturally already just "fits" there (for many cases, at least), that the combo box or text input ''is'' the foreign key for a related object and the client sets it for us (remember, it will be serialized and shipped to the server, deserialized with the key already in place).

    OK so this means, as I suggested, that you are using natural primary keys. That is, the zipcode "12345" is the primary key of the zip object, and therefore is the foreign key in your Order object. Is that accurate ? "natural primary keys" is a key aspect of rationale here.

    • We ''could'' look these up as you suggest and set the object, but why?

    explicit is better than implicit ?

    I've already told the orm ''how'' to look it up for me.

    for a foreign key attribute yes. for a pending primary key, SQLA does it with the "pending" flag I've given you, but this is totally a breakage of the transition model. When you say X(id=5), then session.add(x), you are "telling" the orm, "id #5 does not exist yet, please insert it".

    At my previous employment I would get so frustrated that certain joins between certain related tables were sometimes more than a single column primary key join and I was irritated that engineers wouldn't always join them quite the same, potentially causing the wrong data to be fetched. The idea of the mapper being a single point of definition was ''very'' appealing to me for this reason.
    (additional appeals to automation of SQL)

    There's no debate that having simple constructs to emit SQL is useful. We're talking here about how best to expose these functions in a new way. I'm asking for how this API could be bolted right into the object state model without introducing glaring inconsistencies in behavior. It's the "right at the center of everyting" nature of it that makes it hard to do.

    There are other APIs that could do this entirely non-controversially, such as MyObject.somerelationship.load_with_keys(x, y, z). Or perhaps orm.populate_foreign_attrs(session, my_transient_object, ['product']('zipcode',)). Those are both ugly and we'd certainly make them really nice and succinct and all that, but the idea is that the state model of the ORM isn't compromised. They would make usage of all the same loader logic that's embedded in strategies.py.

    • This use case already pretty much works perfectly (except for #1939 and maybe other issues I've not encountered)

    Right an alternate API would also alleviate #1939 without adding complexity to the ORM.

    • In addition to all the above, there is another use case I didn't spell out in the ticket Description above which doesn't fit your assertion ''"At the core of all these use cases is that you're passing primary key values into foreign key attributes directly."''
    • Recall that in our use case, the entire client object (and its related objects and collections) is serialized and shipped to the server and unpacked. This means everything is now transient, even objects that truly do exist in the database (those that, if merge()ed now, would yield a persistent instead of pending instance).

    actually no, persistent objects can only become detached. They don't become transient unless you use the make_transient() function. the serialized collection consists of transients + detached.

    For example, an order line's Product related item was at one point sent to the client and now has made the return trip back to the server; now it is transient, but I want to reference something like this: "order.lines0.product.''related_items''" ... Here I want the related_items collection to load from a transient product object, but it has nothing to do with whether I've set the "line[0](0).productid = id" versus "line[0](0).product = object"... either way, I'd like to load this relationship for a transient object.

    So I have this bag of data that I want to emit a SQL query for. What's the first thing I need when I want to emit a SQL query? A connection. How do I associate my objects with a connection ? I put them back into a Session - the detached objects become persistent again, the transients, which represent "INSERT" statements, become pending. Autoflush hits, the transients become part of the transaction, everything on the Python side is also on the DB side and everything can work consistently. You're in a transaction, so if you don't want the INSERT to be effective at the end, you roll back. It works terrifically and simply. There's not "three different ways to do it". I cannot stress how important it is that SQLAlchemy's core usage model remain as simple as possible, we already have serious problems with adoption due to the number of options that already exist.

    P.S. No, I can't edit ticket description or add myself to cc

    I've added you as a developer, should be working....

    P.P.S. I won't be offended if you don't believe this is a useful feature to support for the sqlalchemy project. :)

    I'm 100% in favor of it. But the API must make sense, the rationale and "when to use it" must be crystal clear, and the core state model of the ORM needs to remain consistent. This can't just be thrown in.

  4. Mike Bayer repo owner

    had a thought:

    session.attach_for_related(myobject)
    

    something like that, would just define the state of myobject as a "row, maybe exists, maybe doesn't, will load relationships".

  5. Former user Account Deleted

    Replying to zzzeek:

    Replying to kentbower:

    It is a frequent assumption and is likely one that I had when I started. But I soon came to see it as somewhat naive, in that the objects you're putting into the Session are really part of a very limited set of state transitions. The object you deal with is either representative of database state, or it is not. When I really came to appreciate that distinction, cases like non-ORM objects that "sort of" act like them fell away. That is not to say exceptions cannot be made.

    I hear you saying they should always mirror exactly what is in the database. Agreed (for the most part, but from a practical standpoint I sure am glad sqlalchemy also has some controls that allow you to temporarily break these rules, like turning off autoflush... real problems to solve sometimes need exceptions).

    But what is the distinction between behaviors: * If I say x = SomeObject(id=5), then I say, "x.somename", does the database emit a SELECT statement for "somename" against SomeObject? It doesn't, because SomeObject is transient or pending - "id=5" is the future id of the object.

    • If I say x = SomeObject(id=5), "x.somename = 'newname'", does it emit an UPDATE statement ? It does not - because thanks to our very simple and concise state model, we know that "id=5" is again a future identifier.

    In my view, I am constantly seeking to ensure the ORM treats column-attributes and related object/collection attributes as consistently as possible. The feature request here is basically asking us to "leak" this very consistent model, by treating relationships differently than local attributes. I can do that, but I really would like to know the exact boundary. Because what if someone then says, "Well I said SomeObject(id=5), it loads someobject.somerelated, but then doesn't load someobject.somecolumn? that's inconsistent!" Why are related objects and local columns different ? I really just need an ironclad justification, because this will come up. Unless you think SomeObject(id=5).somecolumn should also emit a SELECT. Then we'd have a lot more work to do...

    Ok, there are a lot of concerns. I understand not wanting it to be inconsistent, which is why there is a part of me thinking this isn't appropriate for the sa project in general. In my mind, however, because relationships represent a different entity (generally another table) that ''could already have a record with a matching primary key in the database'', the expected behavior (again, from my viewpoint) is, indeed, quite different for a relationship versus a column. This would need to be optional behavior (i.e. the load_on_pending flag. Personally, having this flag at a higher level would be useful because in my case I want it universally turned on for all relationships, but keeping it granular is good, too).

    • Often these are chosen by the end user in the form of a dropdown (for state/providence, store location, shipping type, etc.) or other control like a text box for zipcode. So it naturally already just "fits" there (for many cases, at least), that the combo box or text input ''is'' the foreign key for a related object and the client sets it for us (remember, it will be serialized and shipped to the server, deserialized with the key already in place).

    OK so this means, as I suggested, that you are using natural primary keys.
    Many are "natural primary keys", correct. But even if they weren't, I'd still want the ability to load relationships for transient objects because of the case I mention below...

    • We ''could'' look these up as you suggest and set the object, but why?

    explicit is better than implicit ?

    In my view, invoking the relationship (InstrumentedAttribute) ''is'' explicit. It works out quite well because those relationships that are expected to be provided by the user (for example, order.lines collection) will, ''of necessity'' already be set in the object's __dict__ and therefore never will the orm be requested to lazily load such a collection/object (or else your program flow is incorrect. It is only for related objects that might already exist does the engineer ever need to (explicitly) reference them in the first place.
    Before I added this feature for us with hacks, we were always fighting the accidental use of an unloaded relationship and getting NoneType errors, etc. We'd have to first test whether it existed in the __dict__ and, if not, manually load the object from the database using the join criteria we'd already specified in the mapper. It seemed like "man, the orm should be able to do this for us."

    I've already told the orm ''how'' to look it up for me.

    for a foreign key attribute yes. for a pending primary key, SQLA does it with the "pending" flag I've given you, but this is totally a breakage of the transition model.
    Think of it as the unforeseen but useful use case instead of breaking. ;)

    (additional appeals to automation of SQL)

    There's no debate that having simple constructs to emit SQL is useful. We're talking here about how best to expose these functions in a new way. I'm asking for how this API could be bolted right into the object state model without introducing glaring inconsistencies in behavior. It's the "right at the center of everyting" nature of it that makes it hard to do.

    There are other APIs that could do this entirely non-controversially, such as MyObject.somerelationship.load_with_keys(x, y, z). Or perhaps orm.populate_foreign_attrs(session, my_transient_object, ['product']('zipcode',)). Those are both ugly and we'd certainly make them really nice and succinct and all that, but the idea is that the state model of the ORM isn't compromised. They would make usage of all the same loader logic that's embedded in strategies.py.

    But none of those would be useful to me for the very reason that they are different. Much of our code base is business logic that runs oblivious to whether it is calculating these business rules on a transient or persistent object. So making a different way to access them would wreck or complicate our ability to run this code for both cases.

    • Recall that in our use case, the entire client object (and its related objects and collections) is serialized and shipped to the server and unpacked. This means everything is now transient, even objects that truly do exist in the database (those that, if merge()ed now, would yield a persistent instead of pending instance).

    actually no, persistent objects can only become detached. They don't become transient unless you use the make_transient() function. the serialized collection consists of transients + detached.

    They are logically detached, I agree. But to the orm they are transient because they were freshly reconstructed from a new object has_identity(instance) is False for these.

    For example, an order line's Product related item was at one point sent to the client and now has made the return trip back to the server; now it is transient, but I want to reference something like this: "order.lines0.product.''related_items''" ... Here I want the related_items collection to load from a transient product object, but it has nothing to do with whether I've set the "line[0](0).productid = id" versus "line[0](0).product = object"... either way, I'd like to load this relationship for a transient object.

    So I have this bag of data that I want to emit a SQL query for. What's the first thing I need when I want to emit a SQL query? A connection. How do I associate my objects with a connection ? I put them back into a Session - the detached objects become persistent again, the transients, which represent "INSERT" statements, become pending. Autoflush hits, the transients become part of the transaction, everything on the Python side is also on the DB side and everything can work consistently. You're in a transaction, so if you don't want the INSERT to be effective at the end, you roll back. It works terrifically and simply. There's not "three different ways to do it".
    I'm not sure how to read this.... that you like the mechanism we are using or don't? I cannot stress how important it is that SQLAlchemy's core usage model remain as simple as possible, we already have serious problems with adoption due to the number of options that already exist.
    Very understood.

    P.P.S. I won't be offended if you don't believe this is a useful feature to support for the sqlalchemy project. :)

    I'm 100% in favor of it. But the API must make sense, the rationale and "when to use it" must be crystal clear, and the core state model of the ORM needs to remain consistent. This can't just be thrown in.

    So, I think sqla remains the same and this is optional functionality which can be turned on for a relationship or globally. To me session.attach_for_related(myobject) is slightly less convenient because I'd have to invoke that every time an object is created. In our world, sqlalchemy simply works by loading relationships when possible (when join has been fully specified), regardless of instance's session status.

  6. Denis Otkidach

    Replying to kentbower:

    • Often these are chosen by the end user in the form of a dropdown (for state/providence, store location, shipping type, etc.) or other control like a text box for zipcode. So it naturally already just "fits" there (for many cases, at least), that the combo box or text input ''is'' the foreign key for a related object and the client sets it for us (remember, it will be serialized and shipped to the server, deserialized with the key already in place).

    I think it would be better to call it "identity" than "foreign key". User input have to pass through validation and conversion: verifying that it exists in DB (and providing pretty error message when it doesn't), converting to object. That's how most web form libraries work.

    Also, I'm a member of "No globals" sect, and most application I wrote use several sessions. So the only reasonable way to implement your suggestion is attaching transient object to some session. Probably a new state would be useful if you don't want it becoming "pending".

  7. Former user Account Deleted

    Replying to ods:

    I think it would be better to call it "identity" than "foreign key". User input have to pass through validation and conversion: verifying that it exists in DB (and providing pretty error message when it doesn't), converting to object. That's how most web form libraries work. One of the arguments for doing this is whether I need to manually issue the query to verify that object exists or let the orm since it already knows the join condition

    Also, I'm a member of "No globals" sect, and most application I wrote use several sessions. So the only reasonable way to implement your suggestion is attaching transient object to some session. Probably a new state would be useful if you don't want it becoming "pending". Mike, I think, was also entertaining the idea of a userland function that is invoked so the user can return the appropriate session given the object and context.

  8. Mike Bayer repo owner

    Replying to kentbower:

    So, I think sqla remains the same and this is optional functionality which can be turned on for a relationship or globally. To me session.attach_for_related(myobject) is slightly less convenient because I'd have to invoke that every time an object is created. In our world, sqlalchemy simply works by loading relationships when possible (when join has been fully specified), regardless of instance's session status.

    Confirming, you want to just say x = MyObject(), and then it's magically associated with the current Session ? I.e. even calling session.add(), is too much ?

    Actually that's fine, just use an event. You can set it on mapper() across the board. There's no issue here.

    # note this is pseudocode, I'm not looking up the 
    # exact APIs here
    
    @event.listens_for(mapper, "init")
    def init(mapper, instance):
        scoped_session.attach_for_related(instance)
    
  9. Mike Bayer repo owner

    and because it's totally outside of everything you can even have it in 0.7. once we think of a better name.

  10. Mike Bayer repo owner

    oh but also we need to think how this interacts with load_on_pending. I think that would all stay the same. Or we would deprecated it and attach_for_related() would cover the "load on pending" case as well. Not clear which way to go - see I can serve your use case here, but if someone else has some slightly different need....tricky.

  11. Former user Account Deleted

    Replying to zzzeek:

    Replying to kentbower:

    Confirming, you want to just say x = MyObject(), and then it's magically associated with the current Session ? I.e. even calling session.add(), is too much ?

    Please, yes.

    Actually that's fine, just use an event. You can set it on mapper() across the board. There's no issue here.

    {{{ #!python

    note this is pseudocode, I'm not looking up the

    exact APIs here

    @event.listens_for(mapper, "init") def init(mapper, instance): scoped_session.attach_for_related(instance)

    }}}

    Excellent. Will that event also dispatch for class_mapper(cls).class_manager.new_instance()? For example, objects created through session.merge()?

  12. Former user Account Deleted

    Replying to zzzeek:

    oh but also we need to think how this interacts with load_on_pending. I think that would all stay the same. Or we would deprecated it and attach_for_related() would cover the "load on pending" case as well. Not clear which way to go - see I can serve your use case here, but if someone else has some slightly different need....tricky. I'm guessing from the couple issues we've found that I may be the only one using load_on_pending... In answer to your plea that sqlalchemy must remain simple, my first thoughts are to simplify it here and make load on pending redundant. However, I do see potential value from being able to specify for which relationships one may wish this transient load to occur (though currently not in my use case).

  13. Mike Bayer repo owner
    • changed milestone to 0.7.5

    im leaning towards the feature here, just need some more time to think about it properly..

  14. Former user Account Deleted

    Sure, take your time, I've already implemented it for our project, this is just for the (hopefully) betterment of sqlalchemy; no rush here.

  15. Mike Bayer repo owner

    Replying to kentbower:

    Excellent. Will that event also dispatch for class_mapper(cls).class_manager.new_instance()? For example, objects created through session.merge()?

    the init event isn't called for new_instance(), no. If you are using Session.merge, the new object it creates is associated with the Session directly using add(). How would attach_for_related() interact with merge() ?

  16. Mike Bayer repo owner

    im working this feature out now. it's a terrible feature, it breaks things that seem expected and im having to litter the docstring with a large variety of caveats. The feature fits very poorly into SQLA's usage model and I'm having a hard time justifying it, seeing that virtually nobody will ever use it.

    Here's an example of totally surprise behavior:

    p1 = Parent()
    sess.add(p1)
    sess.commit()
    
    c3 = Child()
    sess.enable_relationship_loading(c3)
    c3.parent_id = p1.id
    c3.parent = p1
    
    # fails.   c3.parent does a load event
    # based on c3.parent_id=p1.id, and backref handlers don't
    # fire.
    assert c3 in p1.children
    
  17. Former user Account Deleted

    Replying to zzzeek:

    im working this feature out now. it's a terrible feature, it breaks things that seem expected and im having to litter the docstring with a large variety of caveats. The feature fits very poorly into SQLA's usage model and I'm having a hard time justifying it, seeing that virtually nobody will ever use it.

    Not sure what to say... Thank you for trying? If it feels like a cancerous growth and you don't want it in there, I won't be offended if you take it back out. I've already implemented it from my own events/extensions etc. in an unofficial way. This ticket would allow me to revisit that and eventually refactor to use the officially supported way, but it isn't necessary given the extensions I've already added.

    Thanks for your efforts either way.

  18. Log in to comment