Thank you very much for your help. The post_update=True addition is what I
was missing.
The test case I was using was c1.created_by = c1 where created_by was
previously null.
(c1 having previously been saved and flushed) This seems to be the one
corner case where
post_update=True was necessary.
I have attached a script to demonstrate the problem I encountered. I don't
know if this is
expected or known behavior or not, but it was certainly confusing to me. I
would assume that saving
against a null vs non-null value should not result in different behavior.
Thank you again.
-brad
On May 28, 8:47 pm, Michael Bayer <[EMAIL PROTECTED]> wrote:
> Here's a script which exercises your mapping against 0.4 - the
> remote_side should be on the "many-to-one" side of "updated_by".
>
> Addtionally, depending on what kind of combinations of Contact objects
> you want to store, you might run into the scenario where ContactA
> references ContactB, and ContactB references ContactA. Theres no way
> to INSERT data like that without an UPDATE (assuming FK integrity and
> no sequences), so SQLA wants you to use a flag called "post_update" if
> that occurs. It needs the flag if the co-dependent items are being
> UPDATEd too since it works out dependencies for UPDATE and INSERTs in
> the same way...this is something that could perhaps be improved upon.
> So the second half of the script illustrates that mapping as an
> alternative.
>
> cyclical_contacts.py
> 3KDownload
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"sqlalchemy" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at
http://groups.google.com/group/sqlalchemy?hl=en
-~----------~----~----~----~------~----~------~--~---
from sqlalchemy import *
from sqlalchemy import exceptions
from sqlalchemy.orm import *
import logging
#logging.basicConfig()
#logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)
#logging.getLogger('sqlalchemy.orm.properties.PropertyLoader').setLevel(logging.INFO)
#logging.getLogger('sqlalchemy.orm.strategies.LazyLoader').setLevel(logging.INFO)
meta = MetaData(create_engine('sqlite://'))
contacts = Table('contacts', meta,
Column('id', Integer, primary_key=True),
Column('name', String(50)),
Column('created_by', Integer),
Column('updated_by', Integer),
ForeignKeyConstraint(['created_by'], ['contacts.id']),
ForeignKeyConstraint(['updated_by'], ['contacts.id'])
)
meta.create_all()
class Contact(object):
def __init__(self, name, created_by=None):
self.name = name
self.created_by = created_by
def __eq__(self, other):
return other.name == self.name
def __repr__(self):
return "Contact(created_by=%r, updated_by=%r)" % (self.created_by, self.updated_by)
# create a sample mapper with and without post_update=True
# updated_by uses post_update, created_by does not.
mapper(Contact, contacts, properties={
'_created_by': contacts.c.created_by,
'_updated_by': contacts.c.updated_by,
'created_by': relation(Contact, primaryjoin=contacts.c.created_by==contacts.c.id,
remote_side=[contacts.c.id],
),
'updated_by': relation(Contact, primaryjoin=contacts.c.updated_by==contacts.c.id,
remote_side=[contacts.c.id],
post_update=True,
)
})
sess = create_session()
# saving indivisually seems to be necessary to ensure ID ordering
c1 = Contact('c1')
sess.save(c1)
sess.flush()
c2 = Contact('c2')
sess.save(c2)
sess.flush()
# assert proper data
assert contacts.select().order_by(contacts.c.name).execute().fetchall() == [
(1, 'c1', None, None),
(2, 'c2', None, None),
]
sess.clear()
[c1, c2] = sess.query(Contact).order_by(Contact.name).all()
c1.created_by = c2
c1.updated_by = c2
sess.flush()
# saving against a null key with a non self referential key works as expected
assert contacts.select().order_by(contacts.c.name).execute().fetchall() == [
(1, 'c1', 2, 2),
(2, 'c2', None, None),
]
sess.clear()
[c1, c2] = sess.query(Contact).order_by(Contact.name).all()
c1.created_by = c1
c1.updated_by = c1
c2.created_by = c2
c2.updated_by = c2
sess.flush()
# updating a non-null key with a self referential key works as expected
# with and without post_update=True
#
# saving against a null key with a self referential key works only with
# post_update=True.
#
# This does not seem to be a true circular relationship as the resulting SQL
# can be one simple UPDATE.
#
# expected outcome (to me):
# c1 created_by=1, updated_by=1
# c2 created_by=2, updated_by=2
# actual outcome:
# c1 created_by=1, updated_by=1
# c2 created_by=None, updated_by=2
assert contacts.select().order_by(contacts.c.name).execute().fetchall() == [
(1, 'c1', 1, 1),
(2, 'c2', 2, 2),
]
sess.clear()