12搜索功能实现

docker

简介

  • 使用容器让创建,部署,运行应用程序更简单的一个工具
  • Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口
  • 它是目前最流行的 Linux 容器解决方案
  • Docker 将应用程序与该程序的依赖,打包在一个文件里面,运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样
  • 有了 Docker,就不用担心环境问题
  • 容器除了运行其中应用外,基本不消耗额外的系统资源,使得应用的性能很高,同时系统的开销尽量小
  • 传统虚拟机方式运行 10 个不同的应用就要起 10 个虚拟机,而Docker 只需要启动 10 个隔离的应用即可
  • docker 架构
    在这里插入图片描述
    在这里插入图片描述

基本概念

  • Docker 中包括三个基本的概念:
    • Image(镜像)
    • Container(容器)
    • Repository(仓库)

Image(镜像)

  • Image(镜像)是 Docker 运行容器的前提,仓库是存放镜像的场所,可见镜像更是 Docker 的核心
  • Docker 镜像可以看作是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等),镜像不包含任何动态数据,其内容在构建之后也不会被改变
  • 镜像(Image)就是一堆只读层(read-only layer)的统一视角,也许这个定义有些难以理解,下面的这张图能够帮助读者理解镜像的定义
    在这里插入图片描述
    从左边我们看到了多个只读层,它们重叠在一起。除了最下面一层,其它层都会有一个指针指向下一层。这些层是Docker 内部的实现细节,并且能够在主机的文件系统上访问到。统一文件系统 (union file system) 技术能够将不同的层整合成一个文件系统,为这些层提供了一个统一的视角,这样就隐藏了多层的存在,在用户的角度看来,只存在一个文件系统。我们可以在图片的右边看到这个视角的形式

Container (容器)

  • 容器 (container) 的定义和镜像 (image) 几乎一模一样,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的
    在这里插入图片描述

  • 由于容器的定义并没有提及是否要运行容器,所以实际上,容器 = 镜像 + 读写层

Repository (仓库)

Docker 仓库是集中存放镜像文件的场所。镜像构建完成后,可以很容易的在当前宿主上运行,但是, 如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry (仓库注册服务器)就是这样的服务。有时候会把仓库 (Repository) 和仓库注册服务器 (Registry) 混为一谈,并不严格区分。Docker 仓库的概念跟 Git 类似,注册服务器可以理解为 GitHub 这样的托管服务。实际上,一个 Docker Registry 中可以包含多个仓库 (Repository) ,每个仓库可以包含多个标签 (Tag),每个标签对应着一个镜像。所以说,镜像仓库是 Docker 用来集中存放镜像文件的地方类似于我们之前常用的代码仓库。
通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本 。我们可以通过<仓库名>:<标签>的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签.。

  • 仓库又可以分为两种形式:
    • public(公有仓库)
    • private(私有仓库)

安装 docker

sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common
  • 添加Docker的官方GPG密钥:
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
  • 添加仓库。
sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"
  • 安装docker ce
    sudo apt-get install docker-ce docker-ce-cli containerd.io
  • 测试
    sudo docker run hello-world
  • 添加当前用户到 docker 用户组
    sudo usermod -aG docker xiaoge #xiaoge 为当前用户名

新闻搜索功能实现

需求分析

  • 可以使用数据库的模糊查询(like 关键字)来实现,但效率极低
    SELECT * FROM users WHERE username LIKE "%python%" AND is_delete=0; % 表示任意多个字符,_ 表示一个任意字符
  • 在多个字段中查询,使用 like 关键字不方便
  • 因此使用搜索引擎来实现全文检索

搜索引擎原理

  • 搜索引擎并不是直接在数据库中进行查询
  • 会对数据库中的数据进行一遍预处理,单独建立一份索引结构数据
  • 类似字典的索引检索页

elasticsearch

  • 开源
  • 搜索引擎首选
  • 底层是开源库 lucene
  • REST API 的操作接口
    在这里插入图片描述
  • 搜索引擎在对数据构建索引时,需要进行分词处理,分词是指将一句话拆解成多个单字或词,这些字或词便是这句话的关键词
  • Elasticsearch 不支持对中文进行分词建立索引,需要配合拓展elasticsearch-analysis-ik来实现中文分词处理

使用 docker 安装 elasticsearch

获取镜像

  • 获取镜像 image
  • 拉取镜像到本地仓库,类似于 git
