在使用Flask在Python应用程序中充分利用SQLAlchemy的潜力时,我遇到了麻烦,并且了解了多对一关系的工作方式以及如何利用它们。
我正在使用一个具有以下格式的歌曲和歌手数据库:
CREATE TABLE song (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(64),
artist_id INTEGER
);
CREATE TABLE artist (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(64)
);
或者,通过Python SQLAlchemy,它看起来像这样:
class Song(db.Model):
id = db.Column(db.Integer, primary_key=True)
kws_id = db.Column(db.String(32))
name = db.Column(db.String(255))
artist_id = db.Column(db.Integer, db.ForeignKey('artist.id'))
artist = db.relationship('Artist', backref='songs', foreign_keys=[artist_id])
class Artist(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255))
song.artist_id
是指列artist.id
;这是一种多对一的关系,许多歌曲可以引用一位歌手。
我希望我可以将结果显示为:
artist:
song1
song2
song3
artist2:
song4
song5
...
或者,在JSON中,大致为:{artist: [song1, song2, song3], artist2: [song4, song5]}
。 (最终目标是通过Flask在AJAX分页响应中返回此值)。
基本上,我希望我能有一个艺术家的字典,其中包含一些歌曲列表;并且如果我要搜索特定的歌曲,它将返回一个结构相同的字典,只包含匹配的歌曲。
如果我想运行查询并选择所有艺术家的所有歌曲,我可以简单地查询所有艺术家,然后使用backref
属性来获取所有歌曲。
artists = Artist.query.all()
for artist in artists:
songs = artist.songs
# construct json: artist => song
但是,我正在努力寻找一种过滤歌曲的解决方案,同时保持其返回的数据结构完整。
我可以在Python中手动完成此操作,只需让查询返回原始列表即可:
artists = Artist.query.all()
for artist in artists:
for song in artist.songs
if song == "search": # or starts with, or something else
# this is a song we want to match
我可以从中建立一个json响应,但是感觉就像我在使用SQL像一个列表而不是一种查询语言一样-没有充分利用它并牺牲了速度。
我可以查询艺术家表并按歌曲进行过滤:
q = Artist.query
q = q.join(Artist.songs)
q = q.filter(Song.name.like("%" + search + "%"))
# q is a query which will return a list of artists
# each artist has the aforementioned backref for a list of song
此搜索,但返回每个结果的整个艺术家。例如,在此数据集中:
{artist1: [song1, song2, song3], artist2: [song4, song5, song6]}
搜索"song1"
将返回artist1,而backref将包含artist1的所有歌曲。我构造的json将是{artist1: [song1, song2, song3]}
而不是{artist1: song1}
,这是理想的选择。
我不确定是否有办法改进此功能以过滤掉我不想要的歌曲,或者查询歌手表是否是错误的方法。
我探索的另一种方法是在歌曲列表中进行查询:
q = Song.query
q = q.join(Artist)
q = q.filter(Song.name.like("%" + i + "%"))
但是,在这种情况下,q.all()是歌曲列表,尽管它们包含歌手姓名,但它们并未按歌手姓名“分组”(我必须做手工才能制成一个列表) (对于每位歌手,具有相同歌手名称的所有歌曲)。即我能做的最好的事情是:
[song1, song2, song3, song4]
或者,
artists = {}
for song in songs:
artists[song.artist.name].append(song.name)
# or initialize artists[song.artist.name] = [song.name,]
我尝试过的所有这些解决方案似乎在Python程序的一部分上都涉及某种手动任务,我觉得应该有一种方法可以改善我的解决方案以更加依赖SQL。 是否有一种查询表并返回按多对一关系的“一个”部分分组/列出的组的方法,只匹配符合特定条件的“许多”?