Error: When initializing mapper, expression failed to locate a name ("name is not defined").

6,765 views
Skip to first unread message

Tim Black

unread,
Aug 13, 2011, 4:01:43 PM8/13/11
to sqlalchemy
I'm getting the following error message running TurboGears 2.1.1 and SQLAlchemy 0.7.2, using TurboGears' multiple database setup described here:  http://turbogears.org/2.1/docs/main/MultipleDatabases.html.  Both databases are SQLite3 databases; one is the main database for the TurboGears app; the other is a Trac 0.11.4 database.  Can you help me figure out what I'm doing wrong?

InvalidRequestError: One or more mappers failed to initialize - can't proceed with initialization of other mappers. Original exception was: When initializing mapper Mapper|WorkDone|work_done, expression 'ticket' failed to locate a name ("name 'ticket' is not defined"). If this is a class name, consider adding this relationship() to the <class 'projects.model.main.WorkDone'> class after both dependent classes have been defined.

In my model/__init__.py, I import the model objects in this order (the traceback makes me think this is the right place to work--either I need to change the import order here, or add the relation() after the final import, but neither of these solutions have seemed to work):

from projects.model.trac import ticket
from projects.model.main import WorkDone
# To follow the error message's instructions, I tried adding a relation() here, but it generated a further error; maybe I didn't get the syntax right.

If I should add a relation immediately above, what is the right syntax?  But this also seems like a hack to add a relation() here; if I have to add a relation() here, does that mean my current code has circular dependencies?

Here's the rest of my relevant code (with important items in bold):

In projects/model/trac.py, I have the following (I haven't put any relation() or backref in this table definition, because this file is borrowed mostly unmodified from Trac's experimental SQLAlchemy model object code, and I want to keep it that way):

# begin Tim's modification
from projects.model import metadata2
metadata = metadata2
# end Tim's modification

# Ticket system
ticket = Table('ticket', metadata,
        Column('id', Integer, primary_key=True),
        Column('type', Text),
        Column('time', Integer, index=True),
        Column('changetime', Integer),
        Column('component', Text),
        Column('severity', Text),
        Column('priority', Text),
        Column('owner', Text),
        Column('reporter', Text),
        Column('cc', Text),
        Column('version', Text),
        Column('milestone', Text),
        Column('status', Text, index=True),
        Column('resolution', Text),
        Column('summary', Text),
        Column('description', Text),
        Column('keywords', Text))

In projects/model/main.py, I have:

class WorkDone(DeclarativeBase):
    __tablename__ = 'work_done'
    id = Column(Integer, primary_key=True)
    ticket_id = Column('ticket_id', Integer, ForeignKey('ticket.id')) # many-to-one
    ticket = relation('ticket', primaryjoin=ticket_id == 'ticket.id')

The traceback is generated by line 105 of my projects/controllers/root.py, which reads:

for p in DBSession.query(model.Project):

projects/model/main.py contains:

class Project(DeclarativeBase):
    id = Column(Integer, primary_key=True)
    workDone = relation('WorkDone') # one-to-many

...and that same class contains several properties that reference the WorkDone model object like this one does:

    @property
    def totalHours(self):
        return sum([w.elapsed for w in self.workDone])

Thank you for any help you can offer!

Tim

Michael Bayer

unread,
Aug 13, 2011, 6:43:44 PM8/13/11
to sqlal...@googlegroups.com
Do you have the "metadata" in Table shared with the metadata used by DeclarativeBase ?  Otherwise the name "ticket" won't be located via the ForeignKey you're declaring in the WorkDone class.   As an alternative you could link the ForeignKey to "ticket.c.id" but would be easier if you could use the "DeclarativeBase.metadata" for all table definitions.

Otherwise yes you need to ensure that trac.py and main.py have both been imported before you attempt to use the mappings.


--
You received this message because you are subscribed to the Google Groups "sqlalchemy" group.
To post to this group, send email to sqlal...@googlegroups.com.
To unsubscribe from this group, send email to sqlalchemy+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/sqlalchemy?hl=en.

Tim Black

unread,
Aug 17, 2011, 12:03:35 PM8/17/11
to sqlal...@googlegroups.com
Michael,

