Repository: wagtail/wagtail

Issue:
search fields can't be overridden
In the past, you were able to override a search field of a parent class by redefining it. This functionality appears to be broken in Wagtail 0.7



Discussion:

Resolving patch:
diff --git a/wagtail/wagtailsearch/index.py b/wagtail/wagtailsearch/index.py
--- a/wagtail/wagtailsearch/index.py
+++ b/wagtail/wagtailsearch/index.py
@@ -37,15 +37,26 @@ def indexed_get_toplevel_content_type(cls):
 
     @classmethod
     def get_search_fields(cls):
-        return cls.search_fields
+        search_fields = {}
+
+        for field in cls.search_fields:
+            search_fields[(type(field), field.field_name)] = field
+
+        return list(search_fields.values())
 
     @classmethod
     def get_searchable_search_fields(cls):
-        return filter(lambda field: isinstance(field, SearchField), cls.get_search_fields())
+        return [
+            field for field in cls.get_search_fields()
+            if isinstance(field, SearchField)
+        ]
 
     @classmethod
     def get_filterable_search_fields(cls):
-        return filter(lambda field: isinstance(field, FilterField), cls.get_search_fields())
+        return [
+            field for field in cls.get_search_fields()
+            if isinstance(field, FilterField)
+        ]
 
     @classmethod
     def get_indexed_objects(cls):


Test patch:
diff --git a/wagtail/wagtailsearch/tests/test_indexed_class.py b/wagtail/wagtailsearch/tests/test_indexed_class.py
--- a/wagtail/wagtailsearch/tests/test_indexed_class.py
+++ b/wagtail/wagtailsearch/tests/test_indexed_class.py
@@ -15,3 +15,50 @@ def test_base_content_type_name(self):
     def test_qualified_content_type_name(self):
         name = models.SearchTestChild.indexed_get_content_type()
         self.assertEqual(name, 'tests_searchtest_tests_searchtestchild')
+
+
+class TestSearchFields(TestCase):
+    def make_dummy_type(self, search_fields):
+        return type('DummyType', (index.Indexed, ), dict(search_fields=search_fields))
+
+    def test_basic(self):
+        cls = self.make_dummy_type([
+            index.SearchField('test', boost=100, partial_match=False),
+            index.FilterField('filter_test'),
+        ])
+
+        self.assertEqual(len(cls.get_search_fields()), 2)
+        self.assertEqual(len(cls.get_searchable_search_fields()), 1)
+        self.assertEqual(len(cls.get_filterable_search_fields()), 1)
+
+    def test_overriding(self):
+        # If there are two fields with the same type and name
+        # the last one should override all the previous ones
+        cls = self.make_dummy_type([
+            index.SearchField('test', boost=100, partial_match=False),
+            index.SearchField('test', partial_match=True),
+        ])
+
+        self.assertEqual(len(cls.get_search_fields()), 1)
+        self.assertEqual(len(cls.get_searchable_search_fields()), 1)
+        self.assertEqual(len(cls.get_filterable_search_fields()), 0)
+
+        field = cls.get_search_fields()[0]
+        self.assertIsInstance(field, index.SearchField)
+
+        # Boost should be reset to the default if it's not specified by the override
+        self.assertIsNone(field.boost)
+
+        # Check that the partial match was overridden
+        self.assertTrue(field.partial_match)
+
+    def test_different_field_types_dont_override(self):
+        # A search and filter field with the same name should be able to coexist
+        cls = self.make_dummy_type([
+            index.SearchField('test', boost=100, partial_match=False),
+            index.FilterField('test'),
+        ])
+
+        self.assertEqual(len(cls.get_search_fields()), 2)
+        self.assertEqual(len(cls.get_searchable_search_fields()), 1)
+        self.assertEqual(len(cls.get_filterable_search_fields()), 1)