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 sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to