美文网首页
7.3 django项目-新闻博客系统之新闻搜索3

7.3 django项目-新闻博客系统之新闻搜索3

作者: yungege | 来源:发表于2019-08-27 16:02 被阅读0次

    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建立的索引库名称
        },
    }
    
    # 搜索结果每页显示数量
    HAYSTACK_SEARCH_RESULTS_PER_PAGE = 5
    # 实时更新index
    HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
    

    2、安装elasticsearch-py

    haystack操作es还需要python的es驱动。兼容性见官网

    1563194487143.png

    根据官网,选择2.4.1版本

    pip install elasticsearch==2.4.1
    

    至此,环境搭建完成。相对应的es,es-ik,haystack,es-python的版本请保持一致。

    二、新闻搜索

    1.业务流程分析

    • 判断是否传递查询参数q
    • 如果没有传递q,则直接返回热门新闻数据
    • 如果有传递,则返回查询结果
    • 分页

    2. 接口设计

    1. 接口说明:
    类目 说明
    请求方法 POST
    url定义 /news/search/
    参数格式 查询参数
    1. 参数说明:
    参数名 类型 是否必须 描述
    q 字符串 查询的关键字
    page 整数 页码
    1. 返回结果:

      搜索页面html

    3.后端代码

    1. 创建haystack数据模型

      在apps/news/目录下创建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哪些数据会被
          放进查询回的模型对象中,以及通过哪些字段进行索引和查询
          """
          # 这字段必须这么写,用来告诉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):
              """
              返回要建立索引的数据查询集
              :param using:
              :return:
              """
              return self.get_model().objects.filter(is_delete=False)
      
    2. 创建索引数据模板

      根据上面创建的模型中的第一个text字段中的use_template=True参数,还需要创建一个索引数据模板,用来告诉搜索引擎需要索引哪些字段。

      在templates中创建文件search/indexes/yourappname/modelname_text.txt,所以本项目需要创建search/indexes/news/news_text.txt,文件内容如下:

      {{ object.title }}
      {{ object.digest }}
      {{ object.content }}
      {{ object.author.username }}
      
    3. 创建索引

      按上面的步骤配置好后,就可以运行haystack的命令创建索引了

      ~$ python manage.py rebuild_index
      
    4. 视图代码

      在news/views.py中添加如下视图

      from haystack.generic_views import SearchView
      
      class NewsSearchView(SearchView):
          """
          新闻搜索视图
          """
          # 设置搜索模板文件
          template_name = 'news/search.html'
      
          # 重写get请求,如果请求参数q为空,返回模型News的热门新闻数据
          # 否则根据参数q搜索相关数据
          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="请输入要搜索的内容" name="q" class="search-control" value="{{ query }}">
                      {% else %}
                          <input type="search" placeholder="请输入要搜索的内容" 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">搜索结果 <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>没有找到你想要的找的内容.</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 ================= */
      

    相关文章

      网友评论

          本文标题:7.3 django项目-新闻博客系统之新闻搜索3

          本文链接:https://www.haomeiwen.com/subject/pvbeectx.html