7.3 django項目-新聞博客系統(tǒng)之新聞搜索3

07.3 新聞搜索

一、包安裝

1、安裝djangohaystack

官方文檔

安裝

# 安裝djangohaystack
# 使用的是當期最新版本 2.8.1
pip install django-haystack

配置文件

# 將Haystack添加到`INSTALLED_APPS`中
# settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'haystack',
    'user',
    'news',
    'doc',
    'course',
    'verification'
]
# 配置搜索引擎
# 在settings.py中添加如下設置
# 全文搜索引擎haystack 配置
# 不同的搜索引擎,配置不同,詳情見官方文檔
HAYSTACK_CONNECTIONS = {
    'default': {
        'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine',
        'URL': 'http://127.0.0.1:9200/',    # 此處為elasticsearch運行的服務器ip地址和端口
        'INDEX_NAME': 'tzpython',           # 指定elasticserach建立的索引庫名稱
    },
}

# 搜索結(jié)果每頁顯示數(shù)量
HAYSTACK_SEARCH_RESULTS_PER_PAGE = 5
# 實時更新index
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'

2、安裝elasticsearch-py

haystack操作es還需要python的es驅(qū)動。兼容性見官網(wǎng)

1563194487143.png

根據(jù)官網(wǎng),選擇2.4.1版本

pip install elasticsearch==2.4.1

至此,環(huán)境搭建完成。相對應的es,es-ik,haystack,es-python的版本請保持一致。

二、新聞搜索

1.業(yè)務流程分析

  • 判斷是否傳遞查詢參數(shù)q
  • 如果沒有傳遞q,則直接返回熱門新聞數(shù)據(jù)
  • 如果有傳遞,則返回查詢結(jié)果
  • 分頁

2. 接口設計

  1. 接口說明:
類目 說明
請求方法 POST
url定義 /news/search/
參數(shù)格式 查詢參數(shù)
  1. 參數(shù)說明:
參數(shù)名 類型 是否必須 描述
q 字符串 查詢的關鍵字
page 整數(shù) 頁碼
  1. 返回結(jié)果:

    搜索頁面html

3.后端代碼

  1. 創(chuàng)建haystack數(shù)據(jù)模型

    在apps/news/目錄下創(chuàng)建search_indexes.py文件,<span style="color:red">注意文件名必須使用search_indexes.py</span>,代碼如下:

    # !/usr/bin/env python
    # -*- coding:utf-8 -*-
    from haystack import indexes
    from .models import News
    
    
    class NewsIndex(indexes.SearchIndex, indexes.Indexable):
        """
        這個模型的作用類似django的模型,它告訴haystack哪些數(shù)據(jù)會被
        放進查詢回的模型對象中,以及通過哪些字段進行索引和查詢
        """
        # 這字段必須這么寫,用來告訴haystack和搜索引擎要索引哪些字段
        text = indexes.CharField(document=True, use_template=True)
        id = indexes.CharField(model_attr='id')
        title = indexes.CharField(model_attr='title')
        digest = indexes.CharField(model_attr='digest')
        content = indexes.CharField(model_attr='content')
        image_url = indexes.CharField(model_attr='image_url')
    
        def get_model(self):
            """
            返回建立索引的模型
            :return:
            """
            return News
    
        def index_queryset(self, using=None):
            """
            返回要建立索引的數(shù)據(jù)查詢集
            :param using:
            :return:
            """
            return self.get_model().objects.filter(is_delete=False)
    
  2. 創(chuàng)建索引數(shù)據(jù)模板

    根據(jù)上面創(chuàng)建的模型中的第一個text字段中的use_template=True參數(shù),還需要創(chuàng)建一個索引數(shù)據(jù)模板,用來告訴搜索引擎需要索引哪些字段。

    在templates中創(chuàng)建文件search/indexes/yourappname/modelname_text.txt,所以本項目需要創(chuàng)建search/indexes/news/news_text.txt,文件內(nèi)容如下:

    {{ object.title }}
    {{ object.digest }}
    {{ object.content }}
    {{ object.author.username }}
    
  3. 創(chuàng)建索引

    按上面的步驟配置好后,就可以運行haystack的命令創(chuàng)建索引了

    ~$ python manage.py rebuild_index
    
  4. 視圖代碼

    在news/views.py中添加如下視圖

    from haystack.generic_views import SearchView
    
    class NewsSearchView(SearchView):
        """
        新聞搜索視圖
        """
        # 設置搜索模板文件
        template_name = 'news/search.html'
    
        # 重寫get請求,如果請求參數(shù)q為空,返回模型News的熱門新聞數(shù)據(jù)
        # 否則根據(jù)參數(shù)q搜索相關數(shù)據(jù)
        def get(self, request, *args, **kwargs):
            query = request.GET.get('q')
            if not query:
                # 顯示熱門新聞
                hot_news = HotNews.objects.select_related('news__tag').only('news__title', 'news__image_url', 'news_id',
                                                                            'news__tag__name').filter(
                    is_delete=False).order_by('priority', '-news__clicks')
                paginator = Paginator(hot_news, settings.HAYSTACK_SEARCH_RESULTS_PER_PAGE)
                try:
                    page = paginator.get_page(int(request.GET.get('page')))
                except Exception as e:
                    page = paginator.get_page(1)
    
                return render(request, 'news/search.html', context={
                    'page': page,
                    'paginator': paginator,
                    'query': query
                })
            else:
                # 搜索
                return super().get(request, *args, **kwargs)
    
        def get_context_data(self, *args, **kwargs):
            """
            在context中添加page變量
            :param args: 
            :param kwargs: 
            :return: 
            """
            context = super().get_context_data(*args, **kwargs)
            if context['page_obj']:
                context['page'] = context['page_obj']
            return context
    
  5. 路由

    在news/urls.py中添加如下路由

        path('news/search/', views.NewsSearchView.as_view(), name='news_search')
    

