Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQLAlchemy delete association objects

I'm trying to batch delete objects from an association table by filtering on a column in one of the relationships. I use the following call in SQLAlchemy to make the delete

db.session.query(UserPaper).join(Paper, (UserPaper.paper_id ==
Paper.id)).filter(UserPaper.user_id == user.id).filter(Paper.journal_id 
== journal.id).delete()

and it results in the following error

OperationalError: (OperationalError) (1054, "Unknown column 'papers.journal_id' 
in 'where clause'") 'DELETE FROM userpapers WHERE userpapers.user_id = %s AND
papers.journal_id = %s' (1L, 1L)

Without the delete at the end, the SQLAlchemy query is

SELECT userpapers.user_id AS userpapers_user_id, userpapers.paper_id AS 
userpapers_paper_id, userpapers.created AS userpapers_created, 
userpapers.read_at AS userpapers_read_at, userpapers.score AS userpapers_score
FROM userpapers JOIN papers ON userpapers.paper_id = papers.id
WHERE userpapers.user_id = :user_id_1 AND papers.journal_id = :journal_id_1

which is correct. From the error I can see that when I append delete() to the query the join part of SQL statement gets lost and the database doesn't know how to find the papers.journal_id column obviously. What I don't understand is why does that happen?

This is the setup of my ORM objects

class User(db.Model):
    __tablename__ = 'users'

    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(255), unique=True)
    papers = db.relationship("UserPaper", backref=db.backref('users'), lazy='dynamic')

class Paper(db.Model):
    __tablename__ = 'papers'

    id = db.Column(db.Integer, primary_key = True)
    title = db.Column(db.String(1024))
    journal_id = db.Column(db.Integer, db.ForeignKey('journals.id'))

class UserPaper(db.Model):
    __tablename__ = 'userpapers'

    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), primary_key=True)
    paper_id = db.Column(db.Integer, db.ForeignKey('papers.id'), primary_key=True)
    paper = db.relationship("Paper", backref=db.backref('user_paper'))
    read_at = db.Column(db.DateTime)
    score = db.Column(db.Integer)

class Journal(db.Model):
    __tablename__ = 'journals'

    id = db.Column(db.Integer, primary_key = True)
    title = db.Column(db.String(100), index = True, unique = True)
    papers = db.relationship('Paper', backref = 'journal', lazy = 'dynamic')
like image 534
Jacopo Avatar asked Nov 01 '13 22:11

Jacopo


1 Answers

I had the same problem with SQLALchemy 0.9 using MySQL 5.6. It looks like a bug/limitation. However, one better way to get arround (in comparison to creating the query, looping through the results and deleting them one by one) is to perform this task in two subsequent queries:

    paperQuery = db.session.query(Paper.id)\
                                    filter(Paper.journal_id == journal.id)


    baseQuery  = db.session.query(UserPaper)\
                                    .filter(UserPaper.paper_id.in_(paperQuery.subquery()))
                                    .filter(UserPaper.user_id == user.id).delete(synchronize_session='fetch')

It worked well for me, it should solve you issue too.

like image 136
Christian O'Reilly Avatar answered Sep 28 '22 16:09

Christian O'Reilly