[sqlalchemy] Multiple delete attempts of same rows with a star schema configuration

2014-09-04 Thread Ken Lareau
So I have a few tables as follows (abbreviated for unnecessary columns):

class Project(Base):
__tablename__ = 'projects'

id = Column(u'project_id', INTEGER(), primary_key=True)

applications = relationship(
'AppDefinition',
secondary=lambda: Base.metadata.tables['project_package'],
passive_deletes=True,
lazy=False,
)

package_definitions = relationship(
'PackageDefinition',
secondary=lambda: Base.metadata.tables['project_package'],
passive_deletes=True,
)


class PackageDefinition(Base):
__tablename__ = 'package_definitions'

id = Column(u'pkg_def_id', INTEGER(), primary_key=True)

applications = relationship(
'AppDefinition',
secondary=lambda: Base.metadata.tables['project_package'],
passive_deletes=True,
)

projects = relationship(
'Project',
secondary=lambda: Base.metadata.tables['project_package'],
passive_deletes=True,
)


class AppDefinition(Base):
__tablename__ = 'app_definitions'

id = Column(u'AppID', SMALLINT(display_width=2), primary_key=True)

package_definitions = relationship(
'PackageDefinition',
secondary=lambda: Base.metadata.tables['project_package'],
passive_deletes=True,
)

projects = relationship(
'Project',
secondary=lambda: Base.metadata.tables['project_package'],
passive_deletes=True,
)


class ProjectPackage(Base):
__tablename__ = 'project_package'

project_id = Column(
INTEGER(),
ForeignKey('projects.project_id', ondelete='cascade'),
primary_key=True
)
pkg_def_id = Column(
INTEGER(),
ForeignKey('package_definitions.pkg_def_id', ondelete='cascade'),
primary_key=True
)
app_id = Column(
SMALLINT(display_width=6),
ForeignKey('app_definitions.AppID', ondelete='cascade'),
primary_key=True
)

app_definition = relationship('AppDefinition', uselist=False)
package_definition = relationship('PackageDefinition', uselist=False)
project = relationship('Project', uselist=False)


If I select a row from the projects table and try to delete it, when I try
to commit
the session I get something like this:

[INFO] DELETE FROM project_package WHERE project_package.project_id = ? AND
project_package.app_id = ?
[INFO] (1L, 1L)
[INFO] DELETE FROM project_package WHERE project_package.project_id = ? AND
project_package.pkg_def_id = ?
[INFO] (1L, 1L)
[INFO] ROLLBACK

At the time of the delete, the project_package table simply has this:

[('project_id', 1L), ('pkg_def_id', 1L), ('app_id', 1L)]

Obviously the combination of the ondelete='cascade' settings in the
project_package table and the 'passive_deletes' settings in the
relationships
is what's triggering this, but after try many different searches on Google,
I'm
not finding the right solution to fix this; obviously I only want it to
attempt to
delete the row once, but each relationship in project is triggering its own
delete which is bad.

So... help?  Am I missing something simple here, or is my model not good
enough to do the right thing?

-- 
- Ken Lareau

-- 
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.
For more options, visit https://groups.google.com/d/optout.


Re: [sqlalchemy] Multiple delete attempts of same rows with a star schema configuration

2014-09-04 Thread Michael Bayer