Thank you for your advice.  It took me a while to figure out how to follow it, but in the end it worked.  After reading the (excellent) SQLAlchemy docs a lot more, and some other discussion group posts that explained the function of the engine, metadata, and session objects, and reassured me it was possible to have two databases, two engines, one metadata, and one session, I figured out how to modify the TurboGears 2.1 multiple database setup (linked below) to work that way.  For others' reference, the essential modifications to TurboGears' multiple databases setup (not including the necessary changes to imports in model sub-modules) for making a relation between two databases are below.

Tim

--- /tmp/bzr-diff-EWNNdM/old/projects/projects/model/__init__.py
+++ /tmp/bzr-diff-EWNNdM/new/projects/projects/model/__init__.py
@@ -12,16 +12,16 @@

-maker2 = sessionmaker(autoflush=True, autocommit=False,
-                     extension=ZopeTransactionExtension())
-DBSession2 = scoped_session(maker2)
+#maker2 = sessionmaker(autoflush=True, autocommit=False,
+#                     extension=ZopeTransactionExtension())
+#DBSession2 = scoped_session(maker2)
 
-DeclarativeBase2 = declarative_base()
+#DeclarativeBase2 = declarative_base()
 
@@ -35,11 +35,11 @@
 
-metadata2 = DeclarativeBase2.metadata
+#metadata2 = DeclarativeBase2.metadata
 
@@ -49,10 +49,12 @@
 
def init_model(engine1, engine2):
    """Call me before using any of the tables or classes in the model."""

#    DBSession.configure(bind=engine)
    DBSession.configure(bind=engine1)
-    DBSession2.configure(bind=engine2)
+    DBSession.configure(binds={Ticket:engine2})
+    #DBSession2.configure(bind=engine2)
 
+    # TODO: Is this statement necessary, since the engines are already configured in the session?
     metadata.bind = engine1
-    metadata2.bind = engine2
+    #metadata2.bind = engine2

Michael Bayer

unread,
Aug 17, 2011, 12:16:30 PM8/17/11
to sqlal...@googlegroups.com
Great, and to answer your TODO no, metadata.bind = engine is not necessary and I try to discourage people from using that activity by default (but the plethora of Turbogears and Pylons tutorials that keep using it makes this task more or less impossible).    A discussion of that feature specifically is at http://www.sqlalchemy.org/docs/core/schema.html#binding-metadata-to-an-engine-or-connection .


Tim Black

unread,
Aug 17, 2011, 1:06:51 PM8/17/11
to sqlal...@googlegroups.com
On 08/17/2011 11:16 AM, Michael Bayer wrote:
Great, and to answer your TODO no, metadata.bind = engine is not necessary and I try to discourage people from using that activity by default (but the plethora of Turbogears and Pylons tutorials that keep using it makes this task more or less impossible).    A discussion of that feature specifically is at http://www.sqlalchemy.org/docs/core/schema.html#binding-metadata-to-an-engine-or-connection .
I read that, and because the docs indicate removing metadata.bind = engine1 would make me lose "implicit" execution (unless it's provided by some other means in TurboGears' config), I worried that might break TurboGears' transaction management or query execution configuration somehow--is that possible?  I only ask because TurboGears' creators may have some question like this in mind--maybe you can see better than I whether this indicates a way the docs can be improved.  After reading more of the SQLAlchemy docs, it appears to me the answer to that question is "No," because "implicit" execution and transaction management are two very different and unrelated things in SQLAlchemy, and that whatever "implicit" execution TurboGears may need is probably provided by binding the engine to the session.  And, so far, my code works without the metadata.bind = engine1 statement.

I'll mention one more thing where maybe the docs should be improved--when I created the lines below, I had to read through SQLAlchemy's code to try to figure out whether I could use both bind (singular) and binds (plural) simultaneously on the same object.  Initially the docs made me feel uncomfortable using both bind and binds; it seemed like if bind stores one connectable in the session, binds might replace that one connectable with a list of connectables, so maybe I could only use one and not both (bind and binds).  But, through trial and error (which docs need never attempt to completely replace), using both works!


    DBSession.configure(bind=engine1)
-    DBSession2.configure(bind=engine2)
+    DBSession.configure(binds={Ticket:engine2})

