Sqlalchemy, выберите с помощью обратного включения (не в) список значений дочернего столбца

У меня есть типичная связь Post/Tags (много тегов, связанных с одной записью) в flask-sqlalchemy, и я хочу выбирать сообщения, которые не помечены каким-либо тегом в списке, который я предоставляю. Во-первых, созданные мной модели:

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    tags = db.relationship('Tag', lazy='dynamic')

class Tag(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Text(50))
    post_id = db.Column(db.Integer, db.ForeignKey('post.id'))

Что-то вроде

db.session.query(Post).filter(Post.tags.name.notin_(['dont','want','these']))

не работает с

AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with Post.tags has an attribute 'name'

который я предполагаю, потому что теги являются отношением, а не столбцом. Я работал над другим проектом, когда писал реальный SQL вручную. Это был SQL, который работал:

SELECT * FROM $posts WHERE id NOT IN (SELECT post_id FROM $tags WHERE name IN ('dont','want','these'))

Как я могу достичь этого, используя API sqlalchemy?

Ответы

Ответ 1

Довольно просто, используя отрицание any:

query = session.query(Post).filter(~Post.tags.any(Tag.name.in_(['dont', 'want', 'these'])))

Ответ 2

Попробуйте это, просто:

users = session.query(Post).filter(not_(Post.tags.name.in_(['dont', 'want', these'])))

Надеюсь, это поможет!

Ответ 3

Я придумал противное решение, но оно работает пока. Мне было бы интересно услышать, если кто-нибудь придумает более разумный метод.

ignore_ids = [item.post_id for item in Tag.query.filter(Tag.name.in_(['dont','want','these'])).all()]
Post.query.filter(Post.id.notin_(ignore_ids))