4.前端代碼

  1. 自定義過濾器

    在news/templatetags/news_template_filters.py中定義一個處理分頁的過濾器

    # !/usr/bin/env python
    # -*- coding:utf-8 -*-
    # create_time: 2019/7/14
    # Author = '心藍'
    from django import template
    
    register = template.Library()
    
    
    @register.filter
    def page_bar(page):
        page_list = []
        if page.number != 1:
            page_list.append(1)
        if page.number - 3 > 1:
            page_list.append('...')
        if page.number - 2 > 1:
            page_list.append(page.number - 2)
        if page.number - 1 > 1:
            page_list.append(page.number - 1)
        page_list.append(page.number)
        if page.paginator.num_pages > page.number + 1:
            page_list.append(page.number + 1)
        if page.paginator.num_pages > page.number + 2:
            page_list.append(page.number + 2)
        if page.paginator.num_pages > page.number + 3:
            page_list.append('...')
        if page.paginator.num_pages != page.number:
            page_list.append(page.paginator.num_pages)
        return page_list
    
  2. 前端html代碼

    {% extends 'base/base.html' %}
    {% load static %}
    {% load news_template_filters %}
    
    {% block title %}新聞搜索{% endblock title %}
    
    {% block link %}
        <link rel="stylesheet" href="{% static 'css/news/search.css' %}">
    {% endblock link %}
    
    {% block main_contain %}
        <!-- main-contain start  -->
        <div class="main-contain ">
            <!-- search-box start -->
            <div class="search-box">
                <form action="" style="display: inline-flex;">
                    {% if  query %}
                        <input type="search" placeholder="請輸入要搜索的內(nèi)容" name="q" class="search-control" value="{{ query }}">
                    {% else %}
                        <input type="search" placeholder="請輸入要搜索的內(nèi)容" name="q" class="search-control">
                    {% endif %}
    
                    <input type="submit" value="搜索" class="search-btn">
                </form>
                <!-- 可以用浮動 垂直對齊 以及 flex  -->
            </div>
            <!-- search-box end -->
            <!-- content start -->
            <div class="content">
                {% if query %}
                    <!-- search-list start -->
                    <div class="search-result-list">
                        <h2 class="search-result-title">搜索結(jié)果 <span>{{ page.paginator.num_pages|default:0 }}</span> 頁</h2>
                        <ul class="news-list">
                            {% load highlight %}
                            {% for news in page.object_list %}
                                <li class="news-item clearfix">
                                    <a href="{% url 'news:news_detail' news.id %}" class="news-thumbnail" target="_blank"><img src="{{ news.image_url }}" alt=""></a>
                                    <div class="news-content">
                                        <h4 class="news-title">
                                            <a href="{% url 'news:news_detail' news.id %}">{% highlight news.title with query %}</a>
                                        </h4>
                                        <p class="news-details">{{ news.digest }}</p>
                                        <div class="news-other">
                                            <span class="news-type">{{ news.object.tag.name }}</span>
                                            <span class="news-time">{{ news.object.update_time }}</span>
                                            <span class="news-author">{% highlight news.object.author.username with query %}</span>
                                        </div>
                                    </div>
    
                                </li>
                            {% empty %}
                                <li class="news-item clearfix">
                                    <p>沒有找到你想要的找的內(nèi)容.</p>
                                </li>
                            {% endfor %}
                        </ul>
                    </div>
    
                    <!-- search-list end -->
                {% else %}
                    <!-- news-contain start -->
    
                    <div class="news-contain">
                        <div class="hot-recommend-list">
                            <h2 class="hot-recommend-title">熱門推薦</h2>
                            <ul class="news-list">
                                {% for hotnews in page %}
                                    <li class="news-item clearfix">
                                        <a href="#" class="news-thumbnail">
                                            <img src="{{ hotnews.news.image_url }}">
                                        </a>
                                        <div class="news-content">
                                            <h4 class="news-title">
                                                <a href="{% url 'news:news_detail' hotnews.news_id %}">{{ hotnews.news.title }}</a>
                                            </h4>
                                            <p class="news-details">{{ hotnews.news.digest }}</p>
                                            <div class="news-other">
                                                <span class="news-type">{{ hotnews.news.tag.name }}</span>
                                                <span class="news-time">{{ hotnews.update_time }}</span>
                                                <span class="news-author">{{ hotnews.news.author.username }}</span>
                                            </div>
                                        </div>
                                    </li>
                                {% endfor %}
    
    
                            </ul>
                        </div>
                    </div>
    
    
                    <!-- news-contain end -->
                {% endif %}
                <!-- Pagination start-->
                <div class="page-box" id="pages">
                    <div class="pagebar" id="pageBar">
                        <a class="al">{{ page.paginator.count|default:0 }}條</a>
                        <!-- prev page start-->
                        {% if page.has_previous %}
                            {% if query %}
                                <a href="{% url 'news:news_search' %}?q={{ query }}&page={{ page.previous_page_number }}"
                                   class="prev">上一頁</a>
                            {% else %}
                                <a href="{% url 'news:news_search' %}?page={{ page.previous_page_number }}"
                                   class="prev">上一頁</a>
                            {% endif %}
                        {% endif %}
                        <!-- prev page end-->
    
                        <!-- page bar start-->
                    {% if page.has_previous or page.has_next %}
                        {% for n in page|page_bar %}
                            {% if query %}
                                {% if n == '...' %}
                                <span class="point">{{ n }}</span>
                                {% else %}
                                    {% if n == page.number %}
                                        <span class="sel">{{ n }}</span>
                                    {% else %}
                                        <a href="{% url 'news:news_search' %}?page={{ n }}&q={{ query }}">{{ n }}</a>
                                    {% endif %}
                                {% endif %}
                            {% else %}
                                {% if n == '...' %}
                                    <span class="point">{{ n }}</span>
                                {% else %}
                                    {% if n == page.number %}
                                        <span class="sel">{{ n }}</span>
                                    {% else %}
                                        <a href="{% url 'news:news_search' %}?page={{ n }}">{{ n }}</a>
                                    {% endif %}
                                {% endif %}
                            {% endif %}
                        {% endfor %}
                    {% endif %}
                        <!-- page bar end-->
    
                        <!-- next page start-->
                        {% if page.has_next %}
                            {% if query %}
                                <a href="{% url 'news:news_search' %}?q={{ query }}&page={{ page.next_page_number }}"
                                   class="prev">下一頁</a>
                            {% else %}
                                <a href="{% url 'news:news_search' %}?page={{ page.next_page_number }}"
                                   class="prev">下一頁</a>
                            {% endif %}
                        {% endif %}
                        <!-- next page end-->
    
    
                    </div>
                </div>
                <!-- Pagination end-->
            </div>
            <!-- content end -->
        </div>
        <!-- main-contain  end -->
    {% endblock main_contain %}
    
    {#{% block otherjs %}#}
    {#    <script src="{% static 'js/news/index.js' %}"></script>#}
    {#{% endblock otherjs %}#}
    
  3. css代碼

    修改static/css/news/search.css如下:

    /* ================= main start ================= */
    #main {
        margin-top: 25px;
        min-height: 700px;
    }
    /* ========= main-contain start ============ */
    #main .main-contain {
        width: 800px;
        float: left;
        background: #fff;
    }
    
    /* ===  search-box start === */
    .main-contain .search-box {
        padding: 40px 50px;
        width: 700px;
        box-shadow: 1px 2px rgba(0,0,0,.1);
        display: inline-flex;
    }
    .main-contain .search-box .search-control {
        width: 600px;
        height: 40px;
        border-radius: 20px 0 0 20px;
        border: 1px solid #ddd;
        border-right: none;
        padding-left: 0.88em;
        font-size: 20px;
    }
    .main-contain .search-box .search-btn {
        width: 100px;
        height: 40px;
        border: 1px solid red;
        background: red;
        color: #fff;
        font-size: 20px;
        border-radius:  0 20px 20px 0;
        cursor: pointer;
    }
    /* ===  search-box end === */
    
    /* === content start === */
    /* == search-list start == */
    .content .search-result-list {
        padding-top: 20px;
    }
    .content .search-result-list .search-result-title {
        padding-left: 20px;
        font-size: 20px;
        line-height: 26px;
    }
    .content .search-result-list .search-result-title span {
        font-weight: 700;
        color: #ff6620;
    }
    /* == search-list end == */
    /* == news-contain start == */
    .content .news-contain .hot-recommend-list {
        padding-top: 20px;
    }
    .hot-recommend-list .hot-recommend-title {
        padding-left: 20px;
        font-size: 20px;
        line-height: 26px;
    }
    .content .news-contain li {
        border-bottom: 1px solid #ededed;
    }
    .news-list .news-item {
        padding: 20px;
    }
    .news-list .news-item .news-thumbnail {
        float: left;
        width: 224px;
        height: 160px;
        margin-right: 30px;
        overflow: hidden;
    }
    .news-item .news-thumbnail img {
        width: 100%;
        height: 100%;
        transition: all 0.3s ease-out;
    }
    .news-item .news-thumbnail:hover img {
        transform: scale(1.1);
        transition: all 0.3s ease-in;
    }
    .news-list .news-item .news-content {
        width: 500px;
        height: 170px;
        float: right;
        color: #878787;
        font-size: 14px;
    }
    .news-item .news-content .news-title{
        color: #212121;
        font-size: 22px;
        height: 52px;
        line-height: 26px;
        transition:all 0.3s ease-out;
    }
    .news-item .news-content .news-title:hover {
        color: #5b86db;
        transition:all 0.3s ease-in;
    }
    .news-item .news-content .news-details {
        height: 44px;
        line-height: 22px;
        margin-top: 19px;
        text-align: justify;
    }
    .news-item .news-content .news-other {
        margin-top: 30px;
    }
    .news-content .news-other .news-type {
        color: #5b86db;
    }
    .news-content .news-other .news-author {
        float: right;
        margin-right: 15px;
    }
    .news-content .news-other .news-time {
        float: right;
    }
    /* === current index start === */
    #pages {
     padding: 32px 0 10px;
    }
    
    .page-box {
     text-align: center;
        /*font-size: 14px;*/
    }
    
    #pages a.prev, a.next {
     width: 56px;
     padding: 0
    }
    
    #pages a {
     display: inline-block;
     height: 26px;
     line-height: 26px;
     background: #fff;
     border: 1px solid #e3e3e3;
     text-align: center;
     color: #333;
     padding: 0 10px
    }
    
    #pages .sel {
     display: inline-block;
     height: 26px;
     line-height: 26px;
     background: #0093E9;
     border: 1px solid #0093E9;
     color: #fff;
     text-align: center;
     padding: 0 10px
    }
    #pages .point {
     display: inline-block;
     height: 26px;
     line-height: 26px;
     background: #fff;
     border: 1px solid #e3e3e3;
     text-align: center;
     color: #333;
     padding: 0 10px
    }
    .highlighted {
        font-weight: 700;
        color: #ff6620;
    }
    /* === current index end === */
    /* === content end === */
    /* ================= main end ================= */
    
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內(nèi)容