But here is how the docs could be made more clear - simply state 1. whether it is allowable to use bind and binds simultaneously on the same object, and (if 2. is the case) 2. that the engine specified in bind will be used for all classes, mappers, and tables except where another engine is specified by binds.  For brevity's sake, I note that 1. is implicit in 2.

Tim

Michael Bayer

unread,
Aug 17, 2011, 3:05:24 PM8/17/11
to sqlal...@googlegroups.com
On Aug 17, 2011, at 1:06 PM, Tim Black wrote:

On 08/17/2011 11:16 AM, Michael Bayer wrote:
Great, and to answer your TODO no, metadata.bind = engine is not necessary and I try to discourage people from using that activity by default (but the plethora of Turbogears and Pylons tutorials that keep using it makes this task more or less impossible).    A discussion of that feature specifically is at http://www.sqlalchemy.org/docs/core/schema.html#binding-metadata-to-an-engine-or-connection .
I read that, and because the docs indicate removing metadata.bind = engine1 would make me lose "implicit" execution (unless it's provided by some other means in TurboGears' config), I worried that might break TurboGears' transaction management or query execution configuration somehow--is that possible?  I only ask because TurboGears' creators may have some question like this in mind--maybe you can see better than I whether this indicates a way the docs can be improved.  After reading more of the SQLAlchemy docs, it appears to me the answer to that question is "No," because "implicit" execution and transaction management are two very different and unrelated things in SQLAlchemy, and that whatever "implicit" execution TurboGears may need is probably provided by binding the engine to the session.  And, so far, my code works without the metadata.bind = engine1 statement.

I'll mention one more thing where maybe the docs should be improved--when I created the lines below, I had to read through SQLAlchemy's code to try to figure out whether I could use both bind (singular) and binds (plural) simultaneously on the same object.  Initially the docs made me feel uncomfortable using both bind and binds; it seemed like if bind stores one connectable in the session, binds might replace that one connectable with a list of connectables, so maybe I could only use one and not both (bind and binds).  But, through trial and error (which docs need never attempt to completely replace), using both works!

    DBSession.configure(bind=engine1)
-    DBSession2.configure(bind=engine2)
+    DBSession.configure(binds={Ticket:engine2})

But here is how the docs could be made more clear - simply state 1. whether it is allowable to use bind and binds simultaneously on the same object, and (if 2. is the case) 2. that the engine specified in bind will be used for all classes, mappers, and tables except where another engine is specified by binds.  For brevity's sake, I note that 1. is implicit in 2.

well the docs so far are in http://www.sqlalchemy.org/docs/orm/session.html#sqlalchemy.orm.session.Session.get_bind and basically it will try to return the most specific "bind" first.   ie.....I was about to start typing the order here.   What's the use in that, when I can type it into the docs instead, so click over there to see the exact order of resolution.




Tim Black

unread,
Aug 17, 2011, 4:30:03 PM8/17/11
to sqlal...@googlegroups.com
Aha...I had read that "order of resolution" section, but failed to
remember it when I needed it. That section is sufficient to answer my
concern above, and better than what I recommended!

Tim

Tim Black

unread,
Aug 17, 2011, 5:06:01 PM8/17/11
to sqlal...@googlegroups.com
On 08/17/2011 03:30 PM, Tim Black wrote:
> On 08/17/2011 02:05 PM, Michael Bayer wrote:
>> On Aug 17, 2011, at 1:06 PM, Tim Black wrote:
>> But here is how the docs could be made more clear - simply state 1.
>> whether it is allowable to use bind and binds simultaneously on the
>> same object, and (if 2. is the case) 2. that the engine specified in
>> bind will be used for all classes, mappers, and tables except where
>> another engine is specified by binds. For brevity's sake, I note that
>> 1. is implicit in 2.
>>
>> well the docs so far are
>> in http://www.sqlalchemy.org/docs/orm/session.html#sqlalchemy.orm.session.Session.get_bind
>> and basically it will try to return the most specific "bind" first.
>> ie.....I was about to start typing the order here. What's the use in
>> that, when I can type it into the docs instead, so click over there to
>> see the exact order of resolution.
> Aha...I had read that "order of resolution" section
Or so I thought...from Google's cache it looks like you did just type it
into the docs. I'll stop now... :)

Tim

Reply all
Reply to author
Forward
0 new messages