默认搜索功能

默认情况下,文章搜索插件支持搜索文章的标题和摘要部分。如果要搜索文章内容部分,需要额外的配置。

添加配置

ALDRYN_NEWSBLOG_UPDATE_SEARCH_DATA_ON_SAVE = True

当该变量激活之后,在文章插件保存时,它会将它的内容渲染并保存到数据库,然后我们在搜索时就能够搜到该部分内容。

但是仅仅设置ALDRYN_NEWSBLOG_UPDATE_SEARCH_DATA_ON_SAVE并不能自动地将前面保存的内容添加到对应数据库,我们需要执行下面的命令rebuild_article_search_data

python manage.py rebuild_article_search_data

该命令里的--language是可选的,简写-l,它用于指定翻译的内容,例如

python manage.py rebuild_article_search_data -l en de

如果不指定语言,那么它会处理所有的语言

Aldryn Search and Haystack

Aldryn News & Blog supports Aldryn Search and Django Haystack.

If you have Aldryn Search and Haystack installed and configured in your project, News & Blog’s content can also be rendered searchable. To enable this, add:

ALDRYN_NEWSBLOG_SEARCH = True

in your settings. Note that if your search infrastructure is not configured, this setting will have no effect.

代码适配

实际上仅仅做上面的修改并不能成功工作, 会提示markdown对象没有render_plugin函数,问题出在下面的函数

aldryn_newsblog/utils/aldryn_newsblog.py

def get_plugin_index_data(base_plugin, request):
    text_bits = []
    instance, plugin_type = base_plugin.get_plugin_instance()
    if instance is None:
        # this is an empty plugin
        return text_bits
    search_fields = getattr(instance, 'search_fields', [])
    if hasattr(instance, 'search_fulltext'):
        # check if the plugin instance has search enabled
        search_contents = instance.search_fulltext
    elif hasattr(base_plugin, 'search_fulltext'):
        # now check in the base plugin instance (CMSPlugin)
        search_contents = base_plugin.search_fulltext
    elif hasattr(plugin_type, 'search_fulltext'):
        # last check in the plugin class (CMSPluginBase)
        search_contents = plugin_type.search_fulltext
    else:
        # disabled if there's search fields defined,
        # otherwise it's enabled.
        search_contents = not bool(search_fields)
    if search_contents:
        from djangocms_markdown.models import Markdown  
        # if not type(instance) in [Markdown, Comments, Picture, Style, Bootstrap3CodePlugin, Boostrap3ImagePlugin]:
        if instance and hasattr(instance, 'render_plugin'):
            plugin_contents = instance.render_plugin(
                context=RequestContext(request))
        elif type(instance) in [Markdown]:
            plugin_contents = instance.body
        else:
            plugin_contents = None
        if plugin_contents:
            text_bits = get_cleaned_bits(plugin_contents)
    else:
        values = (get_field_value(instance, field) for field in search_fields)
        for value in values:
            cleaned_bits = get_cleaned_bits(value or '')
            text_bits.extend(cleaned_bits)
    return text_bits

修改上面的代码,如果对象没有render_plugin函数,则忽略
至少有下面函数是没有的

from djangocms_comments.models import Comments
from djangocms_picture.models import Picture 
from djangocms_style.models import Style 
from aldryn_bootstrap3.models import Bootstrap3CodePlugin 
from aldryn_bootstrap3.models import Boostrap3ImagePlugin

但是我们希望还是要搜索markdown的内容,检查markdown对象的定义

class Markdown(CMSPlugin):
    body = models.TextField()

    def __str__(self):
        return self.body[:64]

针对Markdown可以做特别处理。当然其实更好的是搜索处理过之后的内容,但本质上通过上面方法可以搜索了。
上面的针对Markdown部分的代码如下

        elif type(instance) in [Markdown]:
            plugin_contents = instance.body

代码走读

看一下代码的实现 ,下面这段代码是搜索结果的处理,可以看到有一个字段是search_data

class ArticleSearchResultsList(ArticleListBase):
    model = Article
    def get_queryset(self):
        qs = super(ArticleSearchResultsList, self).get_queryset()
        if not self.edit_mode:
            qs = qs.published()
        if self.query:
            return qs.filter(
                Q(translations__title__icontains=self.query) | 
                Q(translations__lead_in__icontains=self.query) |
                Q(translations__search_data__icontains=self.query)
            ).distinct()
        else:
            return qs.none()

它在model里的定义如下,真正的内容content是一个placeholder

class Article(TranslatedAutoSlugifyMixin,
              TranslationHelperMixin,
              TranslatableModel):
    # TranslatedAutoSlugifyMixin options
    slug_source_field_name = 'title'
    slug_default = _('untitled-article')
    # when True, updates the article's search_data field
    # whenever the article is saved or a plugin is saved
    # on the article's content placeholder.
    update_search_on_save = getattr(
        settings,
        'ALDRYN_NEWSBLOG_UPDATE_SEARCH_DATA_ON_SAVE',
        False
    )
    translations = TranslatedFields(
        title=models.CharField(_('title'), max_length=234),
        slug=models.SlugField(
            verbose_name=_('slug'),
            max_length=255,
            db_index=True,
            blank=True,
            help_text=_(
                'Used in the URL. If changed, the URL will change. '
                'Clear it to have it re-created automatically.'),
        ),
        lead_in=HTMLField(
            verbose_name=_('lead'), default='',
            help_text=_(
                'The lead gives the reader the main idea of the story, this '
                'is useful in overviews, lists or as an introduction to your '
                'article.'
            ),
            blank=True,
        ),
        meta_title=models.CharField(
            max_length=255, verbose_name=_('meta title'),
            blank=True, default=''),
        meta_description=models.TextField(
            verbose_name=_('meta description'), blank=True, default=''),
        meta_keywords=models.TextField(
            verbose_name=_('meta keywords'), blank=True, default=''),
        meta={'unique_together': (('language_code', 'slug', ), )},
        search_data=models.TextField(blank=True, editable=False)
    )

    content = PlaceholderField('newsblog_article_content',
                               related_name='newsblog_article_content')

