Skip to content

Commit

Permalink
Add lookup_expr to MultipleChoiceFilter (#1054)
Browse files Browse the repository at this point in the history
This was requested in #49 (back when lookup_expr was called
lookup_type). This is a minor change, with the added advantage of making
``MultipleChoiceFilter`` perform more like other Filter classes, down to
the explicit ``__exact`` filter.

As the documentation only mentions ``lookup_expr`` to be available on
Filter classes in general, and doesn't mention ``MultipleChoiceFilter``
to be different, no documentation change is necessary.
  • Loading branch information
rixx authored and carltongibson committed Mar 8, 2019
1 parent 3027c9e commit 9d921c7
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 2 deletions.
7 changes: 5 additions & 2 deletions django_filters/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,10 +253,13 @@ def filter(self, qs, value):
return qs.distinct() if self.distinct else qs

def get_filter_predicate(self, v):
name = self.field_name
if name and self.lookup_expr != 'exact':
name = LOOKUP_SEP.join([name, self.lookup_expr])
try:
return {self.field_name: getattr(v, self.field.to_field_name)}
return {name: getattr(v, self.field.to_field_name)}
except (AttributeError, TypeError):
return {self.field_name: v}
return {name: v}


class TypedMultipleChoiceFilter(MultipleChoiceFilter):
Expand Down
15 changes: 15 additions & 0 deletions tests/test_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,21 @@ def test_filtering_exclude(self):
qs.exclude.assert_called_once_with(mockQ1.__ior__.return_value)
qs.exclude.return_value.distinct.assert_called_once_with()

def test_filtering_with_lookup_expr(self):
qs = mock.Mock(spec=['filter'])
f = MultipleChoiceFilter(field_name='somefield', lookup_expr='icontains')
with mock.patch('django_filters.filters.Q') as mockQclass:
mockQ1, mockQ2 = mock.MagicMock(), mock.MagicMock()
mockQclass.side_effect = [mockQ1, mockQ2]

f.filter(qs, ['value'])

self.assertEqual(mockQclass.call_args_list,
[mock.call(), mock.call(somefield__icontains='value')])
mockQ1.__ior__.assert_called_once_with(mockQ2)
qs.filter.assert_called_once_with(mockQ1.__ior__.return_value)
qs.filter.return_value.distinct.assert_called_once_with()

def test_filtering_on_required_skipped_when_len_of_value_is_len_of_field_choices(self):
qs = mock.Mock(spec=[])
f = MultipleChoiceFilter(field_name='somefield', required=True)
Expand Down

0 comments on commit 9d921c7

Please sign in to comment.