在 Django 中过滤层次结构的有效方法?
Efficient way to filter a hierarchical structure in Django?
我有一个简单的分层模型,3 只海龟高。让我们说艺术家,专辑,歌曲。在我看来过滤结果树的有效方法是什么?
为了让 Artist/Album/Song 树传递给我的模板,用任意条件过滤,我目前正在做类似的事情:
for current_artist in Artist.objects.filter(album__song__genre='funkadelic mariachi').distinct():
yield current_artist
for current_album in Album.objects.filter(song__genre='funkadelic mariachi').distinct():
yield current_album
for current_song in Song.objects.filter(genre='funkadelic mariachi'):
yield current_song
yield 'End of album'
yield 'End of artist'
但我很确定肯定有一种比在每个级别一直查询到叶子更有效的方法,除非 distinct() 和 Django 的优化从彩虹的另一边提供了一些神奇的缓存。
也许创建一棵完整的树(v.g。每个艺术家和专辑,不检查树叶),然后修剪光秃秃的树枝?或者我应该看看 select_related()?
如需加分,欢迎提供一些实际的 test/benchmark/write-up。蛋客!
P.S:我知道 django-mptt 的优点,但它太过分了。
详细的模型并不重要,因为我正在寻找一个通用的解决方案,但它可能是这样的:
class Artist:
name = models.CharField(max_length=200)
class Album:
name = models.CharField(max_length=200)
artist = models.ForeignKey(Artist, on_delete=models.CASCADE)
class Song:
name = models.CharField(max_length=200)
album= models.ForeignKey(Album, on_delete=models.CASCADE)
genre = models.CharField(max_length=200)
我得到以下结果:
filters = { "genre": 'funkadelic mariachi' }
artist = None
album = None
result = []
# select_related() fetches our chosen songs, and their albums and artists, in a single query
for song in Song.objects.select_related(
'album__artist').filter(**filters):
if album != song.album and album != None:
result.append('End of Album')
if artist != song.album.artist:
if artist != None:
result.append('End of Artist')
artist = song.album.artist
result.append(artist)
if album != song.album:
album = song.album
result.append(album)
result.append(song)
if result:
result.append('End of Album')
result.append('End of Artist')
不是很漂亮,但效率更高。也许 prefetch_related() 允许保留三个循环,使用 Prefetch('artist', to_attr='filtered_artists') 左右,但每个海龟有一个额外的查询。
我有一个简单的分层模型,3 只海龟高。让我们说艺术家,专辑,歌曲。在我看来过滤结果树的有效方法是什么?
为了让 Artist/Album/Song 树传递给我的模板,用任意条件过滤,我目前正在做类似的事情:
for current_artist in Artist.objects.filter(album__song__genre='funkadelic mariachi').distinct():
yield current_artist
for current_album in Album.objects.filter(song__genre='funkadelic mariachi').distinct():
yield current_album
for current_song in Song.objects.filter(genre='funkadelic mariachi'):
yield current_song
yield 'End of album'
yield 'End of artist'
但我很确定肯定有一种比在每个级别一直查询到叶子更有效的方法,除非 distinct() 和 Django 的优化从彩虹的另一边提供了一些神奇的缓存。
也许创建一棵完整的树(v.g。每个艺术家和专辑,不检查树叶),然后修剪光秃秃的树枝?或者我应该看看 select_related()?
如需加分,欢迎提供一些实际的 test/benchmark/write-up。蛋客!
P.S:我知道 django-mptt 的优点,但它太过分了。
详细的模型并不重要,因为我正在寻找一个通用的解决方案,但它可能是这样的:
class Artist:
name = models.CharField(max_length=200)
class Album:
name = models.CharField(max_length=200)
artist = models.ForeignKey(Artist, on_delete=models.CASCADE)
class Song:
name = models.CharField(max_length=200)
album= models.ForeignKey(Album, on_delete=models.CASCADE)
genre = models.CharField(max_length=200)
我得到以下结果:
filters = { "genre": 'funkadelic mariachi' }
artist = None
album = None
result = []
# select_related() fetches our chosen songs, and their albums and artists, in a single query
for song in Song.objects.select_related(
'album__artist').filter(**filters):
if album != song.album and album != None:
result.append('End of Album')
if artist != song.album.artist:
if artist != None:
result.append('End of Artist')
artist = song.album.artist
result.append(artist)
if album != song.album:
album = song.album
result.append(album)
result.append(song)
if result:
result.append('End of Album')
result.append('End of Artist')
不是很漂亮,但效率更高。也许 prefetch_related() 允许保留三个循环,使用 Prefetch('artist', to_attr='filtered_artists') 左右,但每个海龟有一个额外的查询。