On 4/8/15 3:22 AM, Sebastian Eckweiler wrote:
Hi there -
I'm having trouble working with backrefs of detached objects.
I'm basically working with a extended version of the code below:
|
classConfig(Base):
__tablename__ ='config'
ID =Column('ID',Integer,primary_key=True)
name =Column('name',String)
last_modified
=Column('last_modified',DateTime,default=now,onupdate=now)
params=relationship('ConfigParam',backref='config',lazy=False)
classConfigParam(Base):
__tablename__ ='config_params'
ID =Column('ID',Integer,primary_key=True)
ConfigID=Column('ConfigID',Integer,ForeignKey('config.ID'),nullable=False)
key =Column('key',String)
value =Column('value',Float)
|
Now when I load a Config instance I'd have assumed that
Config.params[0].config should be populated - since the eager query
contains all necessary information.
A load that can be resolved to using the session's identity map instead
of SELECT is still a load nonetheless. You would need a lazy setting on
the backref as well. Since these are many-to-ones and the lazyload
pulls from identity map, I suggest "immediate".
Using these settings at the mapping level does mean however that if you
load a ConfigParam object by itself, it will, for a clean session,
immediately issue a second SELECT statement for its Config object.
Testing this with a sqlite engine using the following code:
|
s =Session()
c =Config(name='my_config')
c.params+=[ConfigParam(key='a',value=1),
ConfigParam(key='b',value=2)]
s.add(c)
s.commit()
s.close()
delc
# reload from new session:
logging.info('\n\n')
logging.info('Starting with new session:')
s =Session()
c =s.query(Config).filter(Config.name=='my_config').one()
s.close()
forp inc.params:
logging.info('Params:%s = %s'%(p.key,p.value))
forp inc.params:
logging.info('backref: config = %s'%p.config)
|
however produces (omitting the create_all) the following output:
INFO:sqlalchemy.engine.base.Engine:()
INFO:sqlalchemy.engine.base.Engine:COMMIT
INFO:sqlalchemy.engine.base.Engine:BEGIN (implicit)
INFO:sqlalchemy.engine.base.Engine:INSERT INTO config (name,
last_modified) VALUES (?, ?)
INFO:sqlalchemy.engine.base.Engine:('my_config', '2015-04-08
09:04:16.111000')
INFO:sqlalchemy.engine.base.Engine:INSERT INTO config_params
("ConfigID", "key", value) VALUES (?, ?, ?)
INFO:sqlalchemy.engine.base.Engine:(1, 'a', 1.0)
INFO:sqlalchemy.engine.base.Engine:INSERT INTO config_params
("ConfigID", "key", value) VALUES (?, ?, ?)
INFO:sqlalchemy.engine.base.Engine:(1, 'b', 2.0)
INFO:sqlalchemy.engine.base.Engine:COMMIT
INFO:root:
INFO:root:Starting with new session:
INFO:sqlalchemy.engine.base.Engine:BEGIN (implicit)
INFO:sqlalchemy.engine.base.Engine:SELECT config."ID" AS "config_ID",
config.name AS config_name, config.last_modified AS
config_last_modified, config_params_1."ID" AS "config_params_1_ID",
config_params_1."ConfigID" AS "config_params_1_ConfigID",
config_params_1."key" AS config_params_1_key, config_params_1.value AS
config_params_1_value
FROM config LEFT OUTER JOIN config_params AS config_params_1 ON
config."ID" = config_params_1."ConfigID"
WHERE config.name = ?
INFO:sqlalchemy.engine.base.Engine:('my_config',)
INFO:sqlalchemy.engine.base.Engine:ROLLBACK
INFO:root:Params:a = 1.0
INFO:root:Params:b = 2.0
Traceback (most recent call last):
File "D:/Assess/Kiln/assess/scratch/sqla_example.py", line 77, in
<module>
logging.info('backref: config = %s' % p.config)
File
"D:\Anaconda\envs\assess\lib\site-packages\sqlalchemy\orm\attributes.py",
line 239, in __get__
return self.impl.get(instance_state(instance), dict_)
File
"D:\Anaconda\envs\assess\lib\site-packages\sqlalchemy\orm\attributes.py",
line 591, in get
value = self.callable_(state, passive)
File
"D:\Anaconda\envs\assess\lib\site-packages\sqlalchemy\orm\strategies.py",
line 507, in _load_for_state
(orm_util.state_str(state), self.key)
sqlalchemy.orm.exc.DetachedInstanceError: Parent instance <ConfigParam
at 0x307d898> is not bound to a Session; lazy load operation of
attribute 'config' cannot proceed
I'm somewhat new to ORM's -
naively I had assumed the backref should/could be loaded at the same
time the parent is eagerly loaded, since all required information is
retrieved in the above query.
Is there anything I'm missing here?
ps: This is on sqlalchemy 0.9.8 - if that should matter.
--
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 [email protected]
<mailto:[email protected]>.
To post to this group, send email to [email protected]
<mailto:[email protected]>.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.
--
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 [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.