@@ -429,6 +429,56 @@ class SearchListView(generics.ListAPIView):
429
429
reload_module (filters )
430
430
431
431
432
+ class AttributeModel (models .Model ):
433
+ label = models .CharField (max_length = 32 )
434
+
435
+
436
+ class SearchFilterModelM2M (models .Model ):
437
+ title = models .CharField (max_length = 20 )
438
+ text = models .CharField (max_length = 100 )
439
+ attributes = models .ManyToManyField (AttributeModel )
440
+
441
+
442
+ class SearchFilterM2MSerializer (serializers .ModelSerializer ):
443
+ class Meta :
444
+ model = SearchFilterModelM2M
445
+
446
+
447
+ class SearchFilterM2MTests (TestCase ):
448
+ def setUp (self ):
449
+ # Sequence of title/text/attributes is:
450
+ #
451
+ # z abc [1, 2, 3]
452
+ # zz bcd [1, 2, 3]
453
+ # zzz cde [1, 2, 3]
454
+ # ...
455
+ for idx in range (3 ):
456
+ label = 'w' * (idx + 1 )
457
+ AttributeModel (label = label )
458
+
459
+ for idx in range (10 ):
460
+ title = 'z' * (idx + 1 )
461
+ text = (
462
+ chr (idx + ord ('a' )) +
463
+ chr (idx + ord ('b' )) +
464
+ chr (idx + ord ('c' ))
465
+ )
466
+ SearchFilterModelM2M (title = title , text = text ).save ()
467
+ SearchFilterModelM2M .objects .get (title = 'zz' ).attributes .add (1 , 2 , 3 )
468
+
469
+ def test_m2m_search (self ):
470
+ class SearchListView (generics .ListAPIView ):
471
+ queryset = SearchFilterModelM2M .objects .all ()
472
+ serializer_class = SearchFilterM2MSerializer
473
+ filter_backends = (filters .SearchFilter ,)
474
+ search_fields = ('=title' , 'text' , 'attributes__label' )
475
+
476
+ view = SearchListView .as_view ()
477
+ request = factory .get ('/' , {'search' : 'zz' })
478
+ response = view (request )
479
+ self .assertEqual (len (response .data ), 1 )
480
+
481
+
432
482
class OrderingFilterModel (models .Model ):
433
483
title = models .CharField (max_length = 20 )
434
484
text = models .CharField (max_length = 100 )
0 commit comments