Django评论应用线程化排序:如何实现最早评论优先的树形展示?
解决Django树形评论的排序问题
嘿,我明白你现在遇到的排序困扰了——你用树形路径存储评论,想要的是父评论先展示,然后按顺序展示它的子评论,最后是其他父评论,但当前的order_by('-path')给出的顺序完全反过来了。让我一步步帮你修正:
为什么当前排序不对?
你的path字段是字符串类型(比如"1"、"1,2"、"3"、"1,4"),当用-path降序排序时,Django会按照字符串的ASCII码逆序来排列:
- "3"的第一个字符'3'比'1'大,所以排在最前面
- "1,4"的第三个字符'4'比"1,2"的'2'大,所以在"1,2"前面
这就导致你看到的顺序是[3]、[1]、[1,4]、[1,2],和期望的完全相反。
解决方案1:简单场景(评论ID为个位数)
如果你的评论ID不会超过个位数(或者说path里的每个数字都是1位),直接把排序改成升序即可:
comment_list = Comments.objects.order_by('path')
这样字符串升序排序会得到"1" < "1,2" < "1,4" < "3",正好匹配你想要的[1]、[1,2]、[1,4]、[3]顺序。
解决方案2:通用场景(支持任意位数的评论ID)
如果评论ID可能是多位数(比如10、100),字符串排序就会出问题——比如"1,10"会被排在"1,2"前面(因为字符串比较时'1' < '2')。这时候我们需要把path转换成整数数组再排序:
首先定义一个自定义的Func来处理路径转换:
from django.db.models import Func, IntegerField class SplitPathToIntArray(Func): function = 'string_to_array' # 将字符串按逗号分割后转成整数数组(PostgreSQL语法) template = "%(function)s(%(expressions)s, ',')::integer[]" output_field = IntegerField()
然后用这个Func来排序:
comment_list = Comments.objects.order_by(SplitPathToIntArray('path'))
这样Django会把path转换成整数数组(比如[1]、[1,2]、[1,4]、[3]),然后按数组的自然顺序排序,不管数字位数多少都能得到正确的层级顺序。
额外说明
从你的例子来看,path里的数字是按评论创建顺序递增分配的,所以按路径数组排序正好能实现“父评论先展示,子评论按创建顺序跟在父评论后面”的效果。如果后续你想让子评论按其他规则(比如点赞数)排序,可以在order_by里追加对应的字段,比如:
# 先按路径排序,同路径下按点赞数降序 comment_list = Comments.objects.order_by(SplitPathToIntArray('path'), '-likes')
内容的提问来源于stack exchange,提问作者Shreenath Gandhi




