On Sep 21, 2010, at 11:51 PM, Michael Hipp wrote:

> On 9/21/2010 8:17 PM, Michael Bayer wrote:
>> It definitely does not attempt an INSERT if id_ is set to a non-None value, 
>> assuming that row already exists in the DB, without something else in your 
>> model/usage causing that to happen.    If id_ is None or the given id_ 
>> doesn't exist in the DB, you get an INSERT.   auct_id has no direct effect 
>> here.
> 
>> that also makes no sense since if you set "auct_id" manually, assuming 
>> old.auct_id is not None, it wouldn't be None in the UPDATE statement.
> 
> These behaviors (opposite what we expect) are what I'm indeed seeing.
> 
>> As usual, distilling down the behavior that appears wrong into a single file
> 
> Attached.
> 
> I'll be thrilled if you can figure out what really stupid thing I'm doing to 
> cause this.
> 
> As always, many thanks for your help.

So here, the value of None for car.auction, merges into the session which 
becomes a pending change.   The flush overwrites car.auct_id with None because 
car.auction has been set to None.

The merge() process takes everything that is present on the incoming object and 
assigns it to the object that's in the session.  So here when merge sets 
old.auction = None, this is the effect.

So you want to merge an object where every attribute is either exactly the 
value that you want it to be, or it is not loaded or assigned to in any way 
(i.e. not present in __dict__).   If you pop "auction" from __dict__ before the 
merge, or just don't assign to "auction" in the contructor of Car and also dont 
issue a "print car.auction" later on, the program succeeds.

So for example, this works:

new = Car()  
new.id_ = old.id_ 
new.lane = old.lane  
new = sess.merge(new)
sess.commit()

if you took the None assignments out of the constructor, all you need is "id_" 
and leave everything else untouched, and it succeeds.

So the other thing, with the INSERT, you need to look at stack traces when 
these things happen:

File "empty.py", line 72, in <module>
new = sess.merge(new)
File "sqlalchemy/orm/session.py", line 1165, in merge
self._autoflush()
File "sqlalchemy/orm/session.py", line 863, in _autoflush

anytime you see _autoflush in a stack trace, that means that some state is in 
the session that you don't want it to be, and _autoflush is trying to push it 
out before its ready.   That a "car" row is being INSERTed during autoflush, 
means that a Car object has been added to the session.   The first thing you do 
is then ask "new in sess" to see if that's the case.

Here, the issue is that you're mixing the usage of merge() with the usage of 
objects that are already in the session.   "new" is added to the session via 
cascade:

new = Car()
new.id_ = old.id_
new.lane = old.lane
new.auct_id = old.auct_id
new.auction = old.auction
assert new in sess   # passes

The ways to get around that effect are:

- pass "cascade=None" to your 'cars' backref - this means, when you set 
somecar.auction = someauction, someauction is already in the session, 'somecar' 
doesn't get added automatically.   cascade also affects what merge() does along 
relationships so when changing this make sure it has the cascades that you 
still want.
- expunge "new" before you merge() it, but that's kind of messy.
- don't set any relationships that are going to cascade it into the session 

I think the general rule is to compose the object for merge() carefully so that 
it only contains what state you want to be merged.   "None" counts as state.

I definitely want to add a note about what "the state of the given instance is 
copied" means, regarding things in __dict__.





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

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

Reply via email to