SQLAlchemy:过滤多对一关系,其中一个对象具有包含特定值的列表

时间:2016-01-15 05:10:22

标签: python sqlalchemy flask-sqlalchemy

我有一些像这样的表:

class Genre(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(128), index=True)
    artist_id = db.Column(db.Integer, db.ForeignKey('artist.id'))

class Song(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(128), index=True)
    artist = db.relationship('Artist', uselist=False)
    artist_id = db.Column(db.Integer, db.ForeignKey('artist.id'))

class Artist(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(128), index=True)
    genres = db.relationship('Genre')
    songs = db.relationship('Song')

所以基本上,歌曲有一个艺术家。每位艺术家都可以拥有多种类型。

我正在努力获得任何艺术家的所有歌曲,其中的类型列表包含一个特定名称的流派。我做了一些研究,发现了一些非常接近的东西:

Song.query.filter(Artist.genres.any(Genre.name.in_([genre_name_im_looking_for])))

这将是一种工作,但并非适用于所有情况。例如,上述声明还将返回所有拥有类型独立摇滚乐的艺术家的歌曲。如何指定我不希望类型名称位于值列表中,而是要成为特定值?

Song.query.filter(Artist.genres.any(Genre.name='rock'))

也是不可能的,因为它是关键字表达式。

有什么想法吗?

2 个答案:

答案 0 :(得分:5)

使用此测试数据:

# Test Data
artists = [
    Artist(
        name='should match rock',
        genres=[Genre(name='rock'), Genre(name='pop')],
        songs=[Song(name='love'), Song(name='hate')]
    ),
    Artist(
        name='should NOT match',
        genres=[Genre(name='indie rock')],
        songs=[Song(name='elsewhere')]
    ),
]

db.session.add_all(artists)
db.session.commit()

下面的查询应该做你想做的事:

q = Song.query.filter(Song.artist.has(Artist.genres.any(Genre.name == 'rock')))
assert len(q.all()) == 2

答案 1 :(得分:0)

经过一些研究后,我发现了一种解决这个问题的方法,虽然与我想要的有点不同。

首先,为了让所有包含特定类型的艺术家,我执行了:

artists = Artist.query.filter(
    Artist.genres.any(Genre.name.like('genre_name'))).all()

然后,我遍历每个艺术家,并通过使用艺术家作为过滤器中的关键字表达来查询歌曲模型,如下所示:

for a in artist:
    most_common_genre_songs = Song.query.filter_by(artist=a).all()

我不确定是否有一种更有效的方式来进行此次通话,或者是在一个单行中进行(我打赌有),但是现在,这样做。