On Sep 4, 2014, at 5:37 PM, Ken Lareau klar...@tagged.com wrote:

 So I have a few tables as follows (abbreviated for unnecessary columns):
 
 class Project(Base):
 __tablename__ = 'projects'
 
 id = Column(u'project_id', INTEGER(), primary_key=True)
 
 applications = relationship(
 'AppDefinition',
 secondary=lambda: Base.metadata.tables['project_package'],
 passive_deletes=True,
 lazy=False,
 )
 
 package_definitions = relationship(
 'PackageDefinition',
 secondary=lambda: Base.metadata.tables['project_package'],
 passive_deletes=True,
 )
 
 
 class PackageDefinition(Base):
 __tablename__ = 'package_definitions'
 
 id = Column(u'pkg_def_id', INTEGER(), primary_key=True)
 
 applications = relationship(
 'AppDefinition',
 secondary=lambda: Base.metadata.tables['project_package'],
 passive_deletes=True,
 )
 
 projects = relationship(
 'Project',
 secondary=lambda: Base.metadata.tables['project_package'],
 passive_deletes=True,
 )
 
 
 class AppDefinition(Base):
 __tablename__ = 'app_definitions'
 
 id = Column(u'AppID', SMALLINT(display_width=2), primary_key=True)
 
 package_definitions = relationship(
 'PackageDefinition',
 secondary=lambda: Base.metadata.tables['project_package'],
 passive_deletes=True,
 )
 
 projects = relationship(
 'Project',
 secondary=lambda: Base.metadata.tables['project_package'],
 passive_deletes=True,
 )
 
 
 class ProjectPackage(Base):
 __tablename__ = 'project_package'
 
 project_id = Column(
 INTEGER(),
 ForeignKey('projects.project_id', ondelete='cascade'),
 primary_key=True
 )
 pkg_def_id = Column(
 INTEGER(),
 ForeignKey('package_definitions.pkg_def_id', ondelete='cascade'),
 primary_key=True
 )
 app_id = Column(
 SMALLINT(display_width=6),
 ForeignKey('app_definitions.AppID', ondelete='cascade'),
 primary_key=True
 )
 
 app_definition = relationship('AppDefinition', uselist=False)
 package_definition = relationship('PackageDefinition', uselist=False)
 project = relationship('Project', uselist=False)

this model has serious issues, though it can be made to hobble along with some 
small changes.  The three way association table is not a great pattern in the 
first place.  Additionally, using that same table as secondary in all those 
places is clearly the issue here, and they don't even have back_populates 
between them, which are used in many-to-many in order to try avoid double 
deleting.   But that wouldn't solve the issue here anyway.

In this case, you have mapped to the project_package table directly; you're 
using the association object pattern.  The note at the end of 
http://docs.sqlalchemy.org/en/rel_0_9/orm/relationships.html#association-object 
applies here - you absolutely should have viewonly=True on all of those 
relationship() directives that refer to this association table as the 
secondary table.  The ORM otherwise has no way to know what changes should 
result in what deletions where.


-- 
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.
For more options, visit https://groups.google.com/d/optout.


Re: [sqlalchemy] Multiple delete attempts of same rows with a star schema configuration

2014-09-04 Thread Ken Lareau
On Thu, Sep 4, 2014 at 3:30 PM, Michael Bayer mike...@zzzcomputing.com
wrote:


 On Sep 4, 2014, at 5:37 PM, Ken Lareau klar...@tagged.com wrote:

 So I have a few tables as follows (abbreviated for unnecessary columns):

 class Project(Base):
 __tablename__ = 'projects'

 id = Column(u'project_id', INTEGER(), primary_key=True)

 applications = relationship(
 'AppDefinition',
 secondary=lambda: Base.metadata.tables['project_package'],
 passive_deletes=True,
 lazy=False,
 )

 package_definitions = relationship(
 'PackageDefinition',
 secondary=lambda: Base.metadata.tables['project_package'],
 passive_deletes=True,
 )


 class PackageDefinition(Base):
 __tablename__ = 'package_definitions'

 id = Column(u'pkg_def_id', INTEGER(), primary_key=True)

 applications = relationship(
 'AppDefinition',
 secondary=lambda: Base.metadata.tables['project_package'],
 passive_deletes=True,
 )

 projects = relationship(
 'Project',
 secondary=lambda: Base.metadata.tables['project_package'],
 passive_deletes=True,
 )


 class AppDefinition(Base):
 __tablename__ = 'app_definitions'

 id = Column(u'AppID', SMALLINT(display_width=2), primary_key=True)

 package_definitions = relationship(
 'PackageDefinition',
 secondary=lambda: Base.metadata.tables['project_package'],
 passive_deletes=True,
 )

 projects = relationship(
 'Project',
 secondary=lambda: Base.metadata.tables['project_package'],
 passive_deletes=True,
 )


 class ProjectPackage(Base):
 __tablename__ = 'project_package'

 project_id = Column(
 INTEGER(),
 ForeignKey('projects.project_id', ondelete='cascade'),
 primary_key=True
 )
 pkg_def_id = Column(
 INTEGER(),
 ForeignKey('package_definitions.pkg_def_id', ondelete='cascade'),
 primary_key=True
 )
 app_id = Column(
 SMALLINT(display_width=6),
 ForeignKey('app_definitions.AppID', ondelete='cascade'),
 primary_key=True
 )

 app_definition = relationship('AppDefinition', uselist=False)
 package_definition = relationship('PackageDefinition', uselist=False)
 project = relationship('Project', uselist=False)


 this model has serious issues, though it can be made to hobble along with
 some small changes.  The “three way association table” is not a great
 pattern in the first place.  Additionally, using that same table as
 “secondary” in all those places is clearly the issue here, and they don’t
 even have back_populates between them, which are used in many-to-many in
 order to try avoid “double deleting”.   But that wouldn’t solve the issue
 here anyway.