docker image pull delron/elasticsearch-ik:2.4.6-1.0
#由于 pull 拉取会比较慢,所以不推荐,在百度网盘中获取 elasticsearch-ik-2.4.6_docker.tar 文件更快
#然后倒入镜像
docker load -i elasticsearch-ik-2.4.6_docker.tar
  • 查看本地仓库是否有这个镜像
docker images
#或者 docker image ls

在这里插入图片描述

更改配置

  • 同样将配置文件 elasticsearh.zip 也下载下来,解压,将 elasticsearch/config/elasticsearch.yml 中的 ip 地址改为 0.0.0.0,端口改为 8002
    在这里插入图片描述

创建 docker 容器

  • 创建 docker 容器并运行
docker run -dti --network=host --name=elasticsearch -v /home/xiaoge/web_prv/elasticsearch/config:/usr/share/elasticsearch/config delron/elasticsearch-ik:2.4.6-1.0
#根据自己的 elasticsearch 所在位置更改上面代码的 config: 前面的路径,-ik表示可以搜索中文
  • 查看是否创建成功
docker ps -a
#docker container ls -a
#加上 -a 表示不管是否启动都查出来

在这里插入图片描述
通过 STATUS 可以看出已经启动成功

  • 运行如下命令,如果有显示则 elasticsearch 配置成功,如下图
    curl 127.0.0.1:8002 #端口要对应配置中的端口
    在这里插入图片描述
  • 停止容器
    docker stop elasticsearch
  • 启动容器
    docker start elasticsearch
  • 删除容器
docker rm elasticsearch
#删除容器时,容器必须是停止状态,否则会报错
  • 查看容器日志
    docker logs elasticsearch
    在这里插入图片描述

安装 django-haystack elasticsearch

  • 进入项目的虚拟环境,安装相关包
pip install django-haystack
pip install elasticsearch==2.4.1
#如果安装 django-haystack 出现下面情况(下载完安装包就卡住不安装了或者报错 
distutils.errors.DistutilsError: Could not find suitable distribution for Requirement.parse('setuptools_scm')
就先安装 setuptools-scm)

在这里插入图片描述

django 项目

settings.py

  • settings 文件的 INSTALLED_APPS 中添加 haystack
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'apps.course',
    'apps.users',
    'apps.news',
    'apps.doc',
    'apps.verifications',
    'haystack',
]
  • 添加如下配置
ELASTICSEARCH_DSL = {
    'default': {
        'hosts': '127.0.0.1:8002'	#ip 和端口都对应上
    },
}

# Haystack
HAYSTACK_CONNECTIONS = {
    'default': {
        'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine',
        'URL': 'http://127.0.0.1:8002/',  # 此处为 elasticsearch 运行的服务器 ip 地址和端口
        'INDEX_NAME': 'web_prv',  # 指定 elasticsearch 建立的索引库的名称
    },
}

# 设置每页显示的数据量
HAYSTACK_SEARCH_RESULTS_PER_PAGE = 5
# 当数据库改变时,会自动更新索引
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'

后端功能实现

  • 在 apps/news 创建 search_indexes.py 文件
  • 在 templates 创建 search/indexes/news/news_text.txt 文件(模板名_text.txt)

apps/news/search_indexes.py

#!/home/xiaoge/env python3.6
# -*- coding: utf-8 -*-
"""
  __title__ = ' search_indexes.py'
  __author__ = 'xiaoge'
  __mtime__ = '2019/6/20 上午10:27'
  # code is far away from bugs with the god animal protecting
      I love animals. They taste delicious.
               ┏┓      ┏┓
              ┏┛┻━━━━━━┛┻┓
              ┃        ☃ ┃
              ┃  ┳┛ ┗┳   ┃
              ┃    ┻     ┃
              ┗━┓      ┏━┛
                ┃      ┗━━━┓
                ┃ 神兽保佑  ┣┓
                ┃永无BUG!┏┛
                ┗┓┓┏━┳┓┏┛
                ┃┫┫  ┃┫┫
               ┗┻┛  ┗┻┛
"""
from haystack import indexes

from .models import News


