Django简单博客实战(六)---搜索功能

Django-haystack插件实现

项目地址:https://github.com/ylpxzx/lifeblog

步骤

  1. 安装依赖包
pip install whoosh,jieba,django-haystack

# 尽量采用其他源的pip进行安装,比如
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple django-haystack
  1. 将haystack加入INSTALLED_APPS
INSTALLED_APPS = [
    'django.contrib.admin',
    ...
    'post',
    'haystack',   #添加该行
]
  1. 在需要搜索的应用app下创建search_indexes.py,如在post应用下。
from haystack import indexes
from .models import Post

# 类名的命名规则是固定的,严格按照“应用名+Index”
class PostIndex(indexes.SearchIndex, indexes.Indexable):
    text = indexes.CharField(document=True, use_template=True,template_name='search/indexes/post/post_text.txt')

    def get_model(self):
        return Post

    def index_queryset(self, using=None):
        return self.get_model().objects.all()
  1. 将site-packages/haystack/backends/whoosh_backend.py复制到应用app下,并更名为whoosh_cn_backend.py

  2. 在settings.py中设置haystack配置

# 配置搜索设置
HAYSTACK_CONNECTIONS = {
    'default': {
        'ENGINE': 'post.whoosh_cn_backend.WhooshEngine',  # post应用下的whoosh_cn_backend.py文件
        'PATH': os.path.join(BASE_DIR, 'whoosh_index'),  # 指定了索引文件需要存放的位置,我们设置为项目根目录 BASE_DIR 下的 whoosh_index 文件夹(在建立索引时会自动创建)。
    },
}
HAYSTACK_SEARCH_RESULTS_PER_PAGE = 6  # 指定如何对搜索结果分页,这里设置为每 6 项结果为一页。
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'  # 指定什么时候更新索引,这里我们使用 haystack.signals.RealtimeSignalProcessor,作用是每当有文章更新时就更新索引。由于博客文章更新不会太频繁,因此实时更新没有问题。
  1. 创建template/search/indexes/app_name/model_text.txt文件,建立指定的字段索引
# 如:创建template/search/indexes/post/post_text.txt
{{ object.title }}
{{ object.content }}
  1. 创建索引,自动生成whoosh_index文件夹
# 创建索引
python manage.py rebuild_index  # 输入y即可

# 更新索引
python manage.py update_index
  1. 在应用app的views.py下继承SearchView
from haystack.views import SearchView
from django.core.paginator import Paginator,EmptyPage,PageNotAnInteger
from django.conf import settings
from django.shortcuts import render,redirect
from django.db.models import Q
class MySearchIndex(SearchView):

    # template = 'search.html'

    def extra_context(self):
        context = super(MySearchIndex,self).extra_context()
        context['category_list'] = Category.objects.all().order_by('post_category')
        context['popular_post'] = Post.objects.all().order_by('-total_views')[:4]
        return context

    def create_response(self):
        if self.request.GET.get('q'):
            # print("进入not self.request")
            keyword = self.request.GET.get('q')
            # post_info = Post.objects.filter(title__contains=keyword)
            post_info = Post.objects.filter(Q(title__icontains=keyword)|Q(content__icontains=keyword)).order_by("id")  # 搜索标题和文章内容
            # post_info = Post.objects.all()
            paginator = Paginator(post_info, settings.HAYSTACK_SEARCH_RESULTS_PER_PAGE)
            try:
                page = paginator.page(int(self.request.GET.get('page', 1)))
            except PageNotAnInteger:
                page = paginator.page(1)
            except EmptyPage:
                page = paginator.page(paginator.num_pages)

            context = {
                'query': self.query,
                'form': self.form,
                'page': page,
                'paginator': paginator,
                'suggestion': None,
            }
            context.update(self.extra_context())
            return render(self.request, self.template, context)
        else:
            qs = super(MySearchIndex, self).create_response()
            # print(self.get_context())
            return qs
  1. 在应用urls下配置路由