As I feared, I figured this model would be frowned upon. :)  Note that this
model (or the underlying database)
is not set in stone; if you have suggestions on a better way to link the
three main tables, I would love to hear
it (and we did add back_populates (somehow missed them during the
restructuring of the models), but as
you said, didn't help in this case).



 In this case, you have mapped to the project_package table directly;
 you’re using the association object pattern.  The note at the end of
 http://docs.sqlalchemy.org/en/rel_0_9/orm/relationships.html#association-object
 applies here - you absolutely should have viewonly=True on all of those
 relationship() directives that refer to this association table as the
 “secondary” table.  The ORM otherwise has no way to know what changes
 should result in what deletions where.


Will add the 'viewonly' option as suggested and see if that helps, but as
mentioned above, if you can suggest
a better model for this case, please let me know!

-- 
- Ken Lareau

-- 
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.
For more options, visit https://groups.google.com/d/optout.


Re: [sqlalchemy] Multiple delete attempts of same rows with a star schema configuration

2014-09-04 Thread Michael Bayer

On Sep 4, 2014, at 6:58 PM, Ken Lareau klar...@tagged.com wrote:

 On Thu, Sep 4, 2014 at 3:30 PM, Michael Bayer mike...@zzzcomputing.com 
 wrote:
 
 On Sep 4, 2014, at 5:37 PM, Ken Lareau klar...@tagged.com wrote:
 
 So I have a few tables as follows (abbreviated for unnecessary columns):
 
 class Project(Base):
 __tablename__ = 'projects'
 
 id = Column(u'project_id', INTEGER(), primary_key=True)
 
 applications = relationship(
 'AppDefinition',
 secondary=lambda: Base.metadata.tables['project_package'],
 passive_deletes=True,
 lazy=False,
 )
 
 package_definitions = relationship(
 'PackageDefinition',
 secondary=lambda: Base.metadata.tables['project_package'],
 passive_deletes=True,
 )
 
 
 class PackageDefinition(Base):
 __tablename__ = 'package_definitions'
 
 id = Column(u'pkg_def_id', INTEGER(), primary_key=True)
 
 applications = relationship(
 'AppDefinition',
 secondary=lambda: Base.metadata.tables['project_package'],
 passive_deletes=True,
 )
 
 projects = relationship(
 'Project',
 secondary=lambda: Base.metadata.tables['project_package'],
 passive_deletes=True,
 )
 
 
 class AppDefinition(Base):
 __tablename__ = 'app_definitions'
 
 id = Column(u'AppID', SMALLINT(display_width=2), primary_key=True)
 
 package_definitions = relationship(
 'PackageDefinition',
 secondary=lambda: Base.metadata.tables['project_package'],
 passive_deletes=True,
 )
 
 projects = relationship(
 'Project',
 secondary=lambda: Base.metadata.tables['project_package'],
 passive_deletes=True,
 )
 
 
 class ProjectPackage(Base):
 __tablename__ = 'project_package'
 
 project_id = Column(
 INTEGER(),
 ForeignKey('projects.project_id', ondelete='cascade'),
 primary_key=True
 )
 pkg_def_id = Column(
 INTEGER(),
 ForeignKey('package_definitions.pkg_def_id', ondelete='cascade'),
 primary_key=True
 )
 app_id = Column(
 SMALLINT(display_width=6),
 ForeignKey('app_definitions.AppID', ondelete='cascade'),
 primary_key=True
 )
 
 app_definition = relationship('AppDefinition', uselist=False)
 package_definition = relationship('PackageDefinition', uselist=False)
 project = relationship('Project', uselist=False)
 
 this model has serious issues, though it can be made to hobble along with 
 some small changes.  The three way association table is not a great pattern 
 in the first place.  Additionally, using that same table as secondary in 
 all those places is clearly the issue here, and they don't even have 
 back_populates between them, which are used in many-to-many in order to try 
 avoid double deleting.   But that wouldn't solve the issue here anyway.
 
 As I feared, I figured this model would be frowned upon. :)  Note that this 
 model (or the underlying database)
 is not set in stone; if you have suggestions on a better way to link the 
 three main tables, I would love to hear
 it (and we did add back_populates (somehow missed them during the 
 restructuring of the models), but as
 you said, didn't help in this case).

A row in the 3-way table means that for this project_id, and pkg_def_id, and 
app_id, that's a single allowed value, so that's star schema, OK (though they 
call star schema denormalized - i don't exactly understand why, if you make 
all the columns part of the primary key).  But then you have functions that 
give you lists of things based on two-column slices of that table, in order to 
relate different dimensions to each other; projects-app_definitions, means you 
can have Project (X) - AppDefnition(Y) any number of times, for every value of 
pkg_def_id.I guess that's the awkward part here.   Those aren't really 
collection-based relationships so much as analytical queries across dimension 
tables.   They are valid queries but aren't really object/relational - when 
we map a class to another with a many-to-many collection in between, that means 
something more specific.

So maybe its just, object relational mapping assumes certain patterns are 
mappable to objects and collections, and 3-way associations aren't quite in 
that pattern :).





