On Thu, Nov 19, 2020, at 6:15 PM, Vinit Shah wrote:
> I posted this on StackOverflow a few days ago, but I haven't been able to 
> figure this one out yet. The original post can be found here: StackOverflow: 
> object "is already present in this session" 
> <https://stackoverflow.com/questions/64897088/sqlalchemy-object-is-already-present-in-this-session-when-using-a-new-sessio>.
> 
> I'm seeing the below error:
> sqlalchemy.exc.InvalidRequestError: Can't attach instance <ObjectType at 
> 0x10592fe50>; another instance with key ( <class '__main__.ObjectType'>, 
> (1,), None) is already present in this session.
> 
> I'm seeing this issue when I try to instantiate a new object that has a 
> foreign key relationship with an existing object that was created in another 
> session.
> 
> This happens in a few different cases in my actual code, but in the provided 
> sample it occurs with the following steps:
> 1. Add a new object into a new session
> 2. Close session and remove() from scoped_session
> 3. Reference the object in two newly constructed ones via their relationship
> 4. Error appears on the second object


This can be worked around using the cascade_backrefs option which in 1.4 will 
default to the better setting.  you can see what's happening here in the stack 
trace where you will see some of this:

  File "/home/classic/dev/sqlalchemy/lib/sqlalchemy/orm/attributes.py", line 
1017, in set
    value = self.fire_replace_event(state, dict_, value, old, initiator)
  File "/home/classic/dev/sqlalchemy/lib/sqlalchemy/orm/attributes.py", line 
1039, in fire_replace_event
    value = fn(
  File "/home/classic/dev/sqlalchemy/lib/sqlalchemy/orm/attributes.py", line 
1472, in emit_backref_from_scalar_set_event
    child_impl.append(
  File "/home/classic/dev/sqlalchemy/lib/sqlalchemy/orm/attributes.py", line 
1244, in append
    collection.append_with_event(value, initiator)
  File "/home/classic/dev/sqlalchemy/lib/sqlalchemy/orm/collections.py", line 
654, in append_with_event


see the "backref" ?  your program seeks to add objects to the session using 
session.merge() exclusively.   but above you can see the offending object is 
being "added", that's wrong, it's being "added" due to an "append" to your 
collection, which is from a backref, all of that is what you don't want and 
it's because of "origin=tokyo, destinationInput=luggage" are reverse-cascading 
your object directly into the session including the "origin" which you'd prefer 
is merged.

this is all described at 
https://docs.sqlalchemy.org/en/13/orm/cascades.html#controlling-cascade-on-backrefs
 , however the example there assumes the relationship is configured on the 
one-to-many side.  so to set this for your example, it's a little more verbose:

class Gate(Thing):
    __tablename__ = "gates"
    id = Column(Integer, ForeignKey("things.id"), primary_key=True)

    originId = Column(Integer, ForeignKey("locations.id"))
    origin = relationship(
        Location,
        foreign_keys=[originId],
        backref=backref("originGates", cascade_backrefs=False),
        cascade_backrefs=False,
    )

    originInputId = Column(Integer, ForeignKey("locationinputs.id"))
    originInput = relationship(
        LocationInput,
        foreign_keys=[originInputId],
        backref=backref("originInputGates", cascade_backrefs=False),
        cascade_backrefs=False,
    )

    destinationId = Column(Integer, ForeignKey("locations.id"))
    destination = relationship(
        Location,
        foreign_keys=[destinationId],
        backref=backref("destinationGates", cascade_backrefs=False),
        cascade_backrefs=False,
    )

    destinationInputId = Column(Integer, ForeignKey("locationinputs.id"))
    destinationInput = relationship(
        LocationInput,
        foreign_keys=[destinationInputId],
        backref=backref("destinationInputGates", cascade_backrefs=False),
        cascade_backrefs=False,
    )


in 1.4, cascade_backrefs defaults to False so you won't need to do this anymore.










>     # typeDict just contains a pre-fetched ObjectTypes 
>     tokyo = Location(name="tokyo", objectType=typeDict['location'])
>     tokyo = write(tokyo)
> 
>     # If I clear out the current session here, the error will occur
>     scopedSessionFactory().close()
>     scopedSessionFactory.remove()
> 
>     westGate = Gate(name="westGate", destination=tokyo, 
> objectType=typeDict['gate'])
>     westGate = write(westGate)
> 
>     luggage = LocationInput(name="luggage", 
> objectType=typeDict['locationinput'])
>     luggage = write(luggage)
> 
>     # This is the line where the error occurs
>     eastGate = Gate(name="eastGate", origin=tokyo, destinationInput=luggage, 
> objectType=typeDict['gate'])
>     eastGate = write(eastGate) 
> 
> I'm not sure what exactly causes this or way. For this example, I could just 
> reuse the same session, but I'd like to be able to take an object from one 
> closed session and add as a relationship field to another.
> 
> Full code sample available here: 
> https://gist.github.com/funseiki/a73424bebfb0d809e6d934f699a725bf
> 

> -- 
> SQLAlchemy - 
> The Python SQL Toolkit and Object Relational Mapper
>  
> http://www.sqlalchemy.org/
>  
> To post example code, please provide an MCVE: Minimal, Complete, and 
> Verifiable Example. See http://stackoverflow.com/help/mcve for a full 
> description.
> --- 
> 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 view this discussion on the web visit 
> https://groups.google.com/d/msgid/sqlalchemy/4d01df93-16ed-45a4-82f3-de04a8e57bcbn%40googlegroups.com
>  
> <https://groups.google.com/d/msgid/sqlalchemy/4d01df93-16ed-45a4-82f3-de04a8e57bcbn%40googlegroups.com?utm_medium=email&utm_source=footer>.

-- 
SQLAlchemy - 
The Python SQL Toolkit and Object Relational Mapper

http://www.sqlalchemy.org/

To post example code, please provide an MCVE: Minimal, Complete, and Verifiable 
Example.  See  http://stackoverflow.com/help/mcve for a full description.
--- 
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 view this discussion on the web visit 
https://groups.google.com/d/msgid/sqlalchemy/a672073b-cb58-4334-aedb-7e5daac178a5%40www.fastmail.com.

Reply via email to