url(r'^search/$', MySearchIndex(), name='haystack_search'),
  1. 创建template/search/search.html文件
{% extends 'index.html' %}
{% block Banner %}{% endblock %}

{% block area %}
    <div class="col-lg-8">
            <div class="blog_left_sidebar">
                <div class="row">
                    {% for post in page.object_list %}
                    <div class="col-md-6">
                        <article class="blog_style1 small">
                            <div class="blog_img">
                                <img class="img-fluid" style="height: 280px;" src="/media/{{ post.cover }}" alt="">
                            </div>
                            <div class="blog_text">
                                <div class="blog_text_inner">
                                    <div class="cat">
                                        <a class="cat_btn" href="#">{{ post.category }}</a>
                                        <a href="#"><i class="fa fa-calendar" aria-hidden="true"></i> {{ post.create_time|date:"Y-m-d" }}</a>
                                        <a href="#"><i class="fa fa-comments-o" aria-hidden="true"></i> 05</a>
                                    </div>
                                    <a href="{% url 'post:detail' post.id %}" ><h4>{{ post.title }}</h4></a>
                                    <p>{{ post.short_detail|safe }}</p>
                                    <a class="blog_btn" href="{% url 'post:detail' post.id %}">Read More</a>
                                </div>
                            </div>
                        </article>
                    </div>
                    {% empty %}
                    <h3>没有找到相关文章</h3>
                    {% endfor %}
                </div>



                <!-- 分页栏 -->
                <nav class="blog-pagination justify-content-center d-flex">
                    <ul class="pagination">
                        {% if page.has_previous %}
                        <li class="page-item">
                            <a href="{% url 'post:haystack_search' %}?q={{ query }}&page={{ page.previous_page_number }}" class="page-link" aria-label="Previous">
                                <span aria-hidden="true">
                                    <span class="lnr lnr-chevron-left"></span>
                                </span>
                            </a>

                        </li>
                        {% else %}
                        <li class="page-item">
                            <a href="#" class="page-link" aria-label="Previous">
                                <span aria-hidden="true">
                                    <span class="lnr lnr-chevron-left"></span>
                                </span>
                            </a>
                        </li>
                        {% endif %}

                        {% for num in page.paginator.page_range %}
                            {% if num == page.number %}
                                <li class="page-item active"><a href="{% url 'post:haystack_search' %}?q={{ query }}&page={{ num }}" class="page-link">{{ num }}</a></li>
                            {% else %}
                                <li class="page-item"><a href="{% url 'post:haystack_search' %}?q={{ query }}&page={{ num }}" class="page-link">{{ num }}</a></li>
                            {% endif %}
                        {% endfor %}

                        {% if page.has_next %}
                        <li class="page-item">
                            <a href="{% url 'post:haystack_search' %}?q={{ query }}&page={{ page.next_page_number }}" class="page-link" aria-label="Next">
                                <span aria-hidden="true">
                                    <span class="lnr lnr-chevron-right"></span>
                                </span>
                            </a>
                        </li>
                        {% else %}
                        <li class="page-item">
                            <a href="#" class="page-link" aria-label="Next">
                                <span aria-hidden="true">
                                    <span class="lnr lnr-chevron-right"></span>
                                </span>
                            </a>
                        </li>
                        {% endif %}
                    </ul>
                </nav>

            </div>

                    </div>
{% endblock %}

网上示例需要在字段选取中间加个object,如:

<p>{{ post.object.short_detail|safe }}</p>

项目文件结构

在这里插入图片描述

异常解决

  • ImportError: cannot import name 'six' from 'django.utils'
  1. 安装six
pip install six
  1. 将six.py复制到django/utils

文件路径

site-packages/six.py

site-packages/django/utils

  • ImportError: cannot import name python_2_unicode_compatible
  1. 报错位置导入的包替代为以下导入语句
from django.utils.six import python_2_unicode_compatible
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,923评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,154评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,775评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,960评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,976评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,972评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,893评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,709评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,159评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,400评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,552评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,265评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,876评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,528评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,701评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,552评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,451评论 2 352