ListView中使用了paginate_by之后, 并在template中使用了object_list.first这样的变量时, Django抛出了如下错误:

Cannot reorder a query once a slice has been taken.

Advanced Django ListView中,我也提到过同样的问题, 但是两者的原因是不一样的。 上一次的错误是AssertionError at Cannot filter a query once a slice has been taken, 即一旦分片之后,因为SQL已经执行了所以没法再去filter, 而这一次的问题是不能再排序(reorder),之所以说是再排序, 是因为在分页的时候,Django已经进行了一次排序, 这次排序中,如果在model中没有指定排序的方式(ordering), 那么就会默认使用其主键(默认为id)。 那么第二次排序发生在哪里呢?发生在调用object_list.first时, 看源码可以发现,first并不是简单的取了当前queryset的第一个, 而是又一个判断的过程,判断它是否已经排序,若已经排序,则取第一个, 否则按照主键排序再取第一个,这就是第二次排序发生的地方, 究其原因,是因为我在model的定义中没有在Meta中指定排序字段, 即ordering.如果指定了该字段,则可以避免上面的问题。

在找资料的过程中,我发现了一种可以避免第二次排序的方式, 就是不用object_list.first这样的形式,而用object_list.0, 这个就不管顺序了,直接取出目前最前面那个,这样同样也不会出现第二次排序, 也不会报错了。



blog comments powered by Disqus

Published

07 January 2016

Category

tech_world

Tags