|
24 | 24 | BooleanFilter,
|
25 | 25 | ChoiceFilter,
|
26 | 26 | MultipleChoiceFilter,
|
| 27 | + TypedMultipleChoiceFilter, |
27 | 28 | DateFilter,
|
28 | 29 | DateTimeFilter,
|
29 | 30 | TimeFilter,
|
@@ -520,6 +521,143 @@ def test_filter_conjoined_true(self):
|
520 | 521 | expected_pks, item[1], item[0]))
|
521 | 522 |
|
522 | 523 |
|
| 524 | +class TypedMultipleChoiceFilterTests(TestCase): |
| 525 | + |
| 526 | + def test_default_field(self): |
| 527 | + f = TypedMultipleChoiceFilter() |
| 528 | + field = f.field |
| 529 | + self.assertIsInstance(field, forms.TypedMultipleChoiceField) |
| 530 | + |
| 531 | + def test_filtering_requires_name(self): |
| 532 | + qs = mock.Mock(spec=['filter']) |
| 533 | + f = TypedMultipleChoiceFilter() |
| 534 | + with self.assertRaises(TypeError): |
| 535 | + f.filter(qs, ['value']) |
| 536 | + |
| 537 | + def test_conjoined_default_value(self): |
| 538 | + f = TypedMultipleChoiceFilter() |
| 539 | + self.assertFalse(f.conjoined) |
| 540 | + |
| 541 | + def test_conjoined_true(self): |
| 542 | + f = TypedMultipleChoiceFilter(conjoined=True) |
| 543 | + self.assertTrue(f.conjoined) |
| 544 | + |
| 545 | + def test_filtering(self): |
| 546 | + qs = mock.Mock(spec=['filter']) |
| 547 | + f = TypedMultipleChoiceFilter(name='somefield') |
| 548 | + with mock.patch('django_filters.filters.Q') as mockQclass: |
| 549 | + mockQ1, mockQ2 = mock.MagicMock(), mock.MagicMock() |
| 550 | + mockQclass.side_effect = [mockQ1, mockQ2] |
| 551 | + |
| 552 | + f.filter(qs, ['value']) |
| 553 | + |
| 554 | + self.assertEqual(mockQclass.call_args_list, |
| 555 | + [mock.call(), mock.call(somefield='value')]) |
| 556 | + mockQ1.__ior__.assert_called_once_with(mockQ2) |
| 557 | + qs.filter.assert_called_once_with(mockQ1.__ior__.return_value) |
| 558 | + qs.filter.return_value.distinct.assert_called_once_with() |
| 559 | + |
| 560 | + def test_filtering_exclude(self): |
| 561 | + qs = mock.Mock(spec=['exclude']) |
| 562 | + f = TypedMultipleChoiceFilter(name='somefield', exclude=True) |
| 563 | + with mock.patch('django_filters.filters.Q') as mockQclass: |
| 564 | + mockQ1, mockQ2 = mock.MagicMock(), mock.MagicMock() |
| 565 | + mockQclass.side_effect = [mockQ1, mockQ2] |
| 566 | + |
| 567 | + f.filter(qs, ['value']) |
| 568 | + |
| 569 | + self.assertEqual(mockQclass.call_args_list, |
| 570 | + [mock.call(), mock.call(somefield='value')]) |
| 571 | + mockQ1.__ior__.assert_called_once_with(mockQ2) |
| 572 | + qs.exclude.assert_called_once_with(mockQ1.__ior__.return_value) |
| 573 | + qs.exclude.return_value.distinct.assert_called_once_with() |
| 574 | + |
| 575 | + def test_filtering_on_required_skipped_when_len_of_value_is_len_of_field_choices(self): |
| 576 | + qs = mock.Mock(spec=[]) |
| 577 | + f = TypedMultipleChoiceFilter(name='somefield', required=True) |
| 578 | + f.always_filter = False |
| 579 | + result = f.filter(qs, []) |
| 580 | + self.assertEqual(len(f.field.choices), 0) |
| 581 | + self.assertEqual(qs, result) |
| 582 | + |
| 583 | + f.field.choices = ['some', 'values', 'here'] |
| 584 | + result = f.filter(qs, ['some', 'values', 'here']) |
| 585 | + self.assertEqual(qs, result) |
| 586 | + |
| 587 | + result = f.filter(qs, ['other', 'values', 'there']) |
| 588 | + self.assertEqual(qs, result) |
| 589 | + |
| 590 | + def test_filtering_skipped_with_empty_list_value_and_some_choices(self): |
| 591 | + qs = mock.Mock(spec=[]) |
| 592 | + f = TypedMultipleChoiceFilter(name='somefield') |
| 593 | + f.field.choices = ['some', 'values', 'here'] |
| 594 | + result = f.filter(qs, []) |
| 595 | + self.assertEqual(qs, result) |
| 596 | + |
| 597 | + def test_filter_conjoined_true(self): |
| 598 | + """Tests that a filter with `conjoined=True` returns objects that |
| 599 | + have all the values included in `value`. For example filter |
| 600 | + users that have all of this books. |
| 601 | +
|
| 602 | + """ |
| 603 | + book_kwargs = {'price': 1, 'average_rating': 1} |
| 604 | + books = [] |
| 605 | + books.append(Book.objects.create(**book_kwargs)) |
| 606 | + books.append(Book.objects.create(**book_kwargs)) |
| 607 | + books.append(Book.objects.create(**book_kwargs)) |
| 608 | + books.append(Book.objects.create(**book_kwargs)) |
| 609 | + books.append(Book.objects.create(**book_kwargs)) |
| 610 | + books.append(Book.objects.create(**book_kwargs)) |
| 611 | + |
| 612 | + user1 = User.objects.create() |
| 613 | + user2 = User.objects.create() |
| 614 | + user3 = User.objects.create() |
| 615 | + user4 = User.objects.create() |
| 616 | + user5 = User.objects.create() |
| 617 | + |
| 618 | + user1.favorite_books.add(books[0], books[1]) |
| 619 | + user2.favorite_books.add(books[0], books[1], books[2]) |
| 620 | + user3.favorite_books.add(books[1], books[2]) |
| 621 | + user4.favorite_books.add(books[2], books[3]) |
| 622 | + user5.favorite_books.add(books[4], books[5]) |
| 623 | + |
| 624 | + filter_list = ( |
| 625 | + ((books[0].pk, books[0].pk), # values |
| 626 | + [1, 2]), # list of user.pk that have `value` books |
| 627 | + ((books[1].pk, books[1].pk), |
| 628 | + [1, 2, 3]), |
| 629 | + ((books[2].pk, books[2].pk), |
| 630 | + [2, 3, 4]), |
| 631 | + ((books[3].pk, books[3].pk), |
| 632 | + [4, ]), |
| 633 | + ((books[4].pk, books[4].pk), |
| 634 | + [5, ]), |
| 635 | + ((books[0].pk, books[1].pk), |
| 636 | + [1, 2]), |
| 637 | + ((books[0].pk, books[2].pk), |
| 638 | + [2, ]), |
| 639 | + ((books[1].pk, books[2].pk), |
| 640 | + [2, 3]), |
| 641 | + ((books[2].pk, books[3].pk), |
| 642 | + [4, ]), |
| 643 | + ((books[4].pk, books[5].pk), |
| 644 | + [5, ]), |
| 645 | + ((books[3].pk, books[4].pk), |
| 646 | + []), |
| 647 | + ) |
| 648 | + users = User.objects.all() |
| 649 | + |
| 650 | + for item in filter_list: |
| 651 | + f = TypedMultipleChoiceFilter(name='favorite_books__pk', conjoined=True) |
| 652 | + queryset = f.filter(users, item[0]) |
| 653 | + expected_pks = [c[0] for c in queryset.values_list('pk')] |
| 654 | + self.assertListEqual( |
| 655 | + expected_pks, |
| 656 | + item[1], |
| 657 | + 'Lists Differ: {0} != {1} for case {2}'.format( |
| 658 | + expected_pks, item[1], item[0])) |
| 659 | + |
| 660 | + |
523 | 661 | class DateFilterTests(TestCase):
|
524 | 662 |
|
525 | 663 | def test_default_field(self):
|
|
0 commit comments