Hello Michael
Sorry for the late reply.
So, below is my full stack trace:
Traceback (most recent call last):
File
"/Users/foobar/Developpement/nursery_project/applications/nurserydb/utils_scripts/test2.py",
line 64, in <module>
plant.taxon = taxon_new # triggers an IntegrityError
File "build/bdist.macosx-10.6-x86_64/egg/sqlalchemy/orm/attributes.py",
line 303, in __set__
File "build/bdist.macosx-10.6-x86_64/egg/sqlalchemy/orm/attributes.py",
line 804, in set
File "build/bdist.macosx-10.6-x86_64/egg/sqlalchemy/orm/attributes.py",
line 824, in fire_replace_event
File "build/bdist.macosx-10.6-x86_64/egg/sqlalchemy/orm/attributes.py",
line 1131, in emit_backref_from_scalar_set_event
File "build/bdist.macosx-10.6-x86_64/egg/sqlalchemy/orm/attributes.py",
line 638, in append
File "build/bdist.macosx-10.6-x86_64/egg/sqlalchemy/orm/attributes.py",
line 788, in set
File "build/bdist.macosx-10.6-x86_64/egg/sqlalchemy/orm/attributes.py",
line 613, in get
File "build/bdist.macosx-10.6-x86_64/egg/sqlalchemy/orm/strategies.py",
line 524, in _load_for_state
File "build/bdist.macosx-10.6-x86_64/egg/sqlalchemy/orm/strategies.py",
line 585, in _emit_lazyload
File "build/bdist.macosx-10.6-x86_64/egg/sqlalchemy/orm/query.py", line
2104, in all
File "build/bdist.macosx-10.6-x86_64/egg/sqlalchemy/orm/query.py", line
2215, in __iter__
File "build/bdist.macosx-10.6-x86_64/egg/sqlalchemy/orm/session.py", line
1138, in _autoflush
File "build/bdist.macosx-10.6-x86_64/egg/sqlalchemy/orm/session.py", line
1817, in flush
File "build/bdist.macosx-10.6-x86_64/egg/sqlalchemy/orm/session.py", line
1935, in _flush
File "build/bdist.macosx-10.6-x86_64/egg/sqlalchemy/util/langhelpers.py",
line 58, in __exit__
File "build/bdist.macosx-10.6-x86_64/egg/sqlalchemy/orm/session.py", line
1899, in _flush
File "build/bdist.macosx-10.6-x86_64/egg/sqlalchemy/orm/unitofwork.py",
line 372, in execute
File "build/bdist.macosx-10.6-x86_64/egg/sqlalchemy/orm/unitofwork.py",
line 525, in execute
File "build/bdist.macosx-10.6-x86_64/egg/sqlalchemy/orm/persistence.py",
line 58, in save_obj
File "build/bdist.macosx-10.6-x86_64/egg/sqlalchemy/orm/persistence.py",
line 491, in _emit_update_statements
File "build/bdist.macosx-10.6-x86_64/egg/sqlalchemy/engine/base.py", line
662, in execute
File "build/bdist.macosx-10.6-x86_64/egg/sqlalchemy/engine/base.py", line
761, in _execute_clauseelement
File "build/bdist.macosx-10.6-x86_64/egg/sqlalchemy/engine/base.py", line
874, in _execute_context
File "build/bdist.macosx-10.6-x86_64/egg/sqlalchemy/engine/base.py", line
1024, in _handle_dbapi_exception
File "build/bdist.macosx-10.6-x86_64/egg/sqlalchemy/util/compat.py", line
163, in raise_from_cause
File "build/bdist.macosx-10.6-x86_64/egg/sqlalchemy/engine/base.py", line
867, in _execute_context
File "build/bdist.macosx-10.6-x86_64/egg/sqlalchemy/engine/default.py",
line 324, in do_execute
sqlalchemy.exc.IntegrityError: (IntegrityError) null value in column
"taxon_id" violates not-null constraint
'UPDATE botany.plant SET taxon_id=%(taxon_id)s WHERE botany.plant.id =
%(botany_plant_id)s' {'taxon_id': None, 'botany_plant_id': -2147483643}
I guess it correspond to what you mentioned.
I have tried to use what you told :
with session.no_autoflush:
plant.taxon = taxon_new
...and it works fine.
But the thing is that I don't need it in case I comment out the line :
taxon_old = session.query(Taxon).get(-2147483634)
and the test:
print plant.taxon is taxon_old # True
It seems that the problems occurs because the taxon_old is already loaded
in the identity map so the taxon.plant of the already loaded taxon_old must
be changed to reflect the change made by the below line:
plant.taxon = taxon_new # triggers an IntegrityError
This is totally understandable.
However, I get confused by SQLAlchemy trying to set taxon_id to NULL or
even worse, by SQLAlchemy trying to delete the plant if I configure a
"cascade='all, delete-orphan'" on the backref side of the relationship. I
understand that it might not be "easy" for SQLAlchemy to understand what I
am trying to do, but the fact that I end up with two different results
depending on whether or not taxon_old is previously loaded is confusing.
Anyway, SQLAlchemy is still very impressive and never ceases to amaze me
every day I discover new features. Thank you for that.
Le mardi 4 juin 2013 16:38:30 UTC+2, Etienne Rouxel a écrit :
>
> Hello
>
> I would like to change a value in a one-to-one relationship but I cannot
> because of some actions that SQLAlchemy try to do, and I don't know why.
> Here is my simplified code :
>
> # -*- coding: utf-8 -*-
> from sqlalchemy import *
> from sqlalchemy.orm import *
> from sqlalchemy.ext.declarative import declarative_base
>
> Base = declarative_base()
>
> _taxon_table = Table('taxon', Base.metadata,
> Column('id', Integer, primary_key=True),
> schema='botany'
> )
>
> _foliagetype_table = Table('foliagetype', Base.metadata,
> Column('id', Integer, primary_key=True),
> schema='botany'
> )
>
> _plant_table = Table('plant', Base.metadata,
> Column('id', Integer, primary_key=True),
> Column('taxon_id', Integer),
> Column('foliagetype_id', Integer),
> ForeignKeyConstraint(['taxon_id'], ['botany.taxon.id']),
> ForeignKeyConstraint(['foliagetype_id'], ['botany.foliagetype.id
> ']),
> schema='botany'
> )
>
> class Taxon(Base):
> __table__ = _taxon_table
>
> class Foliagetype(Base):
> __table__ = _foliagetype_table
>
> class Plant(Base):
> __table__ = _plant_table
>
> taxon = relationship('Taxon',
> backref=backref('plant',
> uselist=False))
>
> foliagetype = relationship('Foliagetype',
> backref=backref('plants'))
>
> if __name__ == '__main__':
>
> engine = create_engine('postgresql://xxx@localhost:5432/xxx')
> Session = sessionmaker(bind=engine)
> session = Session()
>
> taxon_old = session.query(Taxon).get(-2147483634)
> taxon_new = session.query(Taxon).get(-2147483645)
>
> foliagetype_old = session.query(Foliagetype).get(-2147483646)
> foliagetype_new = session.query(Foliagetype).get(-2147483645)
>
> plant = session.query(Plant).get(-2147483643)
>
> print "-- Change foliagetype --"
> print plant.foliagetype is foliagetype_old # True
> plant.foliagetype = foliagetype_new
> print plant.foliagetype is foliagetype_new # True
>
> print "-- Change taxon --"
> print plant.taxon is taxon_old # True
> plant.taxon = taxon_new # triggers an IntegrityError
> print plant.taxon is taxon_new
>
>
>
> So a plant must have one and exactly one foliagetype and same with the the
> taxon : a plant must have one and exactly one taxon. The difference
> however, is that a foliagetype can be linked to several plants while a
> taxon can only be linked to a single plant.
>
> In my code above the change of foliagetype leads SQLAlchemy to output the
> expected SQL :
>
> UPDATE botany.plant SET foliagetype_id= -2147483645 WHERE botany.plant.id=
> -2147483643
>
>
> However, concerning the change of taxon, I don't understand what
> SQLAlchemy is doing. It first output:
>
> SELECT botany.plant.id AS botany_plant_id, botany.plant.taxon_id AS
> botany_plant_taxon_id, botany.plant.foliagetype_id AS
> botany_plant_foliagetype_id
> FROM botany.plant
> WHERE -2147483634 = botany.plant.taxon_id
>
>
> and then:
>
> UPDATE botany.plant SET taxon_id=NULL WHERE botany.plant.id = -2147483643
>
>
> which fails because NULL is not allowed for taxon_id. So, why do not
> SQLAlchemy just output this instead?
>
> UPDATE botany.plant SET taxon_id= -2147483645 WHERE botany.plant.id =
> -2147483643
>
>
>
--
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 [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/sqlalchemy?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.