def test_manytomany_filter_invalid_query(self): self.do_invalid_query_param_test( lambda params: ManyToManyFilter('authors', Book, params), MultiValueDict({'authors': ['xxx']})) self.do_missing_related_object_test( lambda params: ManyToManyFilter('authors', Book, params), MultiValueDict({'authors': ['10000']}))
def test_manytomany_filter(self): """ Tests for ManyToManyFilter """ filter1 = ManyToManyFilter('authors', Book, MultiValueDict()) qs = Book.objects.all() # ManyToMany can have 'drill down', i.e. multiple levels of filtering, # which can be removed individually. # First level: choices = filter1.get_choices(qs) # Check list is full, and in right order self.assertEqual([unicode(v) for v in Author.objects.all()], [choice.label for choice in choices]) for choice in choices: # For single choice, param will be single integer: param = int(choice.params[filter1.query_param]) # Check the count count = Book.objects.filter(authors=int(param)).count() self.assertEqual(choice.count, count) author = Author.objects.get(id=param) # Check the label self.assertEqual(unicode(author), choice.label) # Check the filtering filter2 = ManyToManyFilter('authors', Book, choice.params) qs_filtered = filter2.apply_filter(qs) self.assertEqual(len(qs_filtered), choice.count) for book in qs_filtered: self.assertTrue(author in book.authors.all()) # Check we've got a 'remove link' on filtered. choices_filtered = filter2.get_choices(qs) self.assertEqual(choices_filtered[0].link_type, FILTER_REMOVE)
def test_manytomany_filter(self): """ Tests for ManyToManyFilter """ filter1 = ManyToManyFilter('authors', Book, MultiValueDict()) qs = Book.objects.all() # ManyToMany can have 'drill down', i.e. multiple levels of filtering, # which can be removed individually. # First level: choices = filter1.get_choices(qs) # Check list is full, and in right order self.assertEqual([text_type(v) for v in Author.objects.all()], [choice.label for choice in choices]) for choice in choices: # For single choice, param will be single integer: param = int(choice.params[filter1.query_param]) # Check the count count = Book.objects.filter(authors=int(param)).count() self.assertEqual(choice.count, count) author = Author.objects.get(id=param) # Check the label self.assertEqual(text_type(author), choice.label) # Check the filtering filter2 = ManyToManyFilter('authors', Book, choice.params) qs_filtered = filter2.apply_filter(qs) self.assertEqual(len(qs_filtered), choice.count) for book in qs_filtered: self.assertTrue(author in book.authors.all()) # Check we've got a 'remove link' on filtered. choices_filtered = filter2.get_choices(qs) self.assertEqual(choices_filtered[0].link_type, FILTER_REMOVE)
def test_manytomany_filter_multiple(self): qs = Book.objects.all() # Specific example - multiple filtering emily = Author.objects.get(name='Emily Brontë') charlotte = Author.objects.get(name='Charlotte Brontë') anne = Author.objects.get(name='Anne Brontë') # If we select 'emily' as an author: data = MultiValueDict({'authors': [str(emily.pk)]}) with self.assertNumQueries(1): # 1 query for all chosen objects filter1 = ManyToManyFilter('authors', Book, data) with self.assertNumQueries(0): # This shouldn't need to do any more queries qs_emily = filter1.apply_filter(qs) # ...we should get a qs that includes Poems and Wuthering Heights. self.assertTrue(qs_emily.filter(name='Poems').exists()) self.assertTrue(qs_emily.filter(name='Wuthering Heights').exists()) # ...and excludes Jane Eyre self.assertFalse(qs_emily.filter(name='Jane Eyre').exists()) with self.assertNumQueries(2): # 0 query for all chosen objects (already done) # 1 query for available objects # 1 query for counts choices = filter1.get_choices(qs_emily) # We should have a 'choices' that includes charlotte and anne self.assertTrue( text_type(anne) in [c.label for c in choices if c.link_type is FILTER_ADD]) self.assertTrue( text_type(charlotte) in [c.label for c in choices if c.link_type is FILTER_ADD]) # ... but not emily, because that is obvious and boring self.assertTrue( text_type(emily) not in [c.label for c in choices if c.link_type is FILTER_ADD]) # emily should be in 'remove' links, however. self.assertTrue( text_type(emily) in [c.label for c in choices if c.link_type is FILTER_REMOVE]) # Select again - should have sensible params anne_choice = [c for c in choices if c.label.startswith('Anne')][0] self.assertTrue( text_type(emily.pk) in anne_choice.params.getlist('authors')) self.assertTrue( text_type(anne.pk) in anne_choice.params.getlist('authors')) # Now do the second select: filter2 = ManyToManyFilter('authors', Book, anne_choice.params) qs_emily_anne = filter2.apply_filter(qs) # ...we should get a qs that includes Poems self.assertTrue(qs_emily_anne.filter(name='Poems').exists()) # ... but not Wuthering Heights self.assertFalse( qs_emily_anne.filter(name='Wuthering Heights').exists()) # The choices should contain just Emily and Anne to remove, and # Charlotte should have 'link_type' FILTER_ADD. Even though it # is the only choice, adding the choice is not necessarily the same as # not adding it (could have books by Emily and Anne, but not Charlotte) choices = filter2.get_choices(qs_emily_anne) self.assertEqual([(c.label, c.link_type) for c in choices], [(text_type(emily), FILTER_REMOVE), (text_type(anne), FILTER_REMOVE), (text_type(charlotte), FILTER_ADD)])
def test_manytomany_filter_multiple(self): qs = Book.objects.all() # Specific example - multiple filtering emily = Author.objects.get(name='Emily Brontë') charlotte = Author.objects.get(name='Charlotte Brontë') anne = Author.objects.get(name='Anne Brontë') # If we select 'emily' as an author: data = MultiValueDict({'authors': [str(emily.pk)]}) with self.assertNumQueries(1): # 1 query for all chosen objects filter1 = ManyToManyFilter('authors', Book, data) with self.assertNumQueries(0): # This shouldn't need to do any more queries qs_emily = filter1.apply_filter(qs) # ...we should get a qs that includes Poems and Wuthering Heights. self.assertTrue(qs_emily.filter(name='Poems').exists()) self.assertTrue(qs_emily.filter(name='Wuthering Heights').exists()) # ...and excludes Jane Eyre self.assertFalse(qs_emily.filter(name='Jane Eyre').exists()) with self.assertNumQueries(2): # 0 query for all chosen objects (already done) # 1 query for available objects # 1 query for counts choices = filter1.get_choices(qs_emily) # We should have a 'choices' that includes charlotte and anne self.assertTrue(unicode(anne) in [c.label for c in choices if c.link_type is FILTER_ADD]) self.assertTrue(unicode(charlotte) in [c.label for c in choices if c.link_type is FILTER_ADD]) # ... but not emily, because that is obvious and boring self.assertTrue(unicode(emily) not in [c.label for c in choices if c.link_type is FILTER_ADD]) # emily should be in 'remove' links, however. self.assertTrue(unicode(emily) in [c.label for c in choices if c.link_type is FILTER_REMOVE]) # Select again - should have sensible params anne_choice = [c for c in choices if c.label.startswith('Anne')][0] self.assertTrue(unicode(emily.pk) in anne_choice.params.getlist('authors')) self.assertTrue(unicode(anne.pk) in anne_choice.params.getlist('authors')) # Now do the second select: filter2 = ManyToManyFilter('authors', Book, anne_choice.params) qs_emily_anne = filter2.apply_filter(qs) # ...we should get a qs that includes Poems self.assertTrue(qs_emily_anne.filter(name='Poems').exists()) # ... but not Wuthering Heights self.assertFalse(qs_emily_anne.filter(name='Wuthering Heights').exists()) # The choices should contain just Emily and Anne to remove, and # Charlotte should have 'link_type' FILTER_ADD. Even though it # is the only choice, adding the choice is not necessarily the same as # not adding it (could have books by Emily and Anne, but not Charlotte) choices = filter2.get_choices(qs_emily_anne) self.assertEqual([(c.label, c.link_type) for c in choices], [(unicode(emily), FILTER_REMOVE), (unicode(anne), FILTER_REMOVE), (unicode(charlotte), FILTER_ADD)])