On Thu, Mar 3, 2016 at 8:27 AM, Lele Gaifax <l...@metapensiero.it> wrote:

> Hi all,
>
> I have a simple m2m relationship between two entities, with a secondary
> table
> in the middle.
>
> From the left item I need to read both the right items and the middle
> ones, so
> I have two relationships on the left entity.
>
> When I delete one left item, I get the following exception:
>
>   AssertionError: Dependency rule tried to blank-out primary key column
>                   'persons2activities.idperson' on instance
>                   '<PersonActivity...>
>
> I tried to investigate, but without luck. Am I missing something?
>
> The following script exhibits the problem:
>
>
> from sqlalchemy import create_engine
> from sqlalchemy import Column, Integer, ForeignKey, String
> from sqlalchemy.orm import relationship, sessionmaker
> from sqlalchemy.ext.declarative import declarative_base
>
>
> Base = declarative_base()
>
>
> class Person(Base):
>     __tablename__ = 'persons'
>     id = Column(Integer, primary_key=True, nullable=False)
>     name = Column(String, nullable=False)
>     activities = relationship('Activity', secondary='persons2activities')
>
>
> class Activity(Base):
>     __tablename__ = 'activities'
>     id = Column(Integer, primary_key=True, nullable=False)
>     name = Column(String, nullable=False)
>
>
> class PersonActivity(Base):
>     __tablename__ = 'persons2activities'
>     idperson = Column(Integer, ForeignKey('persons.id'), nullable=False,
> primary_key=True)
>     idactivity = Column(Integer, ForeignKey('activities.id'),
> nullable=False, primary_key=True)
>     # Comment out the following, and everything works without errors
>     person = relationship(Person, backref='preferred_activities')
>
>
> def main():
>     engine = create_engine('sqlite:///:memory:', echo=True)
>     Session = sessionmaker(bind=engine)
>     sess = Session()
>     Base.metadata.create_all(engine)
>
>     person = Person(id=1, name='lele')
>     activity = Activity(id=1, name='carrom')
>     person.activities.append(activity)
>
>     sess.add(person)
>     sess.flush()
>     sess.commit()
>
>     sess.expunge_all()
>
>     person = sess.query(Person).get(1)
>     sess.delete(person)
>     sess.flush()
>     sess.commit()
>
>
> if __name__ == '__main__':
>     main()
>
>
In general I think it is not recommended to use "secondary" with a table
that you have also mapped a class to. (eg. see the warning at the bottom of
http://docs.sqlalchemy.org/en/rel_1_0/orm/basic_relationships.html#association-object
).

If SQLAlchemy is handling the many-to-many relationship itself (via the
"secondary" argument), it will delete rows from the association table when
one of the related objects is deleted. However, your
"Person.preferred_activities" relationship is one-to-many, and so the
default behaviour when deleting the Person will be to set the foreign keys
pointing to it to NULL. In this case that means setting
PersonActivity.idperson to NULL, but this is impossible since that column
is part of the primary key of the table.

You *might* be able to fix this by setting the cascade behaviour on
Person.preferred_activities to "all, delete-orphan":

http://docs.sqlalchemy.org/en/rel_1_0/orm/cascades.html#unitofwork-cascades

Hope that helps,

Simon

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