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+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to