This seems like it would be a very common scenario, but it's got me
stumped and feeling a bit stupid at the moment - I would appreciate
anyone helping to point me in the right direction.

I'm using the ORM for a many-to-many relationship, for which over time
I need to be able to prune individual associations from items that may
(obviously) have several currently active.  But I can't seem to figure
out how to do it through ORM/session/object actions without fully
purging the object in question, also removing it from associations I
don't want to touch.  It seems far too fragile for what I would think
would be very common needs of a many-to-many relationship.

For example, here's a stripped down many-to-many setup for jobs which
contain files.  Files may be shared amongst jobs, thus the
many-to-many relationship.

          - - - - - - - - - - - - - - - - - - - - - - - - -

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()

          - - - - - - - - - - - - - - - - - - - - - - - - -

I've used eager loading for the relationships since that mimics my
actual code, but I believe the issues hold even with lazy loading.

(No loading, lazy=None, is whole other thing as that seems very
fragile with respect to relations since if you don't have a particular
relation loaded when you modify an instance SA doesn't know to
follow.  Not that I really blame SA in that case I suppose.)
          
Anyway, assuming that setup() has been called, these are the sort of
activities that have me stumped:

* 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.

  The only case where I'd want the fc instance in the database to be
  fully purged would be if I was deleting the last association with
  any jobs, something I thought delete-orphan would handle.

* 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.

  Originally I had tried including "delete" and "delete-orphan" in the
  cascade rules would accomplish this.  But I found that the delete
  cascade triggered behavior as above - fully removing all files even
  if they still belong to other jobs.  If I only leave the
  delete-orphan cascade, deleting the parent job has no impact on the
  files, leaving stranded files and the old associations around.

Most of the many-to-many examples I found tend to use "all,
delete-orphan" as the cascade rules, but at least in my experience
that makes it dangerous to delete, in my scenario, job instances as it
purges all contained files even if they are still referenced in other
jobs.  And you'd never know later that they were ever part of the
other jobs (E.g., they are cleanly removed from all jobs during the
flush).  While I can understand why assuming a simple iteration over
child container objects during the delete cascade, in a practical
sense that doesn't seem very useful for many-to-many relationships
(as opposed to one-to-one/one-to-many).

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)?

Thanks for any suggestions.

-- 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
-~----------~----~----~----~------~----~------~--~---

Reply via email to