class NewsIndex(indexes.SearchIndex,indexes.Indexable):
    """
    News 索引数据模型类
    继承 indexes 的两个类 SearchIndex Indexable
    """
    # document=True use_template=True作用:为 templates/search/indexes/news/news_text.txt 的三个字段创建索引
    text = indexes.CharField(document=True,use_template=True)
    #下面这些字段可以不定义
    # 后面模板要用到的字段就可以不用 object,如 {{ one_news.object.image_url }} 可以直接写成 {{ one_news.image_url }}
    id = indexes.IntegerField(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):
        """
        限制查询范围,此处 tag_id 为1-6,为全查询
        :param using:
        :return:要建立索引的数据查询集
        """
        # return self.get_model().objects.filter(is_delete=False,tag_id=1)
        #字段名__in 表示字段的值在后面的列表中
        return self.get_model().objects.filter(is_delete=False,tag_id__in=[1,2,3,4,5,6])

apps/news/views.py

  • 添加类 SearchView
import logging
import json

from django.shortcuts import render
from django.views import View
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger  #页码不是整数类型
from django.http import Http404

from web_prv import settings

from haystack.views import SearchView as _SearchView

from apps.news import models
from apps.news import constants

from utils.json_fun import to_json_data
from utils.res_code import Code,error_map
logger = logging.getLogger('django')

# def index(request):
#     return render(request,'news/index.html')


class IndexView(View):
    """
    constants
    """
    def get(self,request):
        # tags = models.Tag.objects.filter(is_delete=False)
        #通过表中的is_delete判断是否被删除
        tags = models.Tag.objects.only('id','name').filter(is_delete=False) #only确定要查询的字段,其他的不查
        #使用select_related关联表优化,一对一和一对多都可用,查询集,用切片获取对象
        hot_news = models.HotNews.objects.select_related('news').only('news__title','news__image_url',\
          'news_id').filter(is_delete=False).order_by('priority','-news__clicks')[0:constants.SHOW_HOTNEWS_COUNT]

        # context = {
        #     'tags':tags,
        # }
        #用模板上下文     将当前方法下的变量都传进locals,python内置函数
        # return render(request,'news/index.html',context=context)
        return render(request,'news/index.html',locals())

#ajax请求
#传参 tag_id  page
#后台的返回  图片,标题,摘要,作者,标签,更新时间,文章id
#请求方式:GET
#/news/
#url查询字符串   ?tag_id=1&page=2    /news/?tag_id=1&page=2


class NewsListView(View):
    """
    对于一对一字段(OneToOneField)和外键字段(ForeignKey),可以使用select_related 来对QuerySet进行优化。
    select_related 返回一个QuerySet,当执行它的查询时它沿着外键关系查询关联的对象的数据。
    它会生成一个复杂的查询并引起性能的损耗,但是在以后使用外键关系时将不需要数据库查询
    """

    def get(self,request):
        """
        # 1.获取参数
        # 2.校验参数
        # 3.从数据库拿数据
        # 4.分页
        # 5.序列化输出
        #6.返回给前端
        :param request:
        :return:
        """
        #1.获取参数,校验参数
        #如果通过不正当手段导致int转换不成功时,返回最新资讯给前端
        try:
            # 如果没传或者格式不对得不到数据,为0,则把最新资讯id设置为0,当刚进入首页没进行选择标签时起作用,即默认为最新资讯
            tag_id = int(request.GET.get('tag_id',0))   #get到的是str类型,存入数据库是int类型
        except Exception as e:
            logger.error('标签错误:\n{}'.format(e))
            tag_id = 0
        try:
            page = int(request.GET.get('page',1))
        except Exception as e:
            logger.error('页码错误:\n{}'.format(e))
            page = 1
        #3.从数据库中拿数据
        #title,digest(摘要),image_url,update_time
        #select_related用于多表查询,是优化措施,参数是关联外键的字段,id为主键,无论怎样都会查到,所以不需要列出来
        #News中的tag字段对应Tag标签中的name字段,News中的author字段对应Users中的username字段
        news_queryset = models.News.objects.select_related('tag','author').only('title','digest',\
                        'image_url','update_time','tag__name','author__username')
        #is_delete:是否被逻辑删除,tag_id=0在queryset中表示为空
        news = news_queryset.filter(is_delete=False,tag_id=tag_id) or news_queryset.filter(is_delete=False)
        #4.分页
        paginator = Paginator(news,constants.PER_PAGE_NEWS_COUNT)   #第一个参数是数据,第二个参数是每页的新闻个数
        try:
            news_info = paginator.page(page)    #对应页的新闻
        except EmptyPage:
            logger.error('用户访问页数大于总页数')
            news_info = paginator.page(paginator.num_pages) #展示最大页码的新闻
        #5.序列化输出
        news_info_list = []
        for n in news_info:
            news_info_list.append({
                'id':n.id,
                'title':n.title,
                'digest':n.digest,
                'image_url':n.image_url,
                'update_time':n.update_time.strftime('%Y-%m-%d %H:%M:%S'),
                'tag_name':n.tag.name,  #tag的name属性
                'author':n.author.username,
            })
        data = {    #返回给前端的,名一定要和前端对应否则没作用,细心
            'news':news_info_list,
            'total_pages':paginator.num_pages    #总页码数
        }
        #6.返回给前端
        return to_json_data(data=data)


