Hi,

I've been using SqlAlchemy and Alembic for a year and a half now and it's 
been a great experience, thanks a lot for providing such an awesome piece 
of engineering!

Recently I stumbled upon a problem that I'm wondering if it might point to 
an inconsistency in SqlAlchemy, or (perhaps more likely) that I'm doing 
something I shouldn't. I've been passing detached SqlAlchemy orm models to 
other processes using python's multiprocessing.Pool, effectively causing 
the models to get pickled. This has been working as expected, and I haven't 
had any trouble with it until now. I realize the models are complex objects 
though, so perhaps doing this was a bad idea in the first place?

Anyway, I recently had a need for specifying eager relationship loads using 
the explicit sqlalchemy.orm.Load() variant described here:
https://docs.sqlalchemy.org/en/13/orm/loading_relationships.html#sqlalchemy.orm.Load

I'm providing full steps for reproducing this situation below, but the 
essence is that I see a difference in 'pickleability' of results 
originating from the following two, in my understanding equivalent, ways of 
specifying an eager relationship load:
session.query(MyClass).options(selectinload('widget'))
session.query(MyClass).options(Load(MyClass).selectinload('widget'))

When the resulting MyClass objects do *not* have any corresponding 
'widget', the latter query specification will yield results that can *not* 
be pickled, whereas results from the former query can always be pickled. Am 
I naïve to expect pickling to work, or have I stumbled across an 
unintentional quirk in SqlAlchemy and that this really is expected to work?

I've tried my best to reduce the setup into a minimal but complete set of 
steps to reproduce below. The original setup uses a MySQL engine, but below 
I use 'sqlite:///:memory:' to keep it simple. The error output is not 
exactly the same, but I'm guessing that's not essential since the overall 
problem is the same.

Thanks and best regards,
Jan Wegger


Starting with a clean virtualenv and just sqlalchemy installed:
$ python
Python 3.7.3 (default, Mar 27 2019, 09:23:15)
[Clang 10.0.1 (clang-1001.0.46.3)] on darwin
>>> import sqlalchemy
>>> sqlalchemy.__version__
'1.3.5'

Common setup:
>>> from sqlalchemy import create_engine
>>> engine = create_engine('sqlite:///:memory:')
>>>
>>> from sqlalchemy.ext.declarative import declarative_base
>>> Base = declarative_base()
>>>
>>> from sqlalchemy import Column, Integer, String, ForeignKey
>>> from sqlalchemy.orm import relationship
>>>
>>> class MyClass(Base):
...     __tablename__ = 'my_class'
...     id = Column(Integer, primary_key=True)
...     widget_id = Column(Integer, ForeignKey('widget.id'))
...     widget = relationship('Widget')
...
>>> class Widget(Base):
...     __tablename__ = 'widget'
...     id = Column(Integer, primary_key=True)
...
>>> Base.metadata.create_all(engine)
>>>
>>> from sqlalchemy.orm import sessionmaker
>>> Session = sessionmaker(bind=engine)
>>> session = Session()
>>> obj = MyClass(widget_id=None)
>>> session.add(obj)
>>> session.commit()
>>> session.close()

Query with explicit Load():
>>> from sqlalchemy.orm import Load
>>> session = Session()
>>> obj = 
session.query(MyClass).options(Load(MyClass).selectinload('widget')).one()
>>>
>>> import pickle
>>> pickle.dumps(obj)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: Can't pickle local object 'setquit.<locals>.Quitter'

Query with sqlalchemy.orm.selectinload() option:
>>> from sqlalchemy.orm import selectinload
>>> obj = session.query(MyClass).options(selectinload('widget')).one()
>>>
>>> import pickle
>>> pickle.dumps(obj)
b'\x80\x03c__main__\nMyClass\nq\x00...

When running the original MySQL setup, the error is instead:
>>> pickle.dumps(obj)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't pickle module objects

-- 
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 view this discussion on the web visit 
https://groups.google.com/d/msgid/sqlalchemy/cf86dd7b-858e-47cf-adef-e322fc24d783%40googlegroups.com.

Reply via email to