目录
10.1 开发“首页”模块
10.2 Django缓存系统
通过前面课程内容的学习,已将企业门户网站的各个子模块构建完毕。在各个模块的开发过程中,穿插阐述了Python Web的关键知识点。从内容安排上来看,各个模块之间从简单到复杂、逐级递进,模块之间具有高度的复用性,这样的结构安排有利于读者循序渐进的掌握知识并且提升项目的开发效率。本节课将讲解企业门户网站的最后一个模块:首页模块。该模块作为一个集成模块,将会使用到之前构建的各个子模块的内容,通过数据筛选和渲染实现各个子模块的整合显示。另外,首页作为门户网站的初始页面,在页面设计的过程中将重点考虑其排版、美观等特性。一个符合美的标准的首页能够使网站的形象得到最大限度的提升,有利于提高用户关注度。另外,本节课也会阐述Django的缓存技巧以进一步优化网站性能。
接下来将会按照示例网站所示效果,对首页主体部分各个模块进行开发,包括:轮播横幅、企业概况、新闻动态、通知公告、科研基地、联系我们、产品中心。
(1)轮播横幅
根据首页页面效果,首页模块的广告横幅采用了多张图片滚动的形式,通过这种动态显示方式,可以包容更多的信息以吸引用户浏览。在轮播图上可以使用鼠标悬停以停止轮播,也可以通过单击轮播图左右按钮进行图片切换,还可以通过轮播图下方圆点计数器跳转图片。Bootstrap提供了现成的轮播组件,我们不需要编写额外的控制代码即可实现图片轮播功能。
下面进入具体的开发环节。找到homeApp应用下的templates文件夹,打开该文件夹下的home.html文件,在{% block content %}中添加轮播代码,详细代码如下:
<div id="ad" class="carousel slide" data-ride="carousel" data-interval="5000"> <ol class="carousel-indicators"> <li data-target="#ad" data-slide-to="0" class="active"></li> <li data-target="#ad" data-slide-to="1"></li> <li data-target="#ad" data-slide-to="2"></li> </ol> <div class="carousel-inner"> <div class="item active"> <img src="{% static 'img/banner1.jpg' %}" alt="广告横幅1"> </div> <div class="item"> <img src="{% static 'img/banner2.jpg' %}" alt="广告横幅2"> </div> <div class="item"> <img src="{% static 'img/banner3.jpg' %}" alt="广告横幅3"> </div> </div> <a class="left carousel-control" href="#ad" data-slide="prev"><span class="glyphicon glyphicon-chevron-left"></span></a> <a class="right carousel-control" href="#ad" data-slide="next"><span class="glyphicon glyphicon-chevron-right"></span></a> </div>
从以上代码看出,仅通过导入Bootstrap的轮播组件然后进行简单的配置即可实现图片轮播功能,整个设计并不需要编写额外的JS代码进行显式控制,开发人员也不再需要关注底层的实现细节。因此,使用Bootstrap可以极大的提高门户网站的开发效率。
效果如下所示:
(2)企业概况
根据首页显示效果,可以将主体部分分为4行:企业概况和新闻动态占1行(所占栅格数分别为4和8);通知公告、科研基地和联系我们占1行(所占栅格数均为4);产品中心占1整行;友情链接占1整行。因此,整个主体部分外层设计代码如下:
<div class="container"> <div class="row row-3"> <div class="col-md-4"> <!-- 企业概况 --> </div> <div class="col-md-8"> <!-- 新闻动态 --> </div> </div> <div class="row row-3"> <div class="col-md-4"> <!-- 通知公告 --> </div> <div class="col-md-4"> <!-- 科研基地 --> </div> <div class="col-md-4"> <!-- 联系我们 --> </div> </div> <div class="row row-3"> <!-- 产品中心 --> <div class="col-md-12 col-pro"> </div> </div> <div class="row row-3"> <!-- 友情链接 --> <div class="col-md-12"> </div> </div> </div>
另外,考虑到页面之间的样式独立,在项目根目录static文件夹下的css子文件夹中新增home.css文件,专门用于定制首页的css样式,添加完以后在{% block content %}内容里面将该css文件导入进来:
<link href="{% static 'css/home.css' %}" rel="stylesheet">
接下来设计企业概况部分,详细代码如下:
<span class="part1"> <a href="{% url 'aboutApp:survey' %}">企业概况</a> </span> <span class="part1 en"> / About Us </span> <div class="line1"> <div class="line2 theme"></div> </div> <div> <img class="img-responsive" src="{% static 'img/aboutCompany.jpg' %}"> <p class="text1"> 恒达科技有限企业,位于中国某高新技术产业开发区, 以社会公共安全领域为主要应用方向,提供极速、准确和防伪装的人脸识别产品。 </p> </div>
其对应的css代码为:
.part1{ font-size:22px; } .part1 a{ text-decoration:none; color:#666; } .en{ color:#828282; font-size:15px; } .line1{ height:3px; background:#d0d0d0; width:100%; margin-bottom:8px } .line2{ height:3px; background: #005197; width:94px; } .text1{ text-align:justify; font-size:13px; line-height:25px; color:#505050; margin-top:5px; }
最终效果如下图所示:
(3)新闻动态
新闻动态分为左右两部分:左边是展报(占5个栅格)、右边是新闻列表(占7个栅格)。展报部分主要用于展示近期重要的新闻信息,以图片形式来吸引用户进行新闻浏览。这里采用Bootstrap的轮播组件进行实现,与轮播横幅不同之处在于每张图像上均附有新闻标题。新闻列表部分主要显示新闻动态模块列表信息,这里将企业要闻和行业新闻合并到一起进行显示,而通知公告则放在下一小节介绍。
前面我们开发新闻模型使用了富文本编辑器Ueditor进行内容编辑和显示,此处为了能够实现展报功能,需要为新闻模型添加一个额外的图片字段用于存储展报。打开newsApp应用下的models.py文件,在MyNew模型中添加字段如下:
photo = models.ImageField(upload_to='news/', blank=True, null=True, verbose_name='展报')
然后在终端输入下述命令以同步数据库:
python manage.py makemigrations python manage.py migrate
打开后台管理系统,为新闻模型上传展报。接下来开始编写后台处理函数,打开homeApp应用下的views.py文件,重新编辑home函数如下
from newsApp.models import MyNew from django.db.models import Q def home(request): # 新闻展报 newList = MyNew.objects.all().filter(~Q( newType='通知公告')).order_by('-publishDate') postList = set() postNum = 0 for s in newList: if s.photo: postList.add(s) postNum += 1 if postNum == 3: # 只截取最近的3个展报 break # 新闻列表 if (len(newList) > 7): newList = newList[0:7] # 返回结果 return render(request, 'home.html', { 'active_menu': 'home', 'postList': postList, 'newList': newList, })
from django.shortcuts import render from newsApp.models import MyNew from django.db.models import Q def home(request): # 新闻展报 newList = MyNew.objects.all().filter(~Q(newType='通知公告')).order_by('-publishDate') postList = set() postNum = 0 for s in newList: if s.photo: postList.add(s) postNum += 1 if postNum == 3: # 只截取最近的3个展报 break # 新闻列表 if (len(newList) > 7): newList = newList[0:7] # 返回结果 return render(request, 'home.html', { 'active_menu': 'home', 'postList': postList, 'newList': newList, })
接下来编写前端页面代码,紧接着上一小节定义好的框架结构,在新闻动态部分填入相应的代码:
<span class="part1"> <a href="#">新闻动态</a> </span> <span class="part1 en"> / News </span> <a class="btn btn-default btn-xs more-btn" href="{% url 'newsApp:news' 'company' %}">+ 更多 </a> <div class="line1"> <div class="line2 theme"></div> </div> <div class="col-md-5"> <div id="myCarousel" class="carousel slide" data-ride="carousel"> <ol class="carousel-indicators nav-point"> {% for post in postList %} <li data-target="#myCarousel" data-slide-to="{{forloop.counter0}}" {% if forloop.first %} class="active" {% endif %}> </li> {% endfor %} </ol> <!-- 轮播(Carousel)项目 --> <div class="carousel-inner"> {% for post in postList %} <div {% if forloop.first %} class="item active" {% else %} class="item" {% endif %} style="background-size:cover;"> <a href="{% url 'newsApp:newDetail' post.id %}"> <img src="{{post.photo.url}}"></a> <div class="carousel-caption nav-title">{{post.title}}</div> </div> {% endfor %} </div> </div> </div> <div class="col-md-7"> <ul class="list-unstyled list-new"> {% for mynew in newList %} <li> <a href="{% url 'newsApp:newDetail' mynew.id %}"> {{mynew.title|truncatechars:"25"}}</a> <span>【{{mynew.publishDate|date:"Y-m-d"}}】</span> </li> {% endfor %} </ul> </div>
最后对样式进行设置,在home.css文件中添加代码:
.carousel { margin-bottom: 40px; } .carousel .item { background-color: #000; } .carousel .nav-point{ bottom:-14px; } .carousel .nav-title{ font-size:12px; bottom:-14px; } .carousel .item img { width: 100%; } .carousel-caption { z-index: 10; } .carousel-caption p { margin-bottom: 20px; font-size: 20px; line-height: 1.8; } .more-btn{ float:right; margin-top:7px; color:#828282; font-size:11px; } .list-new li{ border-bottom:1px dashed #eae7e7; line-height:40px; } .list-new a{ text-decoration:none; color:#666; font-size:13px; } .list-new a:hover{ color:#d30a1c; } .list-new span{ float:right; font-size:12px; } .list-new .public-detail{ float:right; color:#d30a1c; }
接下来启动项目,进入后台管理系统127.0.0.1:8000/admin,然后为想要重点展示的新闻添加新闻展报图片,如下图所示:
上述步骤基本实现了新闻动态子模块的开发,但是还遗留一个问题,当上传的展报图像尺寸不一致时,新闻展报在进行轮播的过程中会出现错位现象,影响页面的整体美观性。一种简单的方法就是直接定义好渲染图像的长宽大小,但是这种方式会造成图像的畸变。另一种方法就是将图片按比例裁剪成固定像素宽高的图片再进行输出,本节将采用jQThumb.js插件实现该功能,该插件下载网址:https://github.com/pakcheong/jqthumb。下载解压后找到根目录下的dist子文件夹,其中jqthumb.min.js文件即为我们最终需要的插件。通过引用jqthumb.min.js文件,只需要在原有HTML模板文件中添加几行配置代码,就可以解决缩略图对齐显示的问题。该插件兼容所有浏览器,包括IE6。
具体的,将jqthumb.min.js文件拷贝到项目根目录/static/js文件夹中,然后在home.html文件的{% block content %}中引入该插件:
<script src="{% static 'js/jqthumb.min.js' %}"></script>
然后添加对图像额外的裁剪代码:
<script> //处理缩略图 function DrawImage(hotimg) { $(hotimg).jqthumb({ width: '100%', // 宽度 height: '250px', // 高度 zoom: '1', // 缩放比例 method: 'auto' // 提交方法,用于不同的浏览器环境,默认为‘auto’ }); } </script>
然后将轮播项目的<img>标签代码修改如下:
<img src="{{post.photo.url}}" class="img-responsive" onl oad="DrawImage(this)">
至此,已完成新闻动态模块的开发。
最终效果如下:
(4)通知公告
通知公告部分主要是将新闻模型中类型为“通知公告”的新闻列表进行显示,参照上一小节的实现过程,在home函数中添加代码如下:
noteList = MyNew.objects.all().filter(Q(newType='通知公告')).order_by('-publishDate') if (len(noteList) > 4): noteList = noteList[0:4]
然后在render返回函数中将变量noteList一起导出。此处为了排版需求,只显示最近的4条通知。
接下来编辑前端页面,这里我们将通知公告模块放置在主体第二行,详细代码如下:
<span class="part1"> <a href="#">通知公告</a> </span> <span class="part1 en"> / Public Release </span> <a class="btn btn-default btn-xs more-btn" href="{% url 'newsApp:news' 'notice' %}">+ 更多 </a> <div class="line1"> <div class="line2 theme"></div> </div> <div> <img class="img-responsive" src="{% static 'img/note.jpg' %}"> <ul class="list-unstyled list-new"> {% for note in noteList %} <li> <a href="{% url 'newsApp:newDetail' note.id %}"> {{note.title|truncatechars:"25"}} </a> <a href="{% url 'newsApp:newDetail' note.id %}" class="public-detail"> 查看详情>> </a> </li> {% endfor %} </ul> </div>
最终效果如下图所示:
(5)科研基地
科研基地模块主要用来显示科研基地相关的介绍性信息,包括静态图片和静态文字的渲染,内容不需要依赖后台服务器,该模块实现相对简单。本小节直接给出对应的前端代码:
<!-- 科研基地 --> <span class="part1"> <a href="{% url 'scienceApp:science' %}">科研基地</a> </span> <span class="part1 en"> / Technology Center </span> <div class="line1"> <div class="line2 theme"></div> </div> <div> <a href="{% url 'scienceApp:science' %}"><img class="img-responsive" src="{% static 'img/ky.jpg' %}"></a> <p class="text1"> <font color="#d30a1c">恒达科技科研基地</font> 恒达科研基地分为计算机视觉、 机器人和视觉深度学习三个事业部,共拥有... </p> </div>
最终效果如下图所示:
(6)联系我们
与上一小节相同,“联系我们”子模块内容均为静态内容,下面给出前端页面代码:
<span class="part1"> <a href="{% url 'contactApp:contact' %}">联系我们</a> </span> <span class="part1 en"> / Contact us </span> <div class="line1"> <div class="line2 theme"></div> </div> <div> <ul class="list-unstyled procurement-li"> <li>业务质询一:111-XXXXXX</li> <li>业务质询二:222-XXXXXX</li> <li>咨询电话:0111-XXXXXX</li> <li>企业传真:0222-XXXXXX</li> <li>地址:某某某新区某某大道1号</li> <li>邮编:XXXXXX</li> <li> 网址:<a href="http://python3web.com">http://python3web.com</a> </li> </ul> <div class="platform"><a href="{% url 'contactApp:contact' %}">详情</a></div> </div>
接下来在home.css文件中添加css样式设置:
.procurement-li{ line-height:35px; color:#666; font-size:13px; } .procurement-li a{ text-decoration:none; color:#666; } .platform{ width:100%; height:45px; background:#eae7e7; color:#fff; text-align:center; font-size:17px; line-height:42px; } .platform a{ text-decoration:none; color:#666; } .platform a:hover{ color:#d30a1c; }
最终效果如下:
(7)产品中心
根据示例网站所示效果,在首页产品中心子模块上将显示4幅产品照片。本节将按照产品的浏览次数对产品进行排序,将浏览次数前4名的产品放置在首页进行显示。
首先编辑后端视图处理函数,为了能够在homeApp中查询产品模型信息,需要将productsApp应用中定义的Product模型导入到home.py文件中:
from productsApp.models import Product
然后在home函数中添加对应的代码:
productList = Product.objects.all().order_by('-views') if(len(productList)>4): productList = productList[0:4]
最后将productList变量通过render函数渲染到模板文件中。
接下来编辑前端页面,根据前面的版面设计,在“产品中心”位置添加对应的代码:
<!-- 产品中心 --> <div class="col-md-12 col-pro"> <div class="row"> <div class="col-md-12"> <span class="part1"> <a href="{% url 'productsApp:products' 'robot' %}">产品中心</a> </span> <span class="part1 en"> / Products </span> <a class="btn btn-default btn-xs more-btn" href="{% url 'productsApp:products' 'robot' %}">+ 更多</a> <div class="line1" style="margin-bottom:5px;"> <div class="line2 theme"></div> </div> </div> <div class="col-md-12 col-pro"> <div id="Carousel" class="carousel slide" style="margin-bottom:30px"> <ol class="carousel-indicators" style="display:none;"> <li data-target="#Carousel" data-slide-to="0" class="active"></li> </ol> <div class="carousel-inner"> <div class="item active"> <div class="row"> {% for product in productList %} <div class="col-md-3 pro-images"> <a href="{% url 'productsApp:productDetail' product.id %}" class="thumbnail"> {% for img in product.productImgs.all %} {% if forloop.first %} <img src="{{img.photo.url}}" alt="产品图片" class="img-responsive" onl oad="DrawImage(this)"> {% endif %} {% endfor %} </a> <div class="carousel-caption nav-title">{{product.title}}</div> </div> {% endfor %} </div> </div> </div> </div> </div> </div> </div>
上述代码将后端传递的productList变量进行了有效渲染,其中需要注意的是图像部分,由于在定义产品模型时将产品的图像单独以ProductImg类进行定义,因此一款产品可能包含多幅图像,需要逐个遍历选取,此处为了快速获取图片只选取每款产品的第一张图像作为产品的展报放置在首页。
最后,在home.css文件中添加样式设置:
.pro-images{ background-color:#f6f6f6; } .thumbnail{ margin-bottom:0px; } .col-pro{ background-color:#F6F6F6; padding-top:15px; }
最终效果图如下所示:
上节内容已经开发完成了门户网站首页模块,该模块通过调用其它模块下的数据信息,对数据进行过滤筛选然后结合页面设计实现渲染。一般来说,用户访问网站会从首页开始,然后按照页面上的链接再跳转到其它页面,因此首页的访问相比其它页面更为频繁,而首页每次访问都要读取数据库,当访问量比较大的时候,就会有很多次的数据库查询,容易造成访问速度变慢,使得服务器消耗大量资源。因此,本节将介绍和使用缓存系统来解决这个问题。
缓存系统工作原理:对于给定的网址,服务器首先尝试从缓存中找到网址,如果页面在缓存中,直接返回缓存的页面,如果缓存中没有,则执行相关操作(比如查数据库)后,保存生成的页面内容到缓存系统以供下一次使用,然后返回生成的页面内容。
使用缓存的一种简单方法就是利用服务器本地的内存来当缓存,速度响应较快,但是这种方式不利于管理,当数据量大的时候可以采用另一种更稳妥、更有效的方式:使用数据库作为缓存后台。
具体的,首先在数据库中创建缓存表,在终端中输入命令:
python manage.py createcachetable cache_table_home
通过上述方式就在数据库中创建了一个名为cache_table_home的缓存表。接下来在项目配置文件中配置缓存,打开settings.py文件,在文件尾添加配置代码如下:
CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.db.DatabaseCache', 'LOCATION': 'cache_table_home', 'TIMEOUT': 600, 'OPTIONS': { 'MAX_ENTRIES': 2000 } } }
其中BACKEND属性用于设置当前缓存使用何种后台,这里设置为数据库形式。LOCATION属性用于设置具体的缓存表,此处填入前面创建的cache_table_home即可。TIMEOUT用于设置默认的超时时间,以秒为单位。可选属性OPTIONS中的MAX_ENTRIES用于设置允许的最大并发量。完成上述配置后只需要在需要缓存的页面视图处理函数前添加缓存装饰器代码即可:
from django.views.decorators.cache import cache_page @cache_page(60 * 15) # 单位:秒数,这里指缓存 15 分钟 def home(request): ...首页代码部分...
完成上述修改后读者可以尝试对模板文件home.html内容进行修改,例如修改部分文字,然后刷新浏览器查看是否同步更新,可以发现当前浏览器并没有将内容同步更改过来,而是需要等待15分钟之后内容才会更新。原因是在15分钟以内用户请求首页时,后台服务器在缓存中存在首页,因此会直接将缓存中的首页返回给用户而不再经过视图函数home处理,当时间超过15分钟以后,新来的请求会经过home函数处理,新的页面会返回给用户同时在缓存中更新备份。
由于我们开发的企业门户网站对页面实时性要求不高,页面内容不会频繁的变更,因此可以使用缓存节省取数据和渲染的时间,不仅能大大提高系统性能,还能提高用户体验,响应速度更快。
到这里本节课完成了首页模块的开发,重点需要掌握首页页面设计的基本方法,能够灵活运用Bootstrap进行页面版式设计。另外,首页模块作为一个集成模块,需要调用其它模块的相关信息,读者需要掌握跨模块之间数据读取的基本方法,能够运用Django相关的数据库过滤命令进行数据筛选。截止到本节课,已完成企业门户网站的所有硬性开发任务(除了人工智能平台没有开发,最后一课会专门讲解)。下一节课将会阐述如何将开发的企业门户网站进行部署和上线运行。