Hi Mike,

On Wed, 29 May 2019 at 15:30, Mike Bayer wrote:

> Note that we suggested approaches that work in conjunction with the 
> serialized approach you gave, the @property approach and the mapper.load() 
> event approach.     
>

Unfortunately I think they would both require massive code changes. 
Everywhere that we use Column(CompressedBinary), we'd have to rename the 
column and add property getters and setters. Unless there's a way to hook 
into the instrumentation machinery to do that automatically?
 

> However, if you are storing the fully serialized object in the column, 
> like the whole Cat object, you don't need to emit a SQL query to restore 
> it, for caching objects in serialized form you'd want to merge() it back 
> into the Session with load=False so that no SQL is emitted.


We are not serializing the whole object, only the PK, but merging it back 
into the session with all its attributes expired, so that any attempt to 
access them triggers a load. That works well for us, unless the current 
session changes in the mean time (not the problem that I originally asked 
about, but a related one, that luckily isn't biting us right now).
 

>    You still need your Session though and of course, using a threadlocal 
> variable is the best way to make that happen right now without changing the 
> type API.    
>

The problem isn't that we're passing objects between Threads, it's that we 
can use multiple sessions in the same thread. I noticed it while trying to 
create a reproducible test case in the debugger, which was switching to our 
test/scratch database, setting a local variable, and then exiting the 
context (back to our main database) with that local variable still in scope 
(with expired attributes). When the debugger rendered the repr() of the 
local variable, it causes its relationships to be loaded from the live 
database, which didn't compare equal to objects in the test database, so I 
had to restart the debugger every time this happened.

It's not critical but it was annoying because it made debugging much harder 
and slower than I thought it could/should be. I admit that this is a niche 
use case, so I consider this a feature request instead of a bug.

> If there were pre-load hooks as well as post-load, then we could set a 
> global variable to object_session(parent_object) for the duration of the 
> load.
>
>
> You shouldn't need "object_session(parent_object)",  there's only one 
> Session in play at a time within a thread so just assign the Session to a 
> thread local variable.  
>

The problem is with objects that have escaped the scope of one active 
Session, and are being refreshed when another is in scope.
 

> The best hook to use here is simply the transaction-level hooks to set the 
> current Session onto a global thread-local variable.  The ones that are 
> SessionTransaction level should work well:
>
>
> https://docs.sqlalchemy.org/en/13/orm/events.html?highlight=after_begin#sqlalchemy.orm.events.SessionEvents.after_transaction_create
>
>
> https://docs.sqlalchemy.org/en/13/orm/events.html?highlight=after_begin#sqlalchemy.orm.events.SessionEvents.after_transaction_end
>

We already have a global variable for the current Session, but I need to 
get back to the one that was used to load the object (containing the 
TypeDecorated attribute) instead of the current one.
 

> The context available is the ExecutionContext, however this isn't passed 
> to the TypeEngine bind/result processor methods right now.  That might not 
> be a bad idea in the future but for the moment would require a major 
> breaking API change that cannot be made quickly or trivially.


Could it be added as an optional argument that is only passed if the 
recipient method is expecting it?
 

>     An example of passing information between a Session and the 
> execution-level context is at 
> https://github.com/sqlalchemy/sqlalchemy/wiki/SessionModifiedSQL but this 
> doesn't give you a way to get inside the TypeDecorator methods without 
> using a global threadlocal.


Unfortunately even that doesn't help, I think, because the TypeDecorator is 
called after the statement has been executed, so there's no concept of a 
"current" load statement, only the last one, and I don't know for sure if 
that was really the same context/object/session that loaded the data being 
processed.

I'm looking at whether something like the mutable extension would have 
access to the parent object, to coerce data structures containing 
SQLAlchemy objects into serializable form on the way into the database, and 
coerce them back to SQLAlchemy objects after a load event.

Thanks again, Chris.

-- 
SQLAlchemy - 
The Python SQL Toolkit and Object Relational Mapper

http://www.sqlalchemy.org/

To post example code, please provide an MCVE: Minimal, Complete, and Verifiable 
Example.  See  http://stackoverflow.com/help/mcve for a full description.
--- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at https://groups.google.com/group/sqlalchemy.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/sqlalchemy/81bb9420-3f48-408e-b5d4-04ebc3a6ea01%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to