问题
I'm having a bit of trouble with using SQLAlchemy to its full potential in a Python application using Flask, and understanding how many-to-one relationships work and how they can be utilized.
I'm working with a database of songs and artists with a schema that looks like this:
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)
);
Or, through Python SQLAlchemy, it looks like this:
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
refers to the column artist.id
; this is a many-to-one relationship where many songs can refer to one artist.
I'm hoping that I can have the results displayed as:
artist:
song1
song2
song3
artist2:
song4
song5
...
Or, in JSON, roughly: {artist: [song1, song2, song3], artist2: [song4, song5]}
. (The end goal is to return this in an AJAX paginated response through Flask).
Basically, I'm hoping that I can have a dict of artists which contains a list of songs; and if I were to run a search on a specific song, it would return a dict of the same structure, with only matching songs.
If I want to run a query and select all songs from all artists, I can simply query all artists and then use the backref
attribute to get all of the songs.
artists = Artist.query.all()
for artist in artists:
songs = artist.songs
# construct json: artist => song
However, I'm struggling with a solution for filtering songs while keeping the data structure it returns intact.
Some solutions I've tried so far:
I could do it manually in Python, and simply have the query return the original list:
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
I could build a json response from this, but this feels like I'm using SQL like a list rather than a query language-- not using it to its fullest potential and sacrificing speed.
I could query the artist table and filter by song:
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
This searches, but returns the entire artist for every result. For example, in this dataset:
{artist1: [song1, song2, song3], artist2: [song4, song5, song6]}
A search for "song1"
would return artist1, and the backref will contain all of the songs for artist1. The json I construct would be {artist1: [song1, song2, song3]}
rather than {artist1: song1}
, which would be ideal.
I'm not sure if there's a way to improve this in order to filter out songs I don't want, or if querying the artist table is the wrong way to go.
Another method I've explored is doing the query on the song list:
q = Song.query
q = q.join(Artist)
q = q.filter(Song.name.like("%" + i + "%"))
However, in this case, q.all() is a list of songs, and although they contain the artist name, they are not "grouped" by the artist name (I would have to do manual work to make a list of all songs with the same artist name, for every artist). i.e. the best I could do is:
[song1, song2, song3, song4]
Or,
artists = {}
for song in songs:
artists[song.artist.name].append(song.name)
# or initialize artists[song.artist.name] = [song.name,]
All of these solutions I've tried seem to involve some sort of manual task on part of the Python program, and I feel there should be a way to improve my solution to rely more heavily on SQL. Is there a way to query a table and return groups, grouped/listed by the "one" part of the many-to-one relationship, only matching the "many"s which match a certain criteria?
来源:https://stackoverflow.com/questions/51013108/sql-query-to-group-records-and-return-them-as-a-list-in-a-many-to-one-relationsh