SQLAlchemy: filter many-to-one relationship where the one object has a list containing a specific value

生来就可爱ヽ(ⅴ<●) 提交于 2019-12-06 08:03:57

问题


I have some tables like this:

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

So basically, Songs have one Artist. And each Artist can have multiple Genres.

I am trying to get all Songs by any Artist whose list of Genre's contains a Genre by a specific name. I did some researching, and I found something very close:

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

This will sort of work, but not for all cases. For example, the above statement will also return all Songs with Artists who have the Genre 'indie rock'. How can I specify that I don't want the Genre name to be in a list of values, but to be a specific value?

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

is also not possible because it is a keyword expression.

Any ideas?


回答1:


With this test data:

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

Query below should do what you want:

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



回答2:


After some more research, I found out one way to approach this problem, although a bit differently than what I wanted.

First, to get all the Artists that contain a specific Genre, I executed:

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

Then, I iterated through each Artist, and queried the Song model by using the artist as a keyword expression in the filter, like so:

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

I am not sure if there is a more efficient way to make this call, or do it in a one-liner (I'm betting there is), but for now, this will do.



来源:https://stackoverflow.com/questions/34804756/sqlalchemy-filter-many-to-one-relationship-where-the-one-object-has-a-list-cont

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!