所以我有这个查询集:
from django.contrib.gis.db.models.query import GeoQuerySet
from django.db import models as base_models
class RestaurantsQuerySet(GeoQuerySet):
def get_list(self, lng, lat):
reference_point = Point(lng, lat, srid=SRID)
return self.annotate(rating=models.Avg('comments__rating'))\
.annotate(distance=Distance('location', reference_point))
def count(self):
return self.values('id').aggregate(count=base_models.Count('id'))['count']
我认为查询看起来像:
SELECT COUNT("__col1")
FROM (
SELECT "restaurants_restaurant"."id" AS "__col1"
FROM "restaurants_restaurant"
GROUP BY "restaurants_restaurant"."id") subquery
而django orm创造了这个小怪物:
SELECT COUNT("__col1")
FROM (
SELECT "restaurants_restaurant"."id" AS Col1, "restaurants_restaurant"."id" AS "__col1"
FROM "restaurants_restaurant"
LEFT OUTER JOIN "comments_comment" ON ("restaurants_restaurant"."id" = "comments_comment"."restaurant_id")
GROUP BY "restaurants_restaurant"."id", ST_Distance_Sphere("restaurants_restaurant"."location",
ST_GeomFromEWKB('\x0101000020e61000003eb555a41d2d4b405a338d81d0a73240'::bytea
))) subquery
要调用的第一个方法是get_list
。看起来好像django会记得"该调用以及qs已使用rating
和distance
进行注释,并将其放入count
查询中。所以我想问题是 - 我如何"重置"这个查询集在注释之前的状态?
修改
好的,似乎我的问题还不完整。我还有RestaurantsList
视图定义如下:
class RestaurantList(generics.ListAPIView):
def get_queryset(self):
return Restaurant.objects.get_list(self._lng, self._lat)
我看了一下django-rest-framework的内脏,我可以看到:
class ListModelMixin(object):
"""
List a queryset.
"""
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
所以看起来它始终使用从get_queryset
方法返回的查询集,因为它使用distance
和rating
进行注释,最终会包含在计数查询。仍然没有解决方案...
答案 0 :(得分:0)
在浏览了django-rest-framework代码后,我提出了这个想法:
覆盖给定视图的默认Paginator
类:
class RestaurantList(generics.ListAPIView):
pagination_class = custom.LimitOffsetPagination
创建自定义分页类:
class LimitOffsetPagination(pagination.LimitOffsetPagination):
def __init__(self):
self._countable_queryset = None
self._was_counted = False
def was_initialized(self):
return self._countable_queryset is not None
def set_raw_queryset_for_count(self, queryset: QuerySet):
self._countable_queryset = queryset
def paginate_queryset(self, queryset, request, view=None):
self.limit = self.get_limit(request)
if self.limit is None:
return None
self.offset = self.get_offset(request)
self.count = self._countable_queryset.count()
self.request = request
if self.count > self.limit and self.template is not None:
self.display_page_controls = True
return list(queryset[self.offset:self.offset + self.limit])
覆盖视图paginator
属性:
@property
def paginator(self):
paginator = super().paginator
if not paginator.was_initialized():
paginator.set_raw_queryset_for_count(Restaurant.objects.all())
return paginator
现在计数查询看起来会更友好
SELECT COUNT(*) AS "__count" FROM "restaurants_restaurant"