在Django中过滤层次结构的有效方法?

时间:2017-02-15 02:06:56

标签: python django tree hierarchical-data

我有一个简单的分层模型,3只乌龟高。我们说艺术家,专辑,歌曲。在我的视图中过滤生成树的有效方法是什么?

要让艺术家/专辑/歌曲树传递到我的模板,使用任意条件进行过滤,我现在正在执行以下操作:

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的优化从彩虹的另一边提供一些神奇的缓存之外,必须有一种更有效的方法,而不是一直查询每个级别的叶子。

也许创造一棵完整的树(例如每个艺术家和专辑,不检查树叶),然后修剪无叶树枝?或者我应该看{{3}}?

对于额外的积分,一些实际的测试/基准/报告将是受欢迎的。丹科!

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)

1 个答案:

答案 0 :(得分:1)

我最终得到了以下内容:

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')左右,但每个乌龟有一个额外的查询。