2012-07-22 66 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它被切换到实际的滤波器方法之前。