SQL Query to group records and return them as a list in a many-to-one relationship

不想你离开。 提交于 2021-01-29 08:46:00

问题


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

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