Hi,
It seems like configuration is attempted for all new mappers,
globally, whenever a query is done. So if library A and B both
use sqlalchemy, and A imports B before A's mappers can be
properly initialised (e.g. there is a
relationship("ClassnameAsString") call somewhere that can't be
resolved yet), and B does something to trigger mapper
configuration, then it will fail.
This occurs even if A and B make separate calls to
declarative_base(), even with explicitly different metadata and
bound engines.
Here's a boiled-down version of the problem that I've been
playing with, which shows that the relationship between Parent
and Child is configured when a query on Test is done - even
though it may be part of a different library and in a different
database:
|
from sqlalchemy import Column, Integer, Text, ForeignKey,
create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship
import traceback
Base1 = declarative_base()
class Test(Base1):
__tablename__ = "test"
id = Column(Integer, primary_key=True)
Base2 = declarative_base()
class Parent(Base2):
__tablename__ = "parent"
id = Column(Integer, primary_key=True)
def deferred_parent():
traceback.print_stack()
return Parent
class Child(Base2):
__tablename__ = "child"
id_parent = Column(Integer, ForeignKey(Parent.id),
primary_key=True)
name = Column(Text, primary_key=True)
parent = relationship(deferred_parent)
engine = create_engine('sqlite://')
Session = sessionmaker(bind=engine)
session = Session()
try:
session.query(Test).all()
except:
pass
|
...the important bit of the traceback being:
File
"R:\sw\external\20150407-0\python27\lib\site-packages\sqlalchemy-0.9.7-py2.7-win32.egg\sqlalchemy\orm\session.py",
line 1165, in query
return self._query_cls(entities, self, **kwargs)
File
"R:\sw\external\20150407-0\python27\lib\site-packages\sqlalchemy-0.9.7-py2.7-win32.egg\sqlalchemy\orm\query.py",
line 108, in __init__
self._set_entities(entities)
File
"R:\sw\external\20150407-0\python27\lib\site-packages\sqlalchemy-0.9.7-py2.7-win32.egg\sqlalchemy\orm\query.py",
line 118, in _set_entities
self._set_entity_selectables(self._entities)
File
"R:\sw\external\20150407-0\python27\lib\site-packages\sqlalchemy-0.9.7-py2.7-win32.egg\sqlalchemy\orm\query.py",
line 151, in _set_entity_selectables
ent.setup_entity(*d[entity])
File
"R:\sw\external\20150407-0\python27\lib\site-packages\sqlalchemy-0.9.7-py2.7-win32.egg\sqlalchemy\orm\query.py",
line 2997, in setup_entity
self._with_polymorphic = ext_info.with_polymorphic_mappers
File
"R:\sw\external\20150407-0\python27\lib\site-packages\sqlalchemy-0.9.7-py2.7-win32.egg\sqlalchemy\util\langhelpers.py",
line 726, in __get__
obj.__dict__[self.__name__] = result = self.fget(obj)
File
"R:\sw\external\20150407-0\python27\lib\site-packages\sqlalchemy-0.9.7-py2.7-win32.egg\sqlalchemy\orm\mapper.py",
line 1871, in _with_polymorphic_mappers
configure_mappers()
File
"R:\sw\external\20150407-0\python27\lib\site-packages\sqlalchemy-0.9.7-py2.7-win32.egg\sqlalchemy\orm\mapper.py",
line 2583, in configure_mappers
mapper._post_configure_properties()
File
"R:\sw\external\20150407-0\python27\lib\site-packages\sqlalchemy-0.9.7-py2.7-win32.egg\sqlalchemy\orm\mapper.py",
line 1688, in _post_configure_properties
prop.init()
File
"R:\sw\external\20150407-0\python27\lib\site-packages\sqlalchemy-0.9.7-py2.7-win32.egg\sqlalchemy\orm\interfaces.py",
line 144, in init
self.do_init()
File
"R:\sw\external\20150407-0\python27\lib\site-packages\sqlalchemy-0.9.7-py2.7-win32.egg\sqlalchemy\orm\relationships.py",
line 1549, in do_init
self._process_dependent_arguments()
File
"R:\sw\external\20150407-0\python27\lib\site-packages\sqlalchemy-0.9.7-py2.7-win32.egg\sqlalchemy\orm\relationships.py",
line 1605, in _process_dependent_arguments
self.target = self.mapper.mapped_table
File
"R:\sw\external\20150407-0\python27\lib\site-packages\sqlalchemy-0.9.7-py2.7-win32.egg\sqlalchemy\util\langhelpers.py",
line 726, in __get__
obj.__dict__[self.__name__] = result = self.fget(obj)
File
"R:\sw\external\20150407-0\python27\lib\site-packages\sqlalchemy-0.9.7-py2.7-win32.egg\sqlalchemy\orm\relationships.py",
line 1522, in mapper
argument = self.argument()
File
"user!winfis!sqlalchemy!query_triggers_relationship_config.py",
line 19, in deferred_parent
traceback.print_stack()
Is there some method that I've missed of delaying mapper
configuration? Aren't the only mappers than need to be set up
those that share metadata with entities in the query, or any
metadata bound to the engine that will be used?
Perhaps configure_mappers() could take an optional
metadata/engine and only set up mappers that are related to this?
As you can see, I'm doing this with 0.9.7 but looking at the
1.0.0 code I think I'd have the same problem.
If it helps, (and you're not already bored) here's our use-case:
We have one library that implements a PEP302 import hook, which
fetches python code from a database and compiles it. This is
managed by sqlalchemy.
Some of the code in the database also use sqlalchemy and define
other sets of ORM-mapped classes, completely unrelated to the
first set, and which relate to tables inreside in completely
different databases.
If a query needs to be executed to fetch and compile some code
while another set of classes are not ready to have their mappers
initialised then exceptions are raised.
Thanks,
Steve.
--
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+...@googlegroups.com <javascript:>.
To post to this group, send email to sqlal...@googlegroups.com
<javascript:>.
Visit this group at http://groups.google.com/group/sqlalchemy
<http://groups.google.com/group/sqlalchemy>.
For more options, visit https://groups.google.com/d/optout
<https://groups.google.com/d/optout>.