2012-07-22 62 views
3

我有一個像這樣的經理模型的GeoDjango項目;Python TastyPie - 自定義管理器方法作爲過濾器?

class AdvertManager(models.GeoManager): 

    def within_box(self, x0, y0, x1, y1): 
     geometry = Polygon.from_bbox((x0, y0, x1, y1,)) 
     return self.filter(point__within=geometry) 

我試圖讓我的資源模型(AdvertResource)通過一個GET參數來暴露within_box函數,就像;

http://127.0.0.1:8000/api/v1/advert/?format=json&box=51.623349,-3.25362,51.514195,-3.4754133 

我開始在資源模型上寫這樣一個build_filters方法;

def build_filters(self, filters=None): 
     if not filters: 
      filters = {} 
     orm_filters = super(AdvertResource, self).build_filters(filters) 

     if 'box' in filters: 
      points = [float(p.strip()) for p in filters['box'].split(',')] 
      orm_filters = {'box': Advert.objects.within_box(*points).all()} 

     return orm_filters 

但是,這會引發錯誤「無法解析關鍵字」框'入字段...「。

是否有可能將自定義管理器的方法暴露給api url?

編輯 - 我現在用下面的解決方案解決了這個問題。

class AdvertResource(ModelResource): 

    longitude = fields.FloatField(attribute='longitude', default=0.0) 
    latitude = fields.FloatField(attribute='latitude', default=0.0) 
    author = fields.ForeignKey(UserResource, 'author') 

    def build_filters(self, filters=None): 
     """ 
     Build additional filters 
     """ 
     if not filters: 
      filters = {} 
     orm_filters = super(AdvertResource, self).build_filters(filters) 

     if 'point__within_box' in filters: 
      points = filters['point__within_box'] 
      points = [float(p.strip()) for p in points.split(',')] 
      orm_filters['within_box'] = points 

     return orm_filters 

    def apply_filters(self, request, applicable_filters): 
     """ 
     Apply the filters 
     """ 
     if 'within_box' in applicable_filters: 
      area = applicable_filters.pop('within_box') 
      poly = Polygon.from_bbox(area) 
      applicable_filters['point__within'] = poly 
     return super(AdvertResource, self).apply_filters(request, 
                 applicable_filters) 

現在,這意味着該請求http://127.0.0.1:8000/api/v1/advert/?format=json&point__within_box=51.623349,-3.25362,51.514195,-3.4754133現在過濾邊框範圍內的結果。

回答

4

上面的代碼有幾個問題。

首先,是的,你可以將任何自定義管理器暴露給任何東西,無論你是否使用它。只有使用您自己的版本替換了默認管理器,您才能通過Advert.objects訪問您在上面定義的AdvertManager。其次,您想在AdvertResource上公開tastypie過濾的方式與過濾實際如何工作是正交的。

所有過濾器都以<field_name>__<filter_name>=<value_or_values>的形式應用爲實際的ORM過濾器。由於在您的示例中,您正在使用box=<number>,<number>,...,<number> tastypie分解爲box__exact=...,並嘗試在AdvertResource中找到box字段,並且按預期失敗。

如果你的廣告有一個名爲場location你可以添加withinbox作爲該領域,並通過過濾器的過濾器:location__withinbox=<values>

如果您想保留原來的方法,您必須從request.GET字典中解析框篩選器,然後將它們傳遞給您在AdvertResource中的obj_getobj_get_list的重寫版本。

最後,在擴展build_filters時,您只是在Tastypie過濾器和ORM過濾器之間進行映射。在你的例子中,你將返回對象作爲過濾器;而不是簡單地將其定義爲:

{ 'withinbox' : 'point__within' } 

和值的列表轉換成Polygonapply_filters它被切換到實際的濾波器方法之前。