【问题标题】:Python TastyPie - Custom manager methods as filters?Python TastyPie - 自定义管理器方法作为过滤器?
【发布时间】:2012-07-21 04:30:37
【问题描述】:

我有一个 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)

我正在尝试让我的资源模型 (AdvertResource) 通过 GET 参数公开 inside_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

但这会引发错误“无法将关键字 'box' 解析为字段...”。

是否可以将自定义管理器中的方法公开给 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 现在过滤边界框内的所有结果。

【问题讨论】:

    标签: python django tastypie geodjango


    【解决方案1】:

    上面的代码有几个问题。

    首先,是的,您可以将任何自定义管理器暴露给任何东西,无论您是否使用它。仅当您将其默认管理器替换为您自己的版本时,才能通过 Advert.objects 访问您上面定义的 AdvertManager。

    其次,您希望在 AdvertResource 上公开 sweetpie 过滤的方式与过滤的实际工作方式是正交的。

    所有过滤器都作为 <field_name>__<filter_name>=<value_or_values> 形式的实际 ORM 过滤器应用。由于在您的示例中,您使用的是 box=<number>,<number>,...,<number>tastepie 将其分解为 box__exact=... 并尝试在 AdvertResource 中找到 box 字段并按预期失败。

    如果您的广告有一个名为 location 的字段,您可以添加 withinbox 作为该字段的过滤器,并按:location__withinbox=<values> 过滤。

    如果您想保留原来的方法,您必须自己从 request.GET 字典中解析框过滤器,然后将它们传递给您自己在 AdvertResource 中覆盖的 obj_getobj_get_list 版本。

    最后,在扩展 build_filters 时,您只是在 Tastypie 过滤器和 ORM 过滤器之间进行映射。在您的示例中,您将对象作为过滤器返回;而是简单地将其定义为:

    { 'withinbox' : 'point__within' }
    

    并将值列表转换为apply_filters 内的Polygon,然后再传递给实际的过滤方法。

    【讨论】:

      猜你喜欢
      • 2012-02-25
      • 1970-01-01
      • 1970-01-01
      • 2012-03-29
      • 1970-01-01
      • 2018-07-15
      • 2016-11-15
      • 2013-07-20
      • 2015-08-12
      相关资源
      最近更新 更多