Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

sqlalchemy: ObjectdereferencedError

I have a flask app where I made a bunch of classes all with relationships to each other:

User Course Lecture Note Queue Asset

So I'm trying to make a new lecture and note, and I have a method defined for each thing.

create Note

def createPad(user,course,lecture):
    lecture.queues.first().users.append(user)
    # make new etherpad for user to wait in
    newNote = Note(dt) # init also creates a new pad at /p/groupID$noteID
    db.session.add(newNote)
    #db.session.commit()

    # add note to user, course, and lecture
    user.notes.append(newNote)
    course.notes.append(newNote)
    lecture.notes.append(newNote)
    db.session.commit()

return newNote

createLecture

def createLecture(user, course):
    # create new lecture
    now = datetime.now()
    dt = now.strftime("%Y-%m-%d-%H-%M")
    newLecture = Lecture(dt)
    db.session.add(newLecture)

    # add lecture to course, add new queue to lecture, add user to queue, add new user to lecture
    course.lectures.append(newLecture)
    newQueue = MatchQueue('neutral')
    db.session.add(newQueue)
    newLecture.users.append(user)


    # hook up the new queue to the user, lecture
    newQueue.users.append(user)
    newQueue.lecture = newLecture
    # put new lecture in correct course
    db.session.commit()

    newLecture.groupID = pad.createGroupIfNotExistsFor(newLecture.course.name+dt)['groupID']

    db.session.commit()
    return newLecture

which is all called from some controller logic

newlec = createLecture(user, courseobj)

# make new pad
newNote = createPad(user,courseobj,newlec)

# make lecture live
newLecture.live = True
db.session.commit()

redirect(somewhere)

This ends up throwing this error:

ObjectDereferencedError: Can't emit change event for attribute 'Queue.users' - parent object of type has been garbage collected.

At lecture.queues.first().users.append(user) in createPad.

I have no clue what this means. I think I'm lacking some fundamental knowledge of sqlalchemy here (I am a sqlalchemy noob). What's going on?

like image 391
Oliver Avatar asked Dec 15 '22 15:12

Oliver


1 Answers

lecture.queues.first().users.append(user)

it means:

the first() method hits the database and produces an object, I'm not following your mappings but I have a guess its a Queue object.

then, you access the "users" collection on Queue.

At this point, Python itself garbage collects Queue - it is not referenced anywhere, once "users" has been returned. This is how reference counting garbage collection works.

Then you attempt to append a "user" to "users". SQLAlchemy has to track the changes to all mapped attributes, if you were to say Queue.name = "some name", SQLAlchemy needs to register that with the parent Queue object so it knows to flush it. If you say Queue.users.append(someuser), same idea, it needs to register the change event with the parent.

SQLAlchemy can't do this, because the Queue is gone. Hence the message is raised. SQLAlchemy has a weakref to the parent here so it knows exactly what has happened (and we can't prevent it because people get very upset when we create unnecessary reference cycles in their object models).

The solution is very easy and also easier to read which is to assign the query result to a variable:

queue = lecture.queues.first()
queue.users.append(user)
like image 69
zzzeek Avatar answered Dec 21 '22 09:12

zzzeek