【问题标题】:SQLAlchemy: filter many-to-one relationship where the one object has a list containing a specific valueSQLAlchemy:过滤多对一关系,其中一个对象具有包含特定值的列表
【发布时间】:2016-01-15 05:10:22
【问题描述】:

我有一些这样的表:

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

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

有什么想法吗?

【问题讨论】:

  • 不应该是Song.query.filter(Artist.genres.any(Genre.name == 'rock'))吗? (比较而不是赋值)
  • @Wash 是对的,但即使in_(['rock']) 也不应该真正匹配indie rock。也许你的Artistrockindie rock 两种类型。
  • @van 是的,我认为它也不应该匹配。我检查了我的数据库......艺术家没有摇滚,但有很多名称中带有“摇滚”的流派,比如“独立摇滚”。仍然困惑为什么它匹配。
  • @Wash 谢谢,我稍后再试。通常你可以使用一个表达式,比如 Artist.query.filter_by(name='whatever'),所以我什至没有考虑使用比较。这个 sqlalchemy 业务还是新手。
  • @van,我尝试了您的查询,但我仍然收到在他们的流派列表中包含“独立摇滚”但没有“摇滚”的艺术家。而且我检查了艺术家没有两种类型。

标签: python sqlalchemy flask-sqlalchemy


【解决方案1】:

有了这个测试数据:

# 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

【讨论】:

  • 这正是我想要的,谢谢。给你一个快速的问题,我还有另一个表,用户,它与歌曲有一对多的关系,我现在正在寻找增加你建议的查询来过滤属于特定用户的歌曲。此查询有效Song.query.filter(Song.artist.has( Artist.genres.any(Genre.name == 'genre'))).filter( Song.user == some_user_object).all() 交换过滤器语句会对性能产生什么样的影响?
  • 我不认为任何现代 RDBMS 都会有任何性能差异。
  • 很好的解决方案。
【解决方案2】:

经过更多研究,我找到了解决此问题的一种方法,尽管与我想要的有点不同。

首先,为了获取包含特定流派的所有艺术家,我执行了:

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

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

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

我不确定是否有更有效的方法来进行此调用,或者在单线中进行(我打赌有),但现在可以这样做。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-17
    • 2014-03-12
    • 1970-01-01
    • 1970-01-01
    • 2017-02-06
    • 2018-03-14
    • 1970-01-01
    相关资源
    最近更新 更多