class NewsBanner(View):
    """
    ajax
    """
    #不传参,不改数据,用get
    def get(self,request):
        banners = models.Banner.objects.select_related('news').only('image_url','news_id','news__title').\
            filter(is_delete=False).order_by('priority')[0:constants.SHOW_BANNER_COUNT]
        #序列化输出
        banners_info_list = []
        for b in banners:
            banners_info_list.append(
                {
                    'image_url':b.image_url,
                    'news_id':b.news_id,
                    'news_title':b.news.title
                }
            )
        data = {
            'banners':banners_info_list,
        }
        return to_json_data(data=data)  #返回给前端

#文章详情
#参数:news_id
#title author__username update_time tag_names content

class NewsDetailView(View):
    """
    /news/<int:news_id>/
    将文章id携带到url
    """
    def get(self,request,news_id):
        news = models.News.objects.select_related('tag','author').only('title','content','update_time',\
                   'tag__name','author__username').filter(is_delete=False,id=news_id).first()
        if news:
            """
            字段:content,update_time,parent.username,parent.content,parent.update_time
            """
            comments = models.Comments.objects.select_related('author','parent').only('content','update_time',\
                           'author__username','parent__content','parent__author__username','parent__update_time')\
                            .filter(is_delete=False,news_id=news_id)
            #序列化输出
            comments_list = []
            for comm in comments:
                comments_list.append(comm.to_dict_data())
            return render(request,'news/news_detail.html',locals())
        else:
            raise Http404('新闻id不存在'.format(news_id))


class NewsCommentView(View):
    """
    给文章评论和给评论人评论用一个接口,只不过确定是否有父类,要对应想要评论的父类
    局部刷新,用ajax
    参数:content,parent_id,news_id(通过url传递)
    请求方式:post
    url:/news/<int:news_id>/comments/
    """
    def post(self,request,news_id):
        #1.获取参数
        #2.校验参数
        #3.存入数据库
        #4.返回给前端
        if not request.user.is_authenticated:   #判断是否登录
            return to_json_data(errno=Code.SESSIONERR,errmsg=error_map[Code.SESSIONERR])    #用户未登陆

        if not models.News.objects.only('id').filter(is_delete=False,id=news_id).exists():
            return to_json_data(errno=Code.PARAMERR,errmsg='新闻不存在')
        json_data = request.body

        if not json_data:
            return to_json_data(errno=Code.PARAMERR,errmsg=error_map[Code.PARAMERR])
        dict_data = json.loads(json_data.decode('utf8'))
        content = dict_data.get('content')

        if not content:
            return to_json_data(errno=Code.PARAMERR,errmsg='评论的内容不能为空')
        #父评论验证:父评论是否存在,parent_id必须是数字,数据库里面是否存在,父评论的新闻的id是否和news_id一致
        parent_id = dict_data.get('parent_id')  #判断前端传来的是否有parent_id
        try:    #正常通过页面评论parent_id是数字,如果通过其他渠道可能会出错
            if parent_id:
                parent_id = int(parent_id)
                if not models.Comments.objects.only('id').filter(is_delete=False,
                                                                 id=parent_id,
                                                                 news_id=news_id).exists():
                    return to_json_data(errno=Code.PARAMERR,errmsg=error_map[Code.PARAMERR])
        except Exception as e:
            logger.info('前端传的parent_id异常{}'.format(e))
            return to_json_data(errno=Code.PARAMERR,errmsg='未知异常')

        #存到数据库
        new_comment = models.Comments()
        new_comment.content = content
        new_comment.news_id = news_id
        new_comment.author = request.user
        new_comment.parent_id = parent_id if parent_id else None
        new_comment.save()

        return to_json_data(data=new_comment.to_dict_data())


