2016-07-01 51 views
2

所以我有這樣的查詢集:優化Django的計數()方法

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被註解爲ratingdistance,並將它也放入count查詢中。所以我想問題是 - 在註釋它之前,我怎樣「重置」這個查詢集到狀態?

編輯:

好吧,看來我的問題是不完整的。我也有定義的RestaurantsList觀點如下:

class RestaurantList(generics.ListAPIView): 
    def get_queryset(self): 
     return Restaurant.objects.get_list(self._lng, self._lat) 

我接過來一看進入Django的REST的架構的內臟,我可以看到這一點:

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方法返回的查詢集以及由distancerating註釋的查詢集最終包含在計數查詢中。仍然沒有解決這個...

回答

0

通過Django的REST的框架代碼瀏覽後,我想出了這個主意:

  1. 覆蓋默認Paginator類對於給定的觀點:

    class RestaurantList(generics.ListAPIView): 
        pagination_class = custom.LimitOffsetPagination 
    
  2. 創建自定義分頁類:

    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]) 
    
  3. 覆蓋意見paginator屬性:

    @property 
    def paginator(self): 
        paginator = super().paginator 
        if not paginator.was_initialized(): 
         paginator.set_raw_queryset_for_count(Restaurant.objects.all()) 
        return paginator 
    

現在count查詢會看起來有點更友好

SELECT COUNT(*) AS "__count" FROM "restaurants_restaurant"