Filter repeated key parameters as NumberField
Enorio opened this issue · comments
Given the following Model and Filter:
class Bar(models.Model):
foo = models.ForeignKey(...)
class BarFilter(FilterSet):
class Meta:
model = Bar
fields = ['foo']
filter_overrides = {
models.ForeignKey: {
'filter_class': filters.NumberFilter
}
}
I need to filter Bar given multiple Foo foreign key ids.
For example GET .../bar/?foo=1&foo=2&foo=3
From what I tested, the endpoint will only filter foo=3
, I'm guessing always the last value.
I've done some debug in the code, and there is a step somewhere that converts the list received in only one value.
I've tried adding a 'lookup_expr': 'in'
as an extra, but raises an exception saying 'decimal.Decimal' object is not iterable
I also used the ModelMultipleChoiceFilter
, but I don't want to return an error saying that some id doesn't exist.
Basically, what I want is given the endpoint, I can filter Foreign Keys from their id, and if some value doesn't exist, ignores it. For example:
foo_a = Foo(id=1)
foo_b = Foo(id=2)
foo_c = Foo(id=3)
bar_a = Bar(foo=foo_a)
bar_b = Bar(foo=foo_b)
bar_c = Bar(foo=foo_c)
GET .../bar/ = [bar_a, bar_b, bar_c]
GET .../bar/?foo=1&foo=2 = [bar_a, bar_b]
GET .../bar/?foo=4 = []
I'm using django-filter 21.1 and django-rest-framework, if it helps.
I also used the ModelMultipleChoiceFilter, but I don't want to return an error saying that some id doesn't exist.
OK, so then you'll want to define a widget that returns a list of items from the request.GET
QueryDict
:
class ManyWiget(Widget):
def value_from_datadict(self, data, files, name):
return data.getlist(name)
Then you'll want a Form field that uses that widget, again expecting a list.
class ManyIntField(Field):
widget = ManyWidget
def to_python(self, value):
if not value:
return []
elif not isinstance(value, (list, tuple)):
raise ValidationError(
self.error_messages["invalid_list"], code="invalid_list"
)
return [int(val) for val in value]
You'll need to think about validation there; you don't want to accept just any data.
Then you'll need a filter to use the field:
class ManyIntFilter(Filter):
field = ManyIntField
lookup_expr = "in"
But with that you'll be able to filter on many items, without erroring just because an ID doesn't exist.