从下面这个代码片段中可以看到它会根据设置ALDRYN_NEWSBLOG_UPDATE_SEARCH_DATA_ON_SAVE来觉得是否更新搜索结果

    update_search_on_save = getattr(
        settings,
        'ALDRYN_NEWSBLOG_UPDATE_SEARCH_DATA_ON_SAVE',
        False
    )

search_dataget_search_data获取,通过遍历plugin内容加到结果里

    def get_search_data(self, language=None, request=None):
        """
        Provides an index for use with Haystack, or, for populating
        Article.translations.search_data.
        """
        if not self.pk:
            return ''
        if language is None:
            language = get_current_language()
        if request is None:
            request = get_request(language=language)
        description = self.safe_translation_getter('lead_in', '')
        text_bits = [strip_tags(description)]
        for category in self.categories.all():
            text_bits.append(
                force_unicode(category.safe_translation_getter('name')))
        for tag in self.tags.all():
            text_bits.append(force_unicode(tag.name))
        if self.content:
            plugins = self.content.cmsplugin_set.filter(language=language)
            for base_plugin in plugins:
                plugin_text_content = ' '.join(
                    get_plugin_index_data(base_plugin, request))
                text_bits.append(plugin_text_content)
        return ' '.join(text_bits)

在更新文章时,这个search_data也会更新,保存时会将内容存到数据库,具体参考下面函数

def update_search_data(sender, instance, **kwargs):
class Article(TranslatedAutoSlugifyMixin,
              TranslationHelperMixin,
              TranslatableModel):
    def save(self, *args, **kwargs):

搜索列表控制

当显示的条目多时,会发现只能显示5行了,查了一下代码

aldryn_newsblog\views.py

class ArticleSearchResultsList(ArticleListBase):
    ...
    def get(self, request, *args, **kwargs):
        self.query = request.GET.get('q')
        self.max_articles = request.GET.get('max_articles', 0)
        self.edit_mode = (request.toolbar and request.toolbar.edit_mode)
        return super(ArticleSearchResultsList, self).get(request)
    def get_paginate_by(self, queryset):
        """
        If a max_articles was set (by a plugin), use that figure, else,
        paginate by the app_config's settings.
        """
        return self.max_articles or super(
            ArticleSearchResultsList, self).get_paginate_by(self.get_queryset())

class ArticleListBase(AppConfigMixin, AppHookCheckMixin, TemplatePrefixMixin,
                      PreviewModeMixin, ViewUrlMixin, ListView):
    def get_paginate_by(self, queryset):
        if self.paginate_by is not None:
            return self.paginate_by
        else:
            try:
                return self.config.paginate_by
            except AttributeError:
                return 10 # sensible failsafe

aldryn_common\paginator.py

def paginate_by(fallback=15):
    return getattr(settings, 'ALDRYN_COMMON_PAGINATOR_PAGINATE_BY', fallback)

要解决显示的问题,有两个方法

1.修改配置使得paginate_by 值改变

它对应的数据库配置是

class NewsBlogConfig(TranslatableModel, AppHookConfig):
    paginate_by = models.PositiveIntegerField(
        _('Paginate size'),
        blank=False,
        default=5,
        help_text=_('When paginating list views, how many articles per page?'),
    )

可以通过下面配置界面修改

这个改法有个问题,这个配置是app级别的,它同时会修改正常的blog列表的结果。

2.传递max_articles参数

我们需要在模板文件里添加该变量

article_search.html

<form action="{{ query_url }}" method="get"> 
    <div class="row">
      <div class="col-sm-12">
        <div class="input-group">
          <input type="text" name="q" id="search-plugin-{{ instance.pk }}" class="form-control" placeholder="{% trans "Search for article ..." %}" required>
          <span class="input-group-btn">
            <input type="hidden" name="max_articles" value="{{ instance.max_articles }}">
            <button class="btn btn-default" type="submit">Go!</button>
          </span>
        </div><!-- /input-group -->
      </div><!-- /.col-sm-12 -->
    </div><!-- /.row -->
</form>

添加一个hidden的input: max_articles

同时,可以看到它的数据库对应的字段

class NewsBlogArticleSearchPlugin(NewsBlogCMSPlugin):
    max_articles = models.PositiveIntegerField(
        _('max articles'), default=10,
        validators=[django.core.validators.MinValueValidator(1)],
        help_text=_('The maximum number of found articles display.')
    )

在下面plugin插件可修改

这样,就可以最大显示100多条搜索记录啦

其实,还有个方法就是跟blog显示一样将结果分页。比如: http://www.codingsoho.com/zh/blog/search/?q=deployment&max_articles=100 这个结果,如果返回结果是多于5条记录的,我们也可以用http://www.codingsoho.com/zh/blog/search/?q=deployment&max_articles=5&page=2遍历它的结果,这个就不在本文详述了。

参考文档

评论

飞翔的鸟: 1 year, 8 months ago
为什么我的搜索结果显示不全?