我有一个GeoDjango项目,它有一个像这样的经理模型;
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)
我正在尝试通过GET参数获取我的资源模型(AdvertResource)来公开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
现在过滤了边界框内的所有结果。
答案 0 :(得分:4)
上面的代码存在一些问题。
首先,是的,您可以将任何自定义管理器暴露给任何东西,无论您是否使用它tastypie。只有在您使用自己的版本替换默认管理器时,才可以通过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_get
和obj_get_list
的重写版本。
最后,在扩展build_filters
时,您只在Tastypie过滤器和ORM过滤器之间进行映射。在您的示例中,您将对象作为过滤器返回;而是简单地将其定义为:
{ 'withinbox' : 'point__within' }
并将值列表转换为Polygon
中的apply_filters
,然后再转换为实际的过滤方法。