[sqlalchemy] Re: Deleting single association in many-to-many relationship

2007-06-13 Thread Michael Bayer


On Jun 13, 2007, at 10:44 AM, David Bolen wrote:

>
> If the cascade becomes "save-update, delete-orphan", then I get the
> behavior where deleting from a single parent job flushes all
> associations for the file.  Although its true you never get an orphan
> that way, files get orphaned "before their time".

"orphan" status really only pertains to a single-parent  
relationship.  it was not designed for use in a "collection of  
parents" sense.


>
> So, adding the cascade, and then changing your line to del j1.files[1]
> (to delete the common file), I get on the second flush:
>
> BEGIN
> DELETE FROM jobs_files WHERE jobs_files.job_id = ? AND  
> jobs_files.file_id = ?
> [[1, 2], [2, 2]]
> DELETE FROM files WHERE files.id = ?
> [2]
> COMMIT
>
> which is removing the common file association with both jobs, and then
> removing the file itself.  It's true that after doing the former, it's
> an orphan which can be deleted, but I don't know why it's including
> the second job in the association pruning.

if it deleted from files without deleting all known referencing rows  
in the association table, youd get a constraint exception.

>
> On the second point, the complexity of the full cascade recursion with
> orphan detection makes sense.  I suppose I'm interested in any input
> from anyone else as to how they are handling these sorts of operations
> in many-to-many cases with changing associations.

I would have a backreference on "files" referencing "jobs" and just  
check that the collection is of length zero..then delete.




--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com
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
-~--~~~~--~~--~--~---



[sqlalchemy] Re: Deleting single association in many-to-many relationship

2007-06-13 Thread sdobrev

>
> On the second point, the complexity of the full cascade recursion
> with orphan detection makes sense.  I suppose I'm interested in any
> input from anyone else as to how they are handling these sorts of
> operations in many-to-many cases with changing associations.

As i need a history (bitemporal) of all things in my db, i did not 
have updates nor deletions - all those operations become inserts (of 
same record with diff. status). For the same reasone, the one-to-many 
rels have become many-to-many - so u can add new versions on either 
side of the relation without changing any previous versions - only 
manipulating the associations inbetween.

Then, for the reasons of nested user-transactions and their rollback 
(see the thread same weeks ago), i ended up updating and deleting 
these (many-to-many) associations. This "transaction" engine is not 
100% working yet, but so far i haven't run into any cascade 
problems - and i do rely on them to work properly.

svil

--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com
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
-~--~~~~--~~--~--~---



[sqlalchemy] Re: Deleting single association in many-to-many relationship

2007-06-13 Thread David Bolen

Michael Bayer <[EMAIL PROTECTED]> writes:

> On Jun 13, 2007, at 6:13 AM, David Bolen wrote:
>
>> * I want to remove the association between "File Common" and "Job 1"
>>   but without affecting "Job 2".
>>
>>   If I session.delete() the fc instance directly, SA purges the file
>>   completely, including links to both jobs.  I can understand SA
>>   thinking I want the file completely gone in this scenario.
>>
>>   But if I remove the fc instance from the relation list (files) from
>>   either job, SA also fully purges fc, including the link to the other
>>   job.  This includes the case of deleting one of the jobs if I have
>>   the cascade on the files relation including "delete".  This would
>>   seem to prevent me from using a delete cascade, since then deleting
>>   any job would remove files it contains from all other jobs also
>>   containing those files which sort of defeats the purpose (at least
>>   for me) of the many to many relationship.
>
> If I add this code to the bottom:
>
> del j1.files[0]
> s.flush()

(...)

> the second flush is:
> 
> BEGIN
> DELETE FROM jobs_files WHERE jobs_files.job_id = ? AND  
> jobs_files.file_id = ?
> [1, 1]
> COMMIT
> 
> association is deleted only.

Argh - my bad.  I should have left a cascade option commented in the
example, since it needs to change slightly to show different
behaviors.

With the default cascade (as in the example as posted, just
save-update), I also see the individual removal from the parent object
only affects the association.  But nothing other than the association
is ever touched, and if you remove it from all parents, you end up
with an orphaned file (no jobs).  E.g., in your case above, file 1 was
only associated with job 1, and is now orphaned.

If the cascade becomes "save-update, delete-orphan", then I get the
behavior where deleting from a single parent job flushes all
associations for the file.  Although its true you never get an orphan
that way, files get orphaned "before their time".

So, adding the cascade, and then changing your line to del j1.files[1]
(to delete the common file), I get on the second flush:

BEGIN
DELETE FROM jobs_files WHERE jobs_files.job_id = ? AND jobs_files.file_id = ?
[[1, 2], [2, 2]]
DELETE FROM files WHERE files.id = ?
[2]
COMMIT

which is removing the common file association with both jobs, and then
removing the file itself.  It's true that after doing the former, it's
an orphan which can be deleted, but I don't know why it's including
the second job in the association pruning.

So I was either getting orphans, or completely removed files when
trying to remove a single association.  I can't seem to get the two
desired behaviors together?


On the second point, the complexity of the full cascade recursion with
orphan detection makes sense.  I suppose I'm interested in any input
from anyone else as to how they are handling these sorts of operations
in many-to-many cases with changing associations.

-- David


--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com
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
-~--~~~~--~~--~--~---



[sqlalchemy] Re: Deleting single association in many-to-many relationship

2007-06-13 Thread Michael Bayer


On Jun 13, 2007, at 6:13 AM, David Bolen wrote:

> * I want to remove the association between "File Common" and "Job 1"
>   but without affecting "Job 2".
>
>   If I session.delete() the fc instance directly, SA purges the file
>   completely, including links to both jobs.  I can understand SA
>   thinking I want the file completely gone in this scenario.
>
>   But if I remove the fc instance from the relation list (files) from
>   either job, SA also fully purges fc, including the link to the other
>   job.  This includes the case of deleting one of the jobs if I have
>   the cascade on the files relation including "delete".  This would
>   seem to prevent me from using a delete cascade, since then deleting
>   any job would remove files it contains from all other jobs also
>   containing those files which sort of defeats the purpose (at least
>   for me) of the many to many relationship.

If I add this code to the bottom:

del j1.files[0]
s.flush()

the first flush is:

BEGIN
INSERT INTO files (name) VALUES (?)
['File 1']
INSERT INTO files (name) VALUES (?)
['File Common']
INSERT INTO files (name) VALUES (?)
['File 2']
INSERT INTO jobs (name) VALUES (?)
['Job 1']
INSERT INTO jobs (name) VALUES (?)
['Job 2']
INSERT INTO jobs_files (job_id, file_id) VALUES (?, ?)
[[1, 1], [1, 2], [2, 3], [2, 2]]
COMMIT


the second flush is:

BEGIN
DELETE FROM jobs_files WHERE jobs_files.job_id = ? AND  
jobs_files.file_id = ?
[1, 1]
COMMIT

association is deleted only.


>
> * Providing I can resolve the prior point, I was hoping to have a way
>   that would let me remove a job completely, including any associated
>   files, but have the file records only pruned if they did not belong
>   to any other job.

that you have to do manually.  cascade isnt going to do that for  
you.  namely, that it requires descending into all child objects,  
magically producing backreferences to them in order to load their  
parent objects (in the case backrefs werent defined), then issuing  
SELECTs for all child objects to determine if they had any other  
parents.  way out of scope.

> Am I just missing something blindingly obvious, or should I be trying
> to manage the many-to-many relationships, at least deleting, in some
> other way?  Should I be interacting with the relationship table
> directly (but then, how do I remove the files from the contained
> project objects without triggering the deletion behavior I don't  
> want)?

if you really wanted to manipulate the relationship table directly,  
thats fine, but ensure that you expire/refresh the relevant objects  
and /or clear out the whole session before continuing past that point  
since SA wouldnt otherwise know you modified relationships behind its  
back.

--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com
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
-~--~~~~--~~--~--~---



[sqlalchemy] Re: Deleting single association in many-to-many relationship

2007-06-13 Thread Michael Bayer


On Jun 13, 2007, at 7:45 AM, [EMAIL PROTECTED] wrote:

> the only line removed was the "from .orm import *" - u shouldnt
> use any of those internal stuff unless u know what u do.

no, this will be required in 0.4, and its mentioned in some of the  
0.3 docs as well.  sqlalchemy.orm is not an internal module.


--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com
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
-~--~~~~--~~--~--~---



[sqlalchemy] Re: Deleting single association in many-to-many relationship

2007-06-13 Thread sdobrev

what version u use?

i tried your thing, that is 
$ python -i zz.py
 s = create_session()
 j = s.query(Job)[0] #get first
 del j.files[0]
 s.flush()
and seems to work

before:
>>> for a in s.query(Job): print a.name, a.files
... 
Job 1 [<__main__.File object at 0xb78ec72c>, <__main__.File object at 
0xb78e666c>]
Job 2 [<__main__.File object at 0xb78ec72c>, <__main__.File object at 
0xb78ec76c>]

after:
>>> for a in s.query(Job): print a.name, a.files
... 
Job 1 [<__main__.File object at 0xb78e666c>]
Job 2 [<__main__.File object at 0xb78ec72c>, <__main__.File object at 
0xb78ec76c>]


-
the only line removed was the "from .orm import *" - u shouldnt 
use any of those internal stuff unless u know what u do.

ciao
svil


On Wednesday 13 June 2007 13:13:58 David Bolen wrote:
> from sqlalchemy import *
> from sqlalchemy.orm import *
>
> meta = BoundMetaData('sqlite:///')
>
> jobs = Table('jobs', meta,
>  Column('id', Integer, primary_key=True),
>  Column('name', String))
>
> files = Table('files', meta,
>   Column('id', Integer, primary_key=True),
>   Column('name', String))
>
> jobs_files = Table('jobs_files', meta,
>Column('job_id', Integer,
> ForeignKey('jobs.id')), Column('file_id', Integer,
> ForeignKey('files.id')))
>
>
> class Job(object):
> pass
> class File(object):
> pass
>
> mapper(File, files)
> mapper(Job, jobs,
>properties = { 'files': relation(File, lazy=False,
> backref=backref('jobs',
> lazy=False), secondary=jobs_files) }) 
>
> def setup():
> meta.create_all()
> s = create_session()
>
> f1 = File()
> f1.name = 'File 1'
> f2 = File()
> f2.name = 'File 2'
> fc = File()
> fc.name = 'File Common'
>
> j1 = Job()
> j1.name = 'Job 1'
> j2 = Job()
> j2.name = 'Job 2'
>
> s.save(j1)
> s.save(j2)
>
> j1.files.extend([f1, fc])
> j2.files.extend([f2, fc])
>
> s.flush()



--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com
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
-~--~~~~--~~--~--~---