具有多个查找的Django RestFramework方法过滤器

时间:2017-09-14 19:57:12

标签: django django-rest-framework django-filter

我有两个定义如下的模型:

class House(models.Model):
    # all the fields here

    def capacity(self):
        capacity = 0
        for room in self.rooms.all():
            capacity += room.capacity
        return capacity

class Room(models.Model):
    house = models.ForeignKey(
        House, 
        on_delete=models.CASCADE,
        related_name='rooms'
    )
    capacity = models.PositiveIntegerField(
        default=1
    )

我需要的是使用restframework中的所有数字查找(lt,lte,gt,gt,gte,range,exact)按容量过滤房屋,我这样做了:

import rest_framework_filters as filters

def capacity_filter(qs, name, value):
    # filter implementation here

class HouseFilter(filters.FilterSet):
    capacity = filters.NumberFilter(
        method=capacity_filter
    )

    class Meta:
        fields = ('capacity',)

class HouseViewSet(viewsets.ModelViewSet):
    filter_class = HouseFilter
    # other attrs here

它的工作原理只是为了确切的值,我不能使用__gt或__lt进行过滤,我尝试使用NumberFilter中的 lookup_expr 参数,但不能正常工作。

2 个答案:

答案 0 :(得分:0)

答案 1 :(得分:0)

您可以使用partial来生成具有相应查找的可调用对象。例如,

def capacity_filter(qs, name, value, lookup_expr):
    # implementation

class HouseFilter(filters.FilterSet):
    capacity = filters.NumberFilter(
        name='capacity',
        method=partial(capacity_filter, lookup_expr='exact'))
    capacity__lt = filters.NumberFilter(
        name='capacity',
        method=partial(capacity_filter, lookup_expr='lt'))
    ...

也就是说,您应该使用注释来计算房间容量,因为它将在数据库中而不是在内存中执行计算。这也可以让你放弃在这里使用method参数。

# the query you want to construct
queryset = House.objects \
    .annotate(capacity=Sum('rooms__capacity'))
    .filter(...)

# filterset
class HouseFilter(FilterSet):
    capacity = filters.NumberFilter(
        name='capacity', lookup_expr='exact', 
        label='Capacity is equal to')
    capacity__lt = filters.NumberFilter(
        name='capacity', lookup_expr='lt', 
        label='Capacity is less than')

    @property
    def qs(self):
        # Only annotate capacity if filtering by capacity
        if not hasattr(self, '_qs') and self.form.is_valid() \
           and any(f.startswith('capacity') for f in self.form.cleaned_data):
            self.queryset = self.queryset \
                .annotate(capacity=Sum('rooms__capacity'))

        return super(HouseFilter, self).qs