class SearchView(_SearchView):
    """

    """
    template = 'news/search.html'
    #重写响应方式,如果请求参数 q 为空,返回模型 News 的热门新闻数据,否则根据参数 q 搜索相关数据

    def create_response(self):
        """
        如果没有要搜索的数据即 q 为空时,显示所有数据,按点击量排序
        如果有要搜索的数据,即 q=* 时,用父类 SearchView 的 create_response 方法进行搜索
        :return:
        """
        kw = self.request.GET.get('q','')

        if not kw:  #没搜索时显示热门新闻,
            show_all = True #显示所有数据
            hot_news = models.HotNews.objects.select_related('news').only('news__title','news__image_url',\
                            'news__id').filter(is_delete=False).order_by('priority','-news__clicks')
            # 对 hot_news 进行分页,每页五个数据
            paginator = Paginator(hot_news,settings.HAYSTACK_SEARCH_RESULTS_PER_PAGE)

            try:
                page = paginator.page(int(self.request.GET.get('page',1)))

            except PageNotAnInteger:
                #如果参数 page 的数据类型不是整形,则返回第一页数据
                page = paginator.page(1)

            except EmptyPage:
                #用户访问的页数大于实际页数,返回最后一页的数据
                page = paginator.page(paginator.num_pages)

            return render(self.request,self.template,locals())

        else:
            show_all = False
            qs = super(SearchView,self).create_response()
            return qs

templates/search/indexes/news/news_text.txt

{{ object.title }}
{{ object.digest }}
{{ object.content }}

apps/news/urls.py

from django.urls import path, re_path

from apps.news import views

app_name = 'news'

urlpatterns = [
    # path('',views.index,name='index'),
    path('',views.IndexView.as_view(),name='index'),
    path('news/',views.NewsListView.as_view(),name='news_list'),
    path('news/banners/',views.NewsBanner.as_view(),name='news_banner'),
    path('news/<int:news_id>/',views.NewsDetailView.as_view(),name='news_detail'),
    path('news/<int:news_id>/comments/',views.NewsCommentView.as_view(),name='news_comments'),
    path('search/',views.SearchView(),name='search')
]
  • 然后执行命令 python manage.py rebuild_index 创建搜索索引

前端功能实现

templates/news1/search.html