-- 
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.
For more options, visit https://groups.google.com/d/optout.


Re: [sqlalchemy] Multiple delete attempts of same rows with a star schema configuration

2014-09-04 Thread Ken Lareau
On Thu, Sep 4, 2014 at 4:35 PM, Michael Bayer mike...@zzzcomputing.com
wrote:


 On Sep 4, 2014, at 6:58 PM, Ken Lareau klar...@tagged.com wrote:

 On Thu, Sep 4, 2014 at 3:30 PM, Michael Bayer mike...@zzzcomputing.com
 wrote:


 On Sep 4, 2014, at 5:37 PM, Ken Lareau klar...@tagged.com wrote:

 So I have a few tables as follows (abbreviated for unnecessary columns):

 class Project(Base):
 __tablename__ = 'projects'

 id = Column(u'project_id', INTEGER(), primary_key=True)

 applications = relationship(
 'AppDefinition',
 secondary=lambda: Base.metadata.tables['project_package'],
 passive_deletes=True,
 lazy=False,
 )

 package_definitions = relationship(
 'PackageDefinition',
 secondary=lambda: Base.metadata.tables['project_package'],
 passive_deletes=True,
 )


 class PackageDefinition(Base):
 __tablename__ = 'package_definitions'

 id = Column(u'pkg_def_id', INTEGER(), primary_key=True)

 applications = relationship(
 'AppDefinition',
 secondary=lambda: Base.metadata.tables['project_package'],
 passive_deletes=True,
 )

 projects = relationship(
 'Project',
 secondary=lambda: Base.metadata.tables['project_package'],
 passive_deletes=True,
 )


 class AppDefinition(Base):
 __tablename__ = 'app_definitions'

 id = Column(u'AppID', SMALLINT(display_width=2), primary_key=True)

 package_definitions = relationship(
 'PackageDefinition',
 secondary=lambda: Base.metadata.tables['project_package'],
 passive_deletes=True,
 )

 projects = relationship(
 'Project',
 secondary=lambda: Base.metadata.tables['project_package'],
 passive_deletes=True,
 )


 class ProjectPackage(Base):
 __tablename__ = 'project_package'

 project_id = Column(
 INTEGER(),
 ForeignKey('projects.project_id', ondelete='cascade'),
 primary_key=True
 )
 pkg_def_id = Column(
 INTEGER(),
 ForeignKey('package_definitions.pkg_def_id', ondelete='cascade'),
 primary_key=True
 )
 app_id = Column(
 SMALLINT(display_width=6),
 ForeignKey('app_definitions.AppID', ondelete='cascade'),
 primary_key=True
 )

 app_definition = relationship('AppDefinition', uselist=False)
 package_definition = relationship('PackageDefinition', uselist=False)
 project = relationship('Project', uselist=False)


 this model has serious issues, though it can be made to hobble along with
 some small changes.  The “three way association table” is not a great
 pattern in the first place.  Additionally, using that same table as
 “secondary” in all those places is clearly the issue here, and they don’t
 even have back_populates between them, which are used in many-to-many in
 order to try avoid “double deleting”.   But that wouldn’t solve the issue
 here anyway.


 As I feared, I figured this model would be frowned upon. :)  Note that
 this model (or the underlying database)
 is not set in stone; if you have suggestions on a better way to link the
 three main tables, I would love to hear
 it (and we did add back_populates (somehow missed them during the
 restructuring of the models), but as
 you said, didn't help in this case).


 A row in the 3-way table means that for this project_id, and pkg_def_id,
 and app_id, that’s a single allowed value, so that’s star schema, OK
 (though they call star schema “denormalized” - i don’t exactly understand
 why, if you make all the columns part of the primary key).  But then you
 have functions that give you lists of things based on two-column slices of
 that table, in order to relate different dimensions to each other;
 projects-app_definitions, means you can have Project (X) -
 AppDefnition(Y) any number of times, for every value of pkg_def_id.I
 guess that’s the awkward part here.   Those aren’t really collection-based
 “relationships” so much as analytical queries across dimension tables.
 They are valid queries but aren’t really “object/relational” - when we map
 a class to another with a many-to-many collection in between, that means
 something more specific.

 So maybe its just, object relational mapping assumes certain patterns are
 mappable to objects and collections, and 3-way associations aren’t quite in
 that pattern :).


