mirror of
https://github.com/Hopiu/wagtail.git
synced 2026-05-12 01:03:11 +00:00
Merge pull request #982 from kaedroho/feature/streamfield-search
[streamfield] Search
This commit is contained in:
commit
22a84e1ca3
4 changed files with 127 additions and 1 deletions
|
|
@ -225,6 +225,12 @@ class Block(six.with_metaclass(BaseBlock, object)):
|
|||
"""
|
||||
return force_text(value)
|
||||
|
||||
def get_searchable_content(self, value):
|
||||
"""
|
||||
Returns a list of strings containing text content within this block to be used in a search engine.
|
||||
"""
|
||||
return []
|
||||
|
||||
def __eq__(self, other):
|
||||
"""
|
||||
The deep_deconstruct method in django.db.migrations.autodetector.MigrationAutodetector does not
|
||||
|
|
@ -347,17 +353,23 @@ class FieldBlock(Block):
|
|||
def clean(self, value):
|
||||
return self.field.clean(value)
|
||||
|
||||
|
||||
class CharBlock(FieldBlock):
|
||||
def __init__(self, required=True, help_text=None, max_length=None, min_length=None, **kwargs):
|
||||
# TODO: decide what to do about 'label' and 'initial' parameters to the form field
|
||||
self.field = forms.CharField(required=required, help_text=help_text, max_length=max_length, min_length=min_length)
|
||||
super(CharBlock, self).__init__(**kwargs)
|
||||
|
||||
def get_searchable_content(self, value):
|
||||
return [force_text(value)]
|
||||
|
||||
|
||||
class URLBlock(FieldBlock):
|
||||
def __init__(self, required=True, help_text=None, max_length=None, min_length=None, **kwargs):
|
||||
self.field = forms.URLField(required=required, help_text=help_text, max_length=max_length, min_length=min_length)
|
||||
super(URLBlock, self).__init__(**kwargs)
|
||||
|
||||
|
||||
class RichTextBlock(FieldBlock):
|
||||
@cached_property
|
||||
def field(self):
|
||||
|
|
@ -367,6 +379,10 @@ class RichTextBlock(FieldBlock):
|
|||
def render_basic(self, value):
|
||||
return mark_safe('<div class="rich-text">' + expand_db_html(value) + '</div>')
|
||||
|
||||
def get_searchable_content(self, value):
|
||||
return [force_text(value)]
|
||||
|
||||
|
||||
class RawHTMLBlock(FieldBlock):
|
||||
def __init__(self, required=True, help_text=None, max_length=None, min_length=None, **kwargs):
|
||||
self.field = forms.CharField(
|
||||
|
|
@ -542,6 +558,14 @@ class BaseStructBlock(Block):
|
|||
for name, val in value.items()
|
||||
])
|
||||
|
||||
def get_searchable_content(self, value):
|
||||
content = []
|
||||
|
||||
for name, block in self.child_blocks.items():
|
||||
content.extend(block.get_searchable_content(value.get(name, block.meta.default)))
|
||||
|
||||
return content
|
||||
|
||||
def deconstruct(self):
|
||||
"""
|
||||
Always deconstruct StructBlock instances as if they were plain StructBlocks with all of the
|
||||
|
|
@ -747,6 +771,14 @@ class ListBlock(Block):
|
|||
)
|
||||
return format_html("<ul>{0}</ul>", children)
|
||||
|
||||
def get_searchable_content(self, value):
|
||||
content = []
|
||||
|
||||
for child_value in value:
|
||||
content.extend(self.child_block.get_searchable_content(child_value))
|
||||
|
||||
return content
|
||||
|
||||
|
||||
# ===========
|
||||
# StreamBlock
|
||||
|
|
@ -925,6 +957,14 @@ class BaseStreamBlock(Block):
|
|||
[(child, child.block_type) for child in value]
|
||||
)
|
||||
|
||||
def get_searchable_content(self, value):
|
||||
content = []
|
||||
|
||||
for child in value:
|
||||
content.extend(child.block.get_searchable_content(child.value))
|
||||
|
||||
return content
|
||||
|
||||
def deconstruct(self):
|
||||
"""
|
||||
Always deconstruct StreamBlock instances as if they were plain StreamBlocks with all of the
|
||||
|
|
|
|||
|
|
@ -82,3 +82,6 @@ class StreamField(with_metaclass(models.SubfieldBase, models.Field)):
|
|||
def value_to_string(self, obj):
|
||||
value = self._get_val_from_obj(obj)
|
||||
return self.get_prep_value(value)
|
||||
|
||||
def get_searchable_content(self, value):
|
||||
return self.stream_block.get_searchable_content(value)
|
||||
|
|
|
|||
|
|
@ -35,6 +35,12 @@ class TestFieldBlock(unittest.TestCase):
|
|||
|
||||
self.assertIn('This field is required.', html)
|
||||
|
||||
def test_charfield_searchable_content(self):
|
||||
block = blocks.CharBlock()
|
||||
content = block.get_searchable_content("Hello world!")
|
||||
|
||||
self.assertEqual(content, ["Hello world!"])
|
||||
|
||||
def test_choicefield_render(self):
|
||||
class ChoiceBlock(blocks.FieldBlock):
|
||||
field = forms.ChoiceField(choices=(
|
||||
|
|
@ -62,6 +68,19 @@ class TestFieldBlock(unittest.TestCase):
|
|||
self.assertIn('<option value="choice-1">Choice 1</option>', html)
|
||||
self.assertIn('<option value="choice-2" selected="selected">Choice 2</option>', html)
|
||||
|
||||
@unittest.expectedFailure # Returning "choice-1" instead of "Choice 1"
|
||||
def test_choicefield_searchable_content(self):
|
||||
class ChoiceBlock(blocks.FieldBlock):
|
||||
field = forms.ChoiceField(choices=(
|
||||
('choice-1', "Choice 1"),
|
||||
('choice-2', "Choice 2"),
|
||||
))
|
||||
|
||||
block = ChoiceBlock()
|
||||
content = block.get_searchable_content("choice-1")
|
||||
|
||||
self.assertEqual(content, ["Choice 1"])
|
||||
|
||||
|
||||
class TestMeta(unittest.TestCase):
|
||||
def test_set_template_with_meta(self):
|
||||
|
|
@ -262,6 +281,19 @@ class TestStructBlock(unittest.TestCase):
|
|||
block = LinkBlock()
|
||||
self.assertIn('<script type="text/x-html-template">hello world</script>', block.all_html_declarations())
|
||||
|
||||
def test_searchable_content(self):
|
||||
class LinkBlock(blocks.StructBlock):
|
||||
title = blocks.CharBlock()
|
||||
link = blocks.URLBlock()
|
||||
|
||||
block = LinkBlock()
|
||||
content = block.get_searchable_content({
|
||||
'title': "Wagtail site",
|
||||
'link': 'http://www.wagtail.io',
|
||||
})
|
||||
|
||||
self.assertEqual(content, ["Wagtail site"])
|
||||
|
||||
|
||||
class TestListBlock(unittest.TestCase):
|
||||
def test_initialise_with_class(self):
|
||||
|
|
@ -398,6 +430,25 @@ class TestListBlock(unittest.TestCase):
|
|||
block = blocks.ListBlock(CharBlockWithDeclarations())
|
||||
self.assertIn('<script type="text/x-html-template">hello world</script>', block.all_html_declarations())
|
||||
|
||||
def test_searchable_content(self):
|
||||
class LinkBlock(blocks.StructBlock):
|
||||
title = blocks.CharBlock()
|
||||
link = blocks.URLBlock()
|
||||
|
||||
block = blocks.ListBlock(LinkBlock())
|
||||
content = block.get_searchable_content([
|
||||
{
|
||||
'title': "Wagtail",
|
||||
'link': 'http://www.wagtail.io',
|
||||
},
|
||||
{
|
||||
'title': "Django",
|
||||
'link': 'http://www.djangoproject.com',
|
||||
},
|
||||
])
|
||||
|
||||
self.assertEqual(content, ["Wagtail", "Django"])
|
||||
|
||||
|
||||
class TestStreamBlock(unittest.TestCase):
|
||||
def test_initialisation(self):
|
||||
|
|
@ -646,3 +697,32 @@ class TestStreamBlock(unittest.TestCase):
|
|||
|
||||
block_value = block.value_from_datadict(post_data, {}, 'article')
|
||||
self.assertEqual(block_value[2].value, "heading 2")
|
||||
|
||||
def test_searchable_content(self):
|
||||
class ArticleBlock(blocks.StreamBlock):
|
||||
heading = blocks.CharBlock()
|
||||
paragraph = blocks.CharBlock()
|
||||
|
||||
block = ArticleBlock()
|
||||
value = block.to_python([
|
||||
{
|
||||
'type': 'heading',
|
||||
'value': "My title",
|
||||
},
|
||||
{
|
||||
'type': 'paragraph',
|
||||
'value': 'My first paragraph',
|
||||
},
|
||||
{
|
||||
'type': 'paragraph',
|
||||
'value': 'My second paragraph',
|
||||
},
|
||||
])
|
||||
|
||||
content = block.get_searchable_content(value)
|
||||
|
||||
self.assertEqual(content, [
|
||||
"My title",
|
||||
"My first paragraph",
|
||||
"My second paragraph",
|
||||
])
|
||||
|
|
|
|||
|
|
@ -116,7 +116,10 @@ class BaseField(object):
|
|||
def get_value(self, obj):
|
||||
try:
|
||||
field = self.get_field(obj.__class__)
|
||||
return field._get_val_from_obj(obj)
|
||||
value = field._get_val_from_obj(obj)
|
||||
if hasattr(field, 'get_searchable_content'):
|
||||
value = field.get_searchable_content(value)
|
||||
return value
|
||||
except models.fields.FieldDoesNotExist:
|
||||
value = getattr(obj, self.field_name, None)
|
||||
if hasattr(value, '__call__'):
|
||||
|
|
|
|||
Loading…
Reference in a new issue