#将 class= 'main-contain' 的div标签的内容替换成下面代码
<div class="main-contain ">
        <!-- search-box start -->
        <div class="search-box">
            <form action="" style="display: inline-flex;">

                <input type="search" placeholder="请输入要搜索的内容" name="q" class="search-control">


                <input type="submit" value="搜索" class="search-btn">
            </form>
            <!-- 可以用浮动 垂直对齐 以及 flex  -->
        </div>
        <!-- search-box end -->
        <!-- content start -->
        <div class="content">
            <!-- search-list start -->
            {% if not show_all %}
                <div class="search-result-list">
                    <h2 class="search-result-title">
                        搜索结果 <span style="font-weight: 700;color: #ff6620;">{{ paginator.num_pages }}</span>页
                    </h2>
                    <ul class="news-list">
                        {# 导入自带高亮功能 #}
                        {% load highlight %}
                        {% for one_news in page.object_list %}
                            <li class="news-item clearfix">
                                <a href="{% url 'news:news_detail' one_news.id %}" class="news-thumbnail"
                                   target="_blank">
                                    <img src="{{ one_news.object.image_url }}">
                                </a>
                                <div class="news-content">
                                    <h4 class="news-title">
                                        <a href="{% url 'news:news_detail' one_news.id %}">
                                            {% highlight one_news.title with query %}
                                        </a>
                                    </h4>
                                    <p class="news-details">{% highlight one_news.digest with query %}</p>
                                    <div class="news-other">
                                        <span class="news-type">{{ one_news.object.tag.name }}</span>
                                        <span class="news-time">{{ one_news.object.update_time }}</span>
                                        <span
                                                class="news-author">{% highlight one_news.object.author.username with query %}

                                      </span>
                                    </div>
                                </div>
                            </li>
                        {% endfor %}


                    </ul>
                </div>

            {% else %}

                <div class="news-contain">
                    <div class="hot-recommend-list">
                        <h2 class="hot-recommend-title">热门推荐</h2>
                        <ul class="news-list">

                            {% for one_hotnews in page.object_list %}

                                <li class="news-item clearfix">
                                    <a href="#" class="news-thumbnail">
                                        <img src="{{ one_hotnews.news.image_url }}">
                                    </a>
                                    <div class="news-content">
                                        <h4 class="news-title">
                                            <a href="{% url 'news:news_detail' one_hotnews.news.id %}">{{ one_hotnews.news.title }}</a>
                                        </h4>
                                        <p class="news-details">{{ one_hotnews.news.digest }}</p>
                                        <div class="news-other">
                                            <span class="news-type">{{ one_hotnews.news.tag.name }}</span>
                                            <span class="news-time">{{ one_hotnews.update_time }}</span>
                                            <span class="news-author">{{ one_hotnews.news.author.username }}</span>
                                        </div>
                                    </div>
                                </li>

                            {% endfor %}


                        </ul>
                    </div>
                </div>

            {% endif %}

            <!-- search-list end -->
            <!-- news-contain start -->

            {# 分页导航 #}
            <div class="page-box" id="pages">
                <div class="pagebar" id="pageBar">
                    <a class="a1">{{ page.paginator.count }}条</a>
                    {# 上一页的URL地址 #}
                    {% if page.has_previous %}
                        {% if query %}
                            <a href="{% url 'news:search' %}?q={{ query }}&amp;page={{ page.previous_page_number }}"
                               class="prev">上一页</a>
                        {% else %}
                            <a href="{% url 'news:search' %}?page={{ page.previous_page_number }}" class="prev">上一页</a>
                        {% endif %}
                    {% endif %}
                    {# 列出所有的URL地址 #}
                    {% for num in page.paginator.page_range|slice:":10" %}
                        {% if num == page.number %}
                            <span class="sel">{{ page.number }}</span>
                        {% else %}
                            {% if query %}
                                <a href="{% url 'news:search' %}?q={{ query }}&amp;page={{ num }}"
                                   target="_self">{{ num }}</a>
                            {% else %}
                                <a href="{% url 'news:search' %}?page={{ num }}" target="_self">{{ num }}</a>
                            {% endif %}
                        {% endif %}
                    {% endfor %}

                    {# 如果页数大于10,则打两点 #}
                    {% if page.paginator.num_pages > 10 %}
                        ..

                        {% if query %}
                            <a href="{% url 'news:search' %}?q={{ query }}&amp;page={{ page.paginator.num_pages }}"
                               target="_self">{{ page.paginator.num_pages }}</a>
                        {% else %}
                            <a href="{% url 'news:search' %}?page={{ page.paginator.num_pages }}"
                               target="_self">{{ page.paginator.num_pages }}</a>
                        {% endif %}

                    {% endif %}

                    {# 下一页的URL地址 #}
                    {% if page.has_next %}
                        {% if query %}
                            <a href="{% url 'news:search' %}?q={{ query }}&amp;page={{ page.next_page_number }}"
                               class="next">下一页</a>
                        {% else %}
                            <a href="{% url 'news:search' %}?page={{ page.next_page_number }}" class="next">下一页</a>
                        {% endif %}
                    {% endif %}
                </div>
            </div>

            <!-- news-contain end -->
        </div>
        <!-- content end -->
    </div>

{% extends 'base/base.html' %}
{% load static %}
{% block title %}<title>searchPage</title>{% endblock %}
{% block link %}
  <link rel="stylesheet" href="{% static 'css/news/search.css' %}">{% endblock %}
{% block contain %}
  {#        <div class="main-contain ">#}
  {#            <!-- search-box start -->#}
  {#            <div class="search-box">#}
  {#                <form action="" style="display: inline-flex;">#}
  {##}
  {#                    <input type="search" placeholder="请输入要搜索的内容" name="q" class="search-control">#}
  {##}
  {##}
  {#                    <input type="submit" value="搜索" class="search-btn">#}
  {#                </form>#}
  {#                <!-- 可以用浮动 垂直对齐 以及 flex  -->#}
  {#            </div>#}
  {#            <!-- search-box end -->#}
  {#            <!-- content start -->#}
  {#            <div class="content">#}
  {#                <!-- search-list start -->#}
  {##}
  {##}
  {#                <!-- search-list end -->#}
  {#                <!-- news-contain start -->#}
  {##}
  {#                <div class="news-contain">#}
  {#                    <div class="hot-recommend-list">#}
  {#                        <h2 class="hot-recommend-title">热门推荐</h2>#}
  {#                        <ul class="news-list">#}
  {##}
  {#                            <li class="news-item clearfix">#}
  {#                                <a href="#" class="news-thumbnail">#}
  {#                                    <img src="{% static 'img/python_gui.jpg' %}">#}
  {#                                </a>#}
  {#                                <div class="news-content">#}
  {#                                    <h4 class="news-title">#}
  {#                                        <a href="#">Python GUI 教程 25行代码写一个小闹钟</a>#}
  {#                                    </h4>#}
  {#                                    <p class="news-details">#}
  {#                                    </p>#}
  {#                                    <div class="news-other">#}
  {#                                        <span class="news-type">PythonGUI</span>#}
  {#                                        <span class="news-time">11/11 18:22</span>#}
  {#                                        <span class="news-author">python</span>#}
  {#                                    </div>#}
  {#                                </div>#}
  {#                            </li>#}
  {##}
  {#                            <li class="news-item clearfix">#}
  {#                                <a href="#" class="news-thumbnail">#}
  {#                                    <img src="{% static 'img/python_advanced.jpg' %}">#}
  {#                                </a>#}
  {#                                <div class="news-content">#}
  {#                                    <h4 class="news-title">#}
  {#                                        <a href="#">python高性能编程方法一</a>#}
  {#                                    </h4>#}
  {#                                    <p class="news-details">#}
  {#                                    </p>#}
  {#                                    <div class="news-other">#}
  {#                                        <span class="news-type">Python高级</span>#}
  {#                                        <span class="news-time">11/11 17:13</span>#}
  {#                                        <span class="news-author">python</span>#}
  {#                                    </div>#}
  {#                                </div>#}
  {#                            </li>#}
  {##}
  {#                            <li class="news-item clearfix">#}
  {#                                <a href="#" class="news-thumbnail">#}
  {#                                    <img src="{% static 'img/jichujiaochen.jpeg' %}">#}
  {#                                </a>#}
  {#                                <div class="news-content">#}
  {#                                    <h4 class="news-title">#}
  {#                                        <a href="#">python基础 split 和 join函数比较</a>#}
  {#                                    </h4>#}
  {#                                    <p class="news-details">#}
  {#                                    </p>#}
  {#                                    <div class="news-other">#}
  {#                                        <span class="news-type">Python基础</span>#}
  {#                                        <span class="news-time">11/11 16:09</span>#}
  {#                                        <span class="news-author">python</span>#}
  {#                                    </div>#}
  {#                                </div>#}
  {#                            </li>#}
  {##}
  {#                            <li class="news-item clearfix">#}
  {#                                <a href="#" class="news-thumbnail">#}
  {#                                    <img src="{% static 'img/python_web.jpg' %}">#}
  {#                                </a>#}
  {#                                <div class="news-content">#}
  {#                                    <h4 class="news-title">#}
  {#                                        <a href="#">Django调试工具django-debug-toolbar安装使用教程</a>#}
  {#                                    </h4>#}
  {#                                    <p class="news-details">#}
  {#                                    </p>#}
  {#                                    <div class="news-other">#}
  {#                                        <span class="news-type">python框架</span>#}
  {#                                        <span class="news-time">11/11 15:28</span>#}
  {#                                        <span class="news-author">python</span>#}
  {#                                    </div>#}
  {#                                </div>#}
  {#                            </li>#}
  {##}
  {#                        </ul>#}
  {#                    </div>#}
  {#                </div>#}
  {##}
  {##}
  {#                <!-- news-contain end -->#}
  {#            </div>#}
  {#            <!-- content end -->#}
  {#        </div>#}
  <div class="main-contain ">
    <!-- search-box start -->
    <div class="search-box">
      <form action="" style="display: inline-flex;">

        <input type="search" placeholder="请输入要搜索的内容" name="q" class="search-control">


        <input type="submit" value="搜索" class="search-btn">
      </form>
      <!-- 可以用浮动 垂直对齐 以及 flex  -->
    </div>
    <!-- search-box end -->
    <!-- content start -->
    <div class="content">
      <!-- search-list start -->
      {% if not show_all %}
        <div class="search-result-list">
          <h2 class="search-result-title">
            搜索结果 <span style="font-weight: 700;color: #ff6620;">{{ paginator.num_pages }}</span>页
          </h2>
          <ul class="news-list">
            {# 导入自带高亮功能 #}
            {% load highlight %}
            {% for one_news in page.object_list %}
              <li class="news-item clearfix">
                <a href="{% url 'news:news_detail' one_news.id %}" class="news-thumbnail"
                   target="_blank">
                  <img src="{{ one_news.object.image_url }}">
                </a>
                <div class="news-content">
                  <h4 class="news-title">
                    <a href="{% url 'news:news_detail' one_news.id %}">
                      {% highlight one_news.title with query %}
                    </a>
                  </h4>
                  <p class="news-details">{% highlight one_news.digest with query %}</p>
                  <div class="news-other">
                    <span class="news-type">{{ one_news.object.tag.name }}</span>
                    <span class="news-time">{{ one_news.object.update_time }}</span>
                    <span
                            class="news-author">{% highlight one_news.object.author.username with query %}

                                      </span>
                  </div>
                </div>
              </li>
            {% endfor %}


          </ul>
        </div>

      {% else %}

        <div class="news-contain">
          <div class="hot-recommend-list">
            <h2 class="hot-recommend-title">热门推荐</h2>
            <ul class="news-list">

              {% for one_hotnews in page.object_list %}

                <li class="news-item clearfix">
                  <a href="#" class="news-thumbnail">
                    <img src="{{ one_hotnews.news.image_url }}">
                  </a>
                  <div class="news-content">
                    <h4 class="news-title">
                      <a href="{% url 'news:news_detail' one_hotnews.news.id %}">{{ one_hotnews.news.title }}</a>
                    </h4>
                    <p class="news-details">{{ one_hotnews.news.digest }}</p>
                    <div class="news-other">
                      <span class="news-type">{{ one_hotnews.news.tag.name }}</span>
                      <span class="news-time">{{ one_hotnews.update_time }}</span>
                      <span class="news-author">{{ one_hotnews.news.author.username }}</span>
                    </div>
                  </div>
                </li>

              {% endfor %}


            </ul>
          </div>
        </div>

      {% endif %}

      <!-- search-list end -->
      <!-- news-contain start -->

      {# 分页导航 #}
      <div class="page-box" id="pages">
        <div class="pagebar" id="pageBar">
          <a class="a1">{{ page.paginator.count }}条</a>
          {# 上一页的URL地址 #}
          {% if page.has_previous %}
            {% if query %}
              <a href="{% url 'news:search' %}?q={{ query }}&amp;page={{ page.previous_page_number }}"
                 class="prev">上一页</a>
            {% else %}
              <a href="{% url 'news:search' %}?page={{ page.previous_page_number }}" class="prev">上一页</a>
            {% endif %}
          {% endif %}
          {# 列出所有的URL地址 #}
          {% for num in page.paginator.page_range|slice:":10" %}
            {% if num == page.number %}
              <span class="sel">{{ page.number }}</span>
            {% else %}
              {% if query %}
                <a href="{% url 'news:search' %}?q={{ query }}&amp;page={{ num }}"
                   target="_self">{{ num }}</a>
              {% else %}
                <a href="{% url 'news:search' %}?page={{ num }}" target="_self">{{ num }}</a>
              {% endif %}
            {% endif %}
          {% endfor %}

          {# 如果页数大于10,则打两点 #}
          {% if page.paginator.num_pages > 10 %}
            ..

            {% if query %}
              <a href="{% url 'news:search' %}?q={{ query }}&amp;page={{ page.paginator.num_pages }}"
                 target="_self">{{ page.paginator.num_pages }}</a>
            {% else %}
              <a href="{% url 'news:search' %}?page={{ page.paginator.num_pages }}"
                 target="_self">{{ page.paginator.num_pages }}</a>
            {% endif %}

          {% endif %}

          {# 下一页的URL地址 #}
          {% if page.has_next %}
            {% if query %}
              <a href="{% url 'news:search' %}?q={{ query }}&amp;page={{ page.next_page_number }}"
                 class="next">下一页</a>
            {% else %}
              <a href="{% url 'news:search' %}?page={{ page.next_page_number }}" class="next">下一页</a>
            {% endif %}
          {% endif %}
        </div>
      </div>

      <!-- news-contain end -->
    </div>
    <!-- content end -->
  </div>
{% endblock %}
<!-- main-contain  end -->
{#{% block domready %}#}
{#  <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>#}
  {#<script src="{% static 'js/commons.js' %}"></script>#}
  {#  <script src="{% static 'js/news/index.js' %}"></script>#}
{#{% endblock %}#}

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;
}
/* == 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;
}
/* === content end === */
/* ================= main end ================= */
/* === 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
}

.highlighted{
    color:coral;
    mso-ansi-font-weight: bold;
}
/* === current index end === */

猜你喜欢

转载自blog.csdn.net/xiaogeldx/article/details/92853900
今日推荐