Hmm, understood, though the main reason for these relationships were to
avoid having to make
DB queries within the main application code (pushing them out into the
models) and simplify the
logic; if you had seen what our application code looked like before, you
might shudder even more
than over what we're doing here. :)

Is there a way to essentially allow something like 'for app in
project.applications:' without having
to make an explicit query to the DB first, but avoiding the awkwardness of
the current setup?

-- 
- Ken Lareau

-- 
You received this message because you are 

Re: [sqlalchemy] Multiple delete attempts of same rows with a star schema configuration

2014-09-04 Thread Michael Bayer

On Sep 4, 2014, at 7:50 PM, Ken Lareau klar...@tagged.com wrote:

 
 Is there a way to essentially allow something like 'for app in 
 project.applications:' without having
 to make an explicit query to the DB first, but avoiding the awkwardness of 
 the current setup? 

just use the viewonly=True and we can all go home :)  I'm not too upset about it


-- 
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.
For more options, visit https://groups.google.com/d/optout.


Re: [sqlalchemy] Multiple delete attempts of same rows with a star schema configuration

2014-09-04 Thread Ken Lareau
On Thu, Sep 4, 2014 at 4:54 PM, Michael Bayer mike...@zzzcomputing.com
wrote:


 On Sep 4, 2014, at 7:50 PM, Ken Lareau klar...@tagged.com wrote:


 Is there a way to essentially allow something like 'for app in
 project.applications:' without having
 to make an explicit query to the DB first, but avoiding the awkwardness of
 the current setup?


 just use the viewonly=True and we can all go home :)  I’m not too upset
 about it


Got it... the change worked fine, thanks for all the help. :)

-- 
- Ken Lareau

-- 
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.
For more options, visit https://groups.google.com/d/optout.