pregmatch
pregmatch

Reputation: 2647

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

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.

Upvotes: 32

Views: 39018

Answers (2)

pspencer
pspencer

Reputation: 694

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

Upvotes: 40

Adam Byrtek
Adam Byrtek

Reputation: 12202

Try to remove objects from the collection instead:

post[0].tags.clear()

You could also remove individual items:

post[0].tags.remove(posttag)

Upvotes: 11

Related Questions