38

While trying to do the following operation:

for line in blines:
    line.account = get_customer(line.AccountCode)

I am getting an error while trying to assign a value to line.account:

DetachedInstanceError: Parent instance <SunLedgerA at 0x16eda4d0> is not bound to a       Session; lazy load operation of attribute 'account' cannot proceed

Am I doing something wrong??

doru
  • 9,022
  • 2
  • 33
  • 43
maruthi reddy
  • 613
  • 2
  • 8
  • 11

6 Answers6

34

"detached" means you're dealing with an ORM object that is not associated with a Session. The Session is the gateway to the relational database, so anytime you refer to attributes on the mapped object, the ORM will sometimes need to go back to the database to get the current value of that attribute. In general, you should only work with "attached" objects - "detached" is a temporary state used for caching and for moving objects between sessions.

See Quickie Intro to Object States, then probably read the rest of that document too ;).

Zitrax
  • 19,036
  • 20
  • 88
  • 110
zzzeek
  • 72,307
  • 23
  • 193
  • 185
  • 6
    As it turns out, the case when I got `DetachedInstanceError` was because session cannot be used in a concurrent fashion - that exception got tripped up by the fact that a celery task was called synchronously. – Devy Jan 08 '16 at 20:36
  • So @Devy what was your solution? I think i'm generating a similar problem with a background thread for a super long running piece of code. – mcpeterson Apr 26 '17 at 19:31
  • 2
    @mcpeterson My solution was to avoid using `session` in a concurrent fashion as it's not thread-safe. See http://docs.sqlalchemy.org/en/latest/orm/session_basics.html#is-the-session-thread-safe – Devy Apr 28 '17 at 16:34
14

I had the same problem with Celery. Adding lazy='subquery' to relationship solved my problem.

Farshid Ashouri
  • 16,143
  • 7
  • 52
  • 66
  • 8
    Can you please say more about this? Did you add that to the ` = .relationship(,back_populates="")` definition of your relationship? – Zach Siegel Aug 07 '17 at 08:12
  • 1
    Need more info, I am facing same problem in Celery. I created create_scoped_session but getting above problem in another Model object class when it access (this.column) – ahmadalibaloch Jul 15 '19 at 16:03
4

I encountered this type of DetachedInstanceError when I prematurely close the query session (that is, having code to deal with those SQLAlchemy model objects AFTER the session is closed). So that's one clue to double check no session closure until you absolutely don't need interact with model objects, I.E. some Lazy Loaded model attributes etc.

Devy
  • 9,655
  • 8
  • 61
  • 59
3

I had the same problem when unittesting.

The solution was to call everything within the "with" context:

with self.app.test_client() as c:
    res = c.post('my_url/test', data=XYZ, content_type='application/json')

Then it worked.

Adding the lazy attribute didn't work for me.

Timo
  • 51
  • 4
2

To access the attribute connected to other table, you should call it within session.

@contextmanager
def get_db_session(engine):
    SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)    
    db = SessionLocal()
    try:
        yield db
    except Exception:
        db.rollback()
        raise
    finally:
        db.close()

with get_db_session(engine) as sess:
    data = sess.query(Groups).all()
    # `group_users` is connected to other table
    print([x.group_users for x in data])  # sucess
print([x.group_users for x in data])  # fail
林邦齊
  • 21
  • 3
0

I just saw this myself, and thought I'd share what fixed it for me.

I wanted to iterate over a dict and change it at the same time, so I copy.deepcopy'd dict_.items() to avoid the common problem that comes from iterating and mutating a dict at the same time.

But I had copy.deepcopy'd a list containing some SQLAlchemy ORM objects, which separated the copies from their session, even though they seemed to be suitable objects in other ways.

Fix: Don't copy.deepcopy them. I just did list(dict_.items()) instead of copy.deepcopy(dict_.items()). The list() makes it eager.

Amir reza Riahi
  • 1,540
  • 2
  • 8
  • 34
dstromberg
  • 6,954
  • 1
  • 26
  • 27