How to delete records from many-to-many (secondary) table in SQLAlchemy?

Question:

I am having problems deleting records from PostTag table that contains Post and Tag column. This is my relation table:

tags = db.Table('PostTag',
    db.Column('Tag', db.Integer, db.ForeignKey('Tag.Id')),
    db.Column('Post', db.Integer, db.ForeignKey('Post.Id'))
)

and

tags = db.relationship(Tag, secondary=tags, backref=db.backref('Post', lazy='dynamic'))

When I do this:

from models.Post import Post

posts = Post.query.join(Post.tags).filter(Post.Id == id).all()
if(posts):
   return posts
return False

and then

for posttag in post[0].tags:
    db.session.delete(posttag)
    db.session.flush()

Rows from many-to-many relation are deleted but also records from Tag table.

I just need to delete records from PostTag table for some condition (Post=1 for example)

I searched the internet but I did not find anything conclusive. I do not need cascade on many-to-many relationship.

This is sql log:

 297 Query  DELETE FROM `PostTag` WHERE `PostTag`.`Tag` = 14 AND `PostTag`.`Post` = 3
 297 Query  DELETE FROM `PostTag` WHERE `PostTag`.`Tag` = 14 AND `PostTag`.`Post` = 18
 297 Query  DELETE FROM `Tag` WHERE `Tag`.`Id` = 14

Last row 297 Query DELETE FROMTagWHERETag.Id= 14 should not be there.

UPDATE / maybe solution

I kind of solved this with:

sql = 'DELETE FROM PostTag WHERE Post=%s'
db.engine.execute(sql, post, ())

But that is not just ORM way. Same goes for insert. I will try to get this resolved ORM way. I will post answer if I make this problem go away.

Asked By: pregmatch

||

Answers:

Try to remove objects from the collection instead:

post[0].tags.clear()

You could also remove individual items:

post[0].tags.remove(posttag)
Answered By: Adam Byrtek

Try this:

post = db.session.query(Post).get(1)
post.tags = []
db.session.commit()

Here we are redefining the collection post.tags to the empty array and committing the changes. To explain this I’ll refer to the SQLAlchemy docs:

Collections in SQLAlchemy are transparently instrumented. Instrumentation means that normal operations on the collection are tracked and result in changes being written to the database at flush time.

So SQLAlchemy keeps track of the changes we make to the collection post.tags and updates it on commit.

If we had only one tag (say sometag) we could use the remove method like this:

post = db.session.query(Post).get(1)
post.tags.remove(sometag)
db.session.commit()
Answered By: pspencer
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.