Can anyone help with these questions? Nothing urgent, I'm just
interested in understanding more about SQLAlchemy.

On 16 February 2017 at 04:07, Zsolt Ero <zsolt....@gmail.com> wrote:
> I'm starting to understand it! Just a few very quick questions:
>
> 1. Is it a good strategy to always use onupdate='CASCADE',
> ondelete='CASCADE' for all foreign keys if the db supports it (in my
> case Postgres 9.5 + psycopg2)?
>
> 2. If I'd like to use it on an already populated db, can I "alter
> table" to use these CASCADE options (for example in an alembic
> migration)?
>
> 3. For delete, is this simply an optimisation step and the default
> behaviour is also perfectly fine?
>
> 4. Simply adding the above parameters is not automatically changing
> SQLAlchemy's delete strategy, I also need to tell it to use passive
> deletes. Do I also need the cascade="all, delete-orphan" option like
> in the docs?
>
> Zsolt
>
>
> On 15 February 2017 at 17:25, mike bayer <mike...@zzzcomputing.com> wrote:
>>
>>
>> On 02/15/2017 09:45 AM, Zsolt Ero wrote:
>>>
>>> 4. An interesting thing is that SQLAlchemy does 3 select calls in the
>>> delete case, even if 1 would be enough. Can be seen in the logs.
>>
>>
>> the ORM only deletes rows one at a time based on primary key match.   So if
>> you have a relationship() that is configured to cascade deletes, and you
>> have not instructed the system that "ON DELETE CASCADE" will take care of
>> those collections, it will need to ensure these collections are present in
>> memory (e.g. the SELECT) and target each row for deletion individually.  You
>> see only one DELETE statement but you'll note it has multiple parameter sets
>> to indicate every row being deleted. Background on how to optimize this is
>> at
>> http://docs.sqlalchemy.org/en/latest/orm/collections.html#using-passive-deletes
>> .
>>
>> In this specific case there seem to be two SELECT statements but that
>> appears to be because of the awkward production of a new object that has the
>> same primary key as another object.   In the logs you'll see an UPDATE but
>> this is actually a special case "hack"; normally, we'd see a DELETE of the
>> old row and an INSERT of the new one, however the unit of work does not
>> support this process.   There is an option to allow it to work this way in
>> specific cases, although this feature is not present in SQLAlchemy at this
>> time.   In the absence of that feature, the behavior is that if the same
>> primary key is present on one object being deleted and another one being
>> added in the same flush, they are rolled into an UPDATE.   Then the
>> collection is being deleted and re-added again too, so this is a bit of a
>> crazy example; using a straight UPDATE with correct cascade rules is
>> obviously much more efficient.
>>
>>
>>
>>>
>>> Zsolt
>>>
>>>
>>>
>>>
>>>
>>> On 15 February 2017 at 04:17, mike bayer <mike...@zzzcomputing.com> wrote:
>>>>
>>>>
>>>>
>>>> On 02/14/2017 08:15 PM, Zsolt Ero wrote:
>>>>>
>>>>>
>>>>> I would like to change a primary key's value, to be deterministic, based
>>>>> on a multi-to-multi relation. Thus I'm populating the tables with a
>>>>> temporary ids (just random strings), then calculating the right, unique
>>>>> id, and changing it afterwards.
>>>>
>>>>
>>>>
>>>> the examples seem to move "transaction.manager" around, which we assume
>>>> is
>>>> the Zope transaction manager and that by using the context manager the
>>>> Session.commit() method is ultimately called, which raises this error.
>>>> One
>>>> guess is that in the second two examples, the Session is not actually
>>>> getting committed, because no invocation of "dbsession" is present within
>>>> the "with transaction.manager" block and I have a vague recollection that
>>>> zope.transaction might work this way.  Another guess is that in the
>>>> second
>>>> two examples, maybe you already changed the data in the DB and the
>>>> operation
>>>> you're doing has no net change to the rows.
>>>>
>>>> In any case, all three examples you should echo the SQL emitted so you
>>>> can
>>>> see what it's doing.   Setting up the onupdate="CASCADE" should fix this
>>>> problem.  As to why that didn't work from you, keep in mind that is a
>>>> CREATE
>>>> TABLE directive so if you just changed it in your model and didn't
>>>> recreate
>>>> the tables, or at least recreate the foreign key constraints using ALTER
>>>> to
>>>> drop and create them again with the CASCADE rule set up; this is a server
>>>> side rule.
>>>>
>>>> Here's the MCVE to demonstrate:
>>>>
>>>> from sqlalchemy import *
>>>> from sqlalchemy.orm import *
>>>> from sqlalchemy.ext.declarative import declarative_base
>>>> import md5 as _md5
>>>> import random
>>>> import string
>>>>
>>>>
>>>> def md5(text):
>>>>     return str(_md5.md5(text))
>>>>
>>>>
>>>> def random_string(num):
>>>>     return ''.join(random.choice(
>>>>         string.ascii_uppercase + string.digits) for _ in range(num))
>>>>
>>>> Base = declarative_base()
>>>>
>>>>
>>>> class Image(Base):
>>>>     __tablename__ = 'images'
>>>>     id = Column(String, primary_key=True, default=lambda:
>>>> random_string(16))
>>>>     collections = relationship(
>>>>         'Collection', secondary='collections_images',
>>>> back_populates='images')
>>>>     date_created = Column(DateTime, default=func.now())
>>>>
>>>>
>>>> class Collection(Base):
>>>>     __tablename__ = 'collections'
>>>>     id = Column(String, primary_key=True, default=lambda:
>>>> random_string(16))
>>>>     name = Column(String)
>>>>     images = relationship(
>>>>         'Image', secondary='collections_images',
>>>>         back_populates='collections',
>>>> order_by='desc(Image.date_created)',
>>>>         lazy='dynamic')
>>>>
>>>>
>>>> collections_images = Table(
>>>>     'collections_images', Base.metadata,
>>>>     Column('collection_id',
>>>>            ForeignKey('collections.id', onupdate="CASCADE"),
>>>>            primary_key=True),
>>>>     Column('image_id', ForeignKey('images.id'), primary_key=True)
>>>> )
>>>>
>>>> e = create_engine("postgresql://scott:tiger@localhost/test", echo=True)
>>>>
>>>> Base.metadata.drop_all(e)
>>>> Base.metadata.create_all(e)
>>>>
>>>> s = Session(e)
>>>>
>>>> with s.transaction:
>>>>     s.add(Collection(name='c1', images=[Image(), Image(), Image()]))
>>>>
>>>> with s.transaction:
>>>>     collections = s.query(Collection).all()
>>>>
>>>>     for collection in collections:
>>>>         image_ids = [i.id for i in collection.images.all()]
>>>>         image_ids_string = ','.join(sorted(image_ids)) + collection.name
>>>>         collection.id = md5(image_ids_string)[:16]
>>>>
>>>>
>>>>
>>>>
>>>>>
>>>>> I have the following models:
>>>>>
>>>>> |classImage(Base):id
>>>>>
>>>>>
>>>>> =Column(String,primary_key=True,default=lambda:random_string(16))collections
>>>>>
>>>>>
>>>>> =relationship('Collection',secondary='collections_images',back_populates='images')classCollection(Base):id
>>>>> =Column(String,primary_key=True,default=lambda:random_string(16))images
>>>>>
>>>>>
>>>>> =relationship('Image',secondary='collections_images',back_populates='collections',order_by='desc(Image.date_created)',lazy='dynamic')Table('collections_images',Base.metadata,Column('collection_id',ForeignKey('collections.id'),primary_key=True),Column('image_id',ForeignKey('images.id'),primary_key=True))|
>>>>>
>>>>> My problem is the following:
>>>>>
>>>>>  1.
>>>>>
>>>>>     Out of the 3 examples below, only one triggers an integrity
>>>>>     exception, the other two does not.
>>>>>
>>>>>     Why?
>>>>>
>>>>>     In all three I'm trying to write to this primary key, which is
>>>>>     referenced, thus should produce an exception. Yet, in 2. and 3. it
>>>>>     seem nothing is happening when |collection.id =| is set. When I
>>>>>     debug via SQL queries it shows absolutely nothing called for
>>>>>     the |collection.id =| line.
>>>>>
>>>>>  2.
>>>>>
>>>>>     How can I solve this problem? I mean how can I change a primary
>>>>>     key's value which is also used in a multi-to-multi relation?
>>>>>
>>>>> The DB is PostgreSQL 9.5 with psycopg2.
>>>>>
>>>>> The examples are:
>>>>>
>>>>>
>>>>> A. triggers exception:
>>>>>
>>>>> |withtransaction.manager:collections
>>>>> =dbsession.query(Collection).all()forcollection incollections:image_ids
>>>>> =[i.id fori incollection.images.all()]image_ids_string
>>>>> =','.join(sorted(image_ids))+collection.name collection.id
>>>>> =md5(image_ids_string)[:16]|
>>>>>
>>>>>
>>>>> B. does not trigger exception
>>>>>
>>>>> |collections =dbsession.query(Collection).all()# ^ and v only these two
>>>>> lines are swapped withtransaction.manager:forcollection
>>>>> incollections:image_ids =[i.id fori
>>>>> incollection.images.all()]image_ids_string
>>>>> =','.join(sorted(image_ids))+collection.name collection.id
>>>>> =md5(image_ids_string)[:16]|
>>>>>
>>>>>
>>>>> C. also does not trigger exception
>>>>>
>>>>> |collections =dbsession.query(Collection).all()forcollection
>>>>> incollections:image_ids =[i.id fori
>>>>> incollection.images.all()]image_ids_string
>>>>> =','.join(sorted(image_ids))+collection.name
>>>>> withtransaction.manager:collection.id =md5(image_ids_string)[:16]|
>>>>>
>>>>>
>>>>> The exception for the first one is:
>>>>>
>>>>> |
>>>>> sqlalchemy.exc.IntegrityError:(psycopg2.IntegrityError)update ordeleteon
>>>>> table "collections"violates foreign key constraint
>>>>> "fk_collections_images_collection_id_collections"on table
>>>>> "collections_images"DETAIL:Key(id)=(jC3sN8952urTGrqz)isstill referenced
>>>>> fromtable "collections_images".
>>>>>
>>>>> |
>>>>>
>>>>> I've also tried onupdate='CASCADE' for both columns
>>>>> in collections_images but didn't change anything.
>>>>>
>>>>> --
>>>>> 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
>>>>> <mailto:sqlalchemy+unsubscr...@googlegroups.com>.
>>>>> To post to this group, send email to sqlalchemy@googlegroups.com
>>>>> <mailto:sqlalchemy@googlegroups.com>.
>>>>> Visit this group at https://groups.google.com/group/sqlalchemy.
>>>>> For more options, visit https://groups.google.com/d/optout.
>>>>
>>>>
>>>>
>>>> --
>>>> 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 a topic in
>>>> the
>>>> Google Groups "sqlalchemy" group.
>>>> To unsubscribe from this topic, visit
>>>> https://groups.google.com/d/topic/sqlalchemy/breBc7iStF0/unsubscribe.
>>>> To unsubscribe from this group and all its topics, send an email to
>>>> sqlalchemy+unsubscr...@googlegroups.com.
>>>> To post to this group, send email to sqlalchemy@googlegroups.com.
>>>> Visit this group at https://groups.google.com/group/sqlalchemy.
>>>> For more options, visit https://groups.google.com/d/optout.
>>>
>>>
>>
>> --
>> 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 a topic in the
>> Google Groups "sqlalchemy" group.
>> To unsubscribe from this topic, visit
>> https://groups.google.com/d/topic/sqlalchemy/breBc7iStF0/unsubscribe.
>> To unsubscribe from this group and all its topics, send an email to
>> sqlalchemy+unsubscr...@googlegroups.com.
>> To post to this group, send email to sqlalchemy@googlegroups.com.
>> Visit this group at https://groups.google.com/group/sqlalchemy.
>> For more options, visit https://groups.google.com/d/optout.

-- 
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 post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to