目录
3.1 模板的概念
3.1.1 Django模板概述
3.1.2 基于Django模板的静态资源配置
3.2 开发“科研基地”模块
3.2.1 制作门户网站基础页面
3.2.2 基于Django模板的页面复用
3.2.3 向模板传递动态参数
3.3 开发“公司简介”模块
3.3.1 制作侧边导航栏
3.3.2 Django数据库模型
3.4.3 优化后台管理系统
在上一节课程中,为了快速生成HTML,我们将HTML对应的内容直接硬编码到Python代码中并返回,这种方法比较直观,可以快速设计项目结构,但是直接将HTML内容编码到Python代码中这本身并不是一种良好的实现方式。由于后台Python代码的编写和前端HTML编码设计是两项不同的工作,一般是由两个团队分别完成,中间通过设计好的接口协议进行交互。为了有效解决这个问题,Django提供了模板机制,仅需少量的改动即可让后端服务器调用并渲染前端HTML页面。
Django提供的模板通常用来处理HTML,其本质是一种文本。简单来说,Django在普通的HTML文件中嵌入一些特殊意义的字符,这时候该HTML文件就称为模板,而这些特殊字符可以归纳为两种:变量和模板标签。
变量由两个大括号括起来,一般形式为:
{{ name }}
变量提供了一种页面内容动态生成的方法,使得后端服务器在渲染HTML页面时可以动态的将变量值插入到HTML中。
模板标签由大括号和百分号包围,一般形式为:
{% if today_is_weekend %} <p>Welcome to the weekend!</p> {% else %} <p>Get back to work.</p> {% endif %}
以上模板语句类似于Python的if条件判断,可以在HTML中执行逻辑流程控制。
本节基于Django的模板机制将对各个应用模块的静态资源进一步进行配置,最终实现通过首页上的Bootstrap导航条能够跳转到各个子页面。
首先,对首页模块homeApp进行页面编辑,通过使用Bootstrap的导航条组件在首页中添加跳转到各个子页面的链接(Bootstrap导航条组件的概念和基本使用方法可以参照Bootstrap官网)。由于需要使用Bootstrap框架进行页面设计,因此先确保在项目根目录下的static文件夹中已经将Bootstrap需要的js、css、fonts等配置文件正确导入,具体导入方法可以参考上一节课内容。
本节不再采用Python硬编码方式生成HTML,而是直接使用Django提供的render函数进行HTML模板渲染。首先在homeApp文件夹下新建templates文件夹(注意该文件夹名字必须为templates)用来存放HTML模板文件。然后在该templates文件夹下新建home.html文件,该文件即为首页页面文件。编辑home.html,在头部引用Bootstrap对应的配置文件,代码如下:
{% load static %} <!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>恒达科技(教学示例网站)</title> <link href="{% static 'css/bootstrap.css' %}" rel="stylesheet"> <link href="{% static 'css/style.css' %}" rel="stylesheet"> <script src="{% static 'js/jquery.min.js' %}"></script> <script src="{% static 'js/bootstrap.min.js' %}"></script> </head> <body> </body> </html>
上述代码首先引入了{% load static %}语句,这里使用了Django模板提供的static标签,通过导入该标签可以在页面中通过关键字static定位到项目的静态资源,最终所有静态文件资源的引用都可以采用类似"{% static 'css/bootstrap.css' %}"这种形式。上述引用方式是Django模板最核心的部分,后面会大量使用这种静态资源引用方式,读者需要牢牢掌握该方法。
接下来在<body>标签部分,引用Bootstrap提供的现成的导航栏组件nav来包含各子页面链接,详细代码如下:
<body> <!-- 导航条 --> <nav class="navbar navbar-default" role="navigation"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example" aria-expanded="false"> <span>导航栏</span> </button> </div> <div class="collapse navbar-collapse" id="bs-example"> <ul class="nav navbar-nav" style="width:100%;"> <li class="active nav-top"> <a href="{% url 'home' %}">首页</a> </li> <li class="dropdown nav-top"> <a href="#" class="dropdown-toggle on" data-toggle="dropdown"> 公司简介</a> <ul class="dropdown-menu"> <li><a href="{% url 'aboutApp:survey' %}">企业概况</a></li> <li><a href="{% url 'aboutApp:honor' %}">荣誉资质</a></li> </ul> </li> <li class="dropdown nav-top"> <a href="#" class="dropdown-toggle on" data-toggle="dropdown"> 新闻动态</a> <ul class="dropdown-menu"> <li><a href="{% url 'newsApp:company' %}">公司要闻</a></li> <li><a href="{% url 'newsApp:industry' %}">行业新闻</a></li> <li><a href="{% url 'newsApp:notice' %}">通知公告</a></li> </ul> </li> <li class="dropdown nav-top"> <a href="#" class="dropdown-toggle on" data-toggle="dropdown"> 产品中心</a> <ul class="dropdown-menu"> <li><a href="{% url 'productsApp:robot' %}"> 家用机器人</a></li> <li><a href="{% url 'productsApp:monitoring' %}"> 智能监控</a></li> <li><a href="{% url 'productsApp:face' %}"> 人脸识别解决方案</a></li> </ul> </li> <li class="dropdown nav-top"> <a href="#" class="dropdown-toggle on" data-toggle="dropdown"> 服务支持</a> <ul class="dropdown-menu"> <li><a href="{% url 'serviceApp:download' %}"> 资料下载</a></li> <li><a href="{% url 'serviceApp:platform' %}"> 人脸识别开放平台</a></li> </ul> </li> <li class="nav-top"> <a href="{% url 'scienceApp:science' %}">科研基地</a> </li> <li class="dropdown nav-top"> <a href="#" class="dropdown-toggle on" data-toggle="dropdown">人才招聘</a> <ul class="dropdown-menu"> <li><a href="{% url 'contactApp:contact' %}">欢迎咨询</a></li> <li><a href="{% url 'contactApp:recruit' %}">加入恒达</a></li> </ul> </li> </ul> </div> </div> </nav> </body>
上述代码中通过使用类class="dropdown nav-top"来调用导航栏的下拉菜单。注意每个链接<a>标签部分,其中href属性使用了Django提供的模板标签{% url '逆向路径' %}来逆向寻找访问路径,寻找方式采用“应用名:函数名”的形式,应用名如果没有可以不使用,例如首页仅仅采用下面这种无应用名的形式:
<a href="{% url 'home' %}">首页</a>
具体的反向解析形式与之前配置的url路由相对应。例如配置“企业概况”页面时,在该应用的urls.py文件中定义了应用名app_name = 'aboutApp',并且绑定了二级路由:
path('survey/', views.survey, name='survey'), # 企业概况
上述路由定义了函数名为survey,因此在模板中最终的反向解析形式为:
"{% url 'aboutApp:survey' %}"
在HTML中使用这种逆向网址解析的方式主要是为了方便变换根访问路径。例如在开发服务器上可以通过访问http://127.0.0.1:8000/aboutApp/survey/ 来浏览企业概况页面,但是当将整个项目部署到生产服务器上时此时就需要全局去修改所有的访问路径,这显然不方便。因此比较好的方式就是通过为每个路由设置名字,然后采用逆向解析的方式根据名字去动态的查找当前对应的访问网址。
最后,为了能够正常的访问首页,需要修改homeApp应用下views.py文件中的home处理函数,修改代码如下:
from django.shortcuts import render # Create your views here. def home(request): return render(request, 'home.html')
另外,由于新建的共享static资源文件夹是在项目根路径下,需要告诉Django模板当前共享的静态资源路径位置。打开项目配置文件夹hengDaProject下的settings.py文件,在文件末尾添加静态资源路径设置:
import os STATICFILES_DIRS = ( os.path.join(BASE_DIR, "static"), )
运行项目,效果图如下所示。单击首页页面各链接可以正常跳转到指定的子页面,单击浏览器返回按钮可以回退到首页。
前面的课程已完成各个应用的基本框架设置,本小节开始开发“科研基地”模块,只需要编写对应的访问页面即可。本小节内容重点学习Bootstrap设计页面的技巧以及基本的django后端模板复用方法。
首先来完成后端的基本配置。“科研基地”模块对应scienceApp应用,视图访问函数为应用下views.py文件中的science函数。在scienceApp文件夹下新建templates文件夹,然后在templates文件夹下新建science.html文件用于页面访问。参照前一小节的开发流程,修改science函数如下:
from django.shortcuts import render # Create your views here. def science(request): return render(request, 'science.html')
通过render函数直接将scienceApp应用下的science.html文件返回给前端浏览器进行显示。这里注意,Django会自动寻找每个应用下名为templates文件夹中的模板文件,因此在使用render函数进行页面渲染时不需要提供templates路径。接下来重点对science.html进行页面设计,主要介绍基于Bootstrap的门户网站页面布局以及CSS样式设置方法。
(1)制作页面头部
参照homeApp应用下的home.html文件结构,导入基本的Bootstrap配置文件。编辑science.html代码如下:
{% load static %} <!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>恒达科技|科研基地</title> <link href="{% static 'css/bootstrap.css' %}" rel="stylesheet"> <link href="{% static 'css/style.css' %}" rel="stylesheet"> <script src="{% static 'js/jquery.min.js' %}"></script> <script src="{% static 'js/bootstrap.min.js' %}"></script> </head> <body> </body> </html>
首行用于导入Django模板的静态资源标签。在<head>标签部分首先申明元数据信息,采用utf-8中文编码,网页初始适配宽度为设备宽度,用于支持响应式设计。<title>标签部分显示页面标题。接下来分别通过<link>和<script>标签导入Bootstrap必要的css和js文件。另外通过导入自定义的style.css文件以方便进行个性化样式设置。
由于所有页面都采用Bootstrap框架,因此将Bootstrap对应的静态配置文件放在项目根目录的static文件夹下以方便其它应用共享该资源。配置好<head>部分以后接下来需要在<body>部分开始进行页面主体设计。
页面的头部可以分为上下两部分,第一部分包括企业logo图片(位于左侧)和电话、邮箱等带小图标的说明文字(位于右侧),第二部分为导航栏。采用Bootstrap进行页面布局时,每行分配12个栅格。对于头部第一行,可以采用6—3—3栅格结构,即在大屏情况下企业logo图标部分占6格,而电话和邮箱各占3格,共占满一行。在小屏幕下,如手机,无法在一行情况下同时放置logo图片和说明文字,考虑到页面布局的美观,此时将说明文字隐藏。根据上述分析,在<body>部分添加代码如下:
<body> <div class="container top"> <div class="row"> <div class="col-md-6"> <a> <img class="img-responsive" src="{% static 'img/logo.jpg' %}"> </a> </div> <div class="col-md-3 hidden-xs"> <a class="phone ant"> <span class="glyphicon glyphicon-phone"></span>电话:400 1111 0000 </a> </div> <div class="col-md-3 hidden-xs"> <a class="mail ant"> <span class="glyphicon glyphicon-envelope"></span>邮箱:hengda@126.com </a> </div> </div> </div> </body>
接下来在static/css文件夹下找到style.css文件(没有的话则手动添加),编辑style.css文件并添加对应的样式设置:
.top{ margin-top: 10px; }
上述修改使得整个div距离上边界10像素。小图标由于承载在<a>标签中,默认颜色为深蓝色,而本书设计的网站文字都采用深灰色风格,因此需要对Bootstrap提供的phone和mail图标样式进行覆盖和改写。在style.css文件中继续添加代码:
.phone{ color:#666; float: right; } .mail{ color:#666; float:right; }
上述代码中属性float:right可以使得图标靠右排列。另外,由于图标嵌入在<a>标签内,因此当鼠标移过或者单击图标时会有默认的下划线,这里通过添加ant样式将下划线取消:
运行项目,通过浏览器访问http://127.0.0.1:8000/scienceApp/science/查看页面效果。这里需要注意的是,很多浏览器(例如360浏览器、火狐浏览器)具有缓存功能,因此当修改过css文件时,有时并不能立刻显示修改后的效果。解决方法就是对浏览器的缓存功能进行设置,可以设置成关闭浏览器时自动清理缓存。
效果图如下所示:
接下来我们制作页面导航条。前面我们已经使用了Bootstrap的导航栏作为首页页面跳转的工具,本节将会继续采用该组件并在原有基础上作一些样式调整。找到homeApp应用templates文件夹下的home.html文件,将其中的导航栏<nav>部分复制到science.html的<body>标签内,保存后启动项目查看效果是否正常。效果如下:
接下来,需要对导航栏样式进行调整,包括上下边距以及导航栏条目间距。另外,对于导航栏上的响应动作属性:鼠标移入移出、单击等也需要做一些调整,使其交互更加美观合理。编辑style.css文件,添加代码如下:
/* 导航条默认属性设置 */ .navbar-default{ margin-bottom:0px; /* 底部边距调整为0 */ border:0px; /* 去掉边框 */ background-color:#e7e7e7; /* 设置导航栏背景色 */ margin-top: 30px; /* 设置导航栏的上边距 */ } /* 导航栏栏目激活时属性 */ .navbar-default .navbar-nav .active a, .navbar-default .navbar-nav .active a:hover, .navbar-default .navbar-nav .active a:focus{ background-color:#005197; /* 背景色设置为深蓝色 */ color:#fff; /* 前景文字颜色设置为白色 */ } /* 一级菜单鼠标移过时属性 */ .navbar-default .navbar-nav li a:hover{ color:#fff; background-color:#005197; } /* 一级菜单单击展开时属性 */ .navbar-default .navbar-nav li.open a.on{ color:#fff; background-color:#005197; } /* 下拉菜单内边距 */ .dropdown-menu{ padding:0px; } /* 二级菜单标签属性 */ .dropdown-menu li a{ padding-top:10px; padding-bottom:10px; color:#777; text-align:center; } /* 一级菜单宽度和文字对齐方式 */ .nav-top{ width:14%; text-align:center; }
保存文件并运行项目查看效果。这里注意,在使用导航栏的过程中只有单击一级菜单后二级下拉菜单才会显示,实际情况中往往是希望当鼠标移到一级菜单时(mouseover特性)自动展开二级下拉菜单,而鼠标移开时二级菜单自动折叠。这时候就需要采用JavaScript对下拉菜单的mouseover属性进行动态调整。下拉菜单的展开动作可以通过为下拉菜单的mouseover属性添加open类实现,而下拉菜单的折叠则移除该属性即可。具体的,在<body>末尾添加JavaScript代码如下:
<script> $(function () { $(".dropdown").mouseover(function () { $(this).addClass("open"); }); $(".dropdown").mouseleave(function () { $(this).removeClass("open"); }); }); </script>
至此,已完成整个页面头部的制作。效果图如下:
(2)制作广告横幅
本小节主要采用Bootstrap提供的响应式图片组件将大尺寸横图显示在导航栏下方。
为了方便区分,首先在导航栏与横幅中间插入一根分割线条。紧接着上一小节添加的<nav>标签部分,在下面添加一个<div>用于显示分割线:
<div class="line"></div>
在style.css中添加该分割线的样式设置:
.line{ height:3px; width:100%; /* 使宽度占满整个屏幕宽度 */ background:#005197; }
效果如下:
接下来添加广告横幅对应的html代码:
<div class="container-fluid"> <div class="row"> <img class="img-responsive model-img" src="{% static 'img/science.jpg' %}"> </div> </div>
为了美观考虑,将广告横幅宽度设置为与导航栏相同,即占满整个屏幕宽度,因此采用了Bootstrap提供的container-fluid样式可以实现该效果。图像采用了img-responsive响应式样式能够自适应的根据浏览器窗口宽度调整图像大小,其src属性指向static/img文件夹下的science.jpg图像,采用了Django提供的模板标签{% static %}来进行资源查找。
效果图如下:
(3)制作页面主体
“科研基地”页面主体部分包括带下拉线的标题和一些介绍文字以及图片。基本结构如下:
<div class="container"> <div class="model-details-title"> 科研基地介绍 </div> <div class="model-details"> </div> </div>
外层定义一个container容器用来限制主体显示区域,在容器内分别定义了两个div用来显示主体标题和主体详细内容。
在style.css文件中添加主体标题的样式类model-details-title:
.model-details-title{ padding:15px 0px; font-size:18px; border-bottom:1px #005197 solid; /* 底部添加蓝色边框 */ color:#005197; margin-bottom:10px; margin-top:10px; }
上述样式对字体和边距做了一些调整。另外,通过对下边框border-bottom进行设置可以构造出一条蓝色的下边框线用作分割线。
主体详细内容是关于“科研基地”的一些说明文字和图片,采用了<p>、<h3>、<h5>等常见的HTML标签元素进行页面设计,详细代码如下:
<div class="model-details"> <p> 近二十年来,恒达致力打造“志存高远,一诺千钧”的企业文化... </p> <img class="img-responsive" src="{% static 'img/kyjd.jpg' %}"> <h3>研究方向</h3> <h5>机器人导航:</h5> <p> 多传感器路径规划、物联网一体化平台、远程人机交互、强化学习控制。 </p> <h5>人体行为识别:</h5> <p> 单用户行为识别、人体骨骼大数据分析、鲁棒特征抽取、多用户行为识别。 </p> <h5>人脸属性识别:</h5> <p> 人脸检测、属性分析、行人再识别。 </p> </div>
为了进一步美化页面,对主体内容部分进行样式设置,包括文字的行高、对齐方式、图片位置等,在style.css文件中添加下述代码即可实现:
/* 文字段落 */ .model-details p{ line-height:30px; text-indent:2em; text-align:justify; text-justify:inter-ideograph; } /* 主体图像 */ .model-details img{ margin:0px auto; }
效果如下图所示:
(4)制作页脚
页脚主要包括站点地图和版权两部分。站点地图可以用来通知搜索引擎页面的网址和页面的重要性,帮助站点得到比较好的收录。版权部分主要用来注明制作的站点的备案信息。网站备案是指向主管机关报告事由存案以备查考,其目的就是为了防止在网上从事非法的网站经营活动,打击不良互联网信息的传播。如果网站不备案的话,很有可能被查处以后关停。因为备案耗时比较长,如果网站项目确定立项的话建议在开发开始的同时立即进入备案流程。网站的详细备案方法将在后面课程中讲解,本节课程仅完成基本的页面设计。
页脚部分设计如下:
<div class="container web-footer"> <!-- 站点地图 --> <div class="row" id="map-footer"> </div> <!-- 版权 --> <div class="row" id="patent-footer"> <p> © 2021 Python Web企业门户 版权所有 | 苏ICP备19006378号 </p> </div> </div>
上述代码的站点地图和版权部分分别内嵌在class="row"的两个div内。外层通过样式类class="container web-footer"进行内容限定,其中container为Bootstrap提供的容器样式类,web-footer为额外的定制样式类。编辑style.css文件,添加web-footer样式如下:
接下来制作站点地图,整体采用Bootstrap栅格2-2-2-2-4布局,共分5列,前4列用来放置网站网页链接,最后1列用来放置二维码图片,具体HTML代码如下:
<div class="row" id="map-footer"> <div class="col-md-2"> <dl> <dt>公司简介</dt> <dd><a href="{% url 'aboutApp:survey' %}">企业概况</a></dd> <dd><a href="{% url 'aboutApp:honor' %}">荣誉资质</a></dd> </dl> </div> <div class="col-md-2"> <dl> <dt>产品中心</dt> <dd><a href="{% url 'productsApp:robot' %}">家用机器人</a></dd> <dd><a href="{% url 'productsApp:monitoring' %}">智能监控</a></dd> <dd><a href="{% url 'productsApp:face' %}">人脸识别解决方案</a></dd> </dl> </div> <div class="col-md-2"> <dl> <dt>服务支持</dt> <dd><a href="{% url 'serviceApp:download' %}">资料下载</a></dd> <dd><a href="{% url 'serviceApp:platform' %}">人脸识别开放平台</a></dd> </dl> </div> <div class="col-md-2"> <dl> <dt>人才招聘</dt> <dd><a href="{% url 'contactApp:contact' %}">欢迎咨询</a></dd> <dd><a href="{% url 'contactApp:recruit' %}">加入恒达</a></dd> </dl> </div> <div class="col-md-4" id="wx"> <p>扫描二维码,关注我们</p> <img class="qrimg" src="{% static 'img/qr.png' %}" alt="wx"> <p>客服热线:<b style="font-size:20px">400 111 2222</b></p> </div> </div>
上述代码每一列均采用了HTML5提供的<dl>、<dt>、<dd>表格标签。最外层使用<dl>来表明这是一个表格,<dt>标签用来表示表格标题,<dd>用来表示具体内容。表格中每一项内容均通过内嵌链接<a>标签将其它页面链接包含进来,每个链接采用了Django模板提供的逆向路由方式得到访问网址。
接下来需要对上述HTML进行一些样式调整,编辑style.css文件,添加如下内容:
#map-footer{ background-color:#3A3A3A; /* 对整个站点地图设置背景色灰色 */ } #map-footer dl{ text-align:center; /* 站点链接文字对齐 */ margin-top:40px; /* 设置表格与上边缘边距 */ } #map-footer dt{ padding:3px; /* 表格标题内边距为3像素 */ color:#fff; /* 表格标题颜色为白色 */ } #map-footer dd{ padding:3px; /* 表格内容内边距为3像素 */ } /* 二维码广告文字样式设置 */ #map-footer p{ margin-top:20px; margin-bottom:10px; color:#fff; font-size:16px; } #map-footer a{ color:#A6A6A6; /* 站点链接文字颜色设置 */ font-size:13px; /* 站点链接文字大小 设置 */ } #map-footer a:hover{ color:#fff; text-decoration:none; /* 去除站点链接鼠标移过时出现的下划线 */ } #wx{ text-align:center; /* 二维码居中对齐 */ } .qrimg{ max-width: 170px; /* 二维码最大宽度为170像素 */ }
最后对版权部分添加对应的css样式:
#patent-footer{ text-align:center; background-color:#3A3A3A; } #patent-footer p{ margin-top:10px; padding-right:80px; color:#8d8d8d; font-size:11px; }
至此,已完成“科研基地”模块前端网页的设计工作,后续内容将会以此为基础,通过组件复用的形式,生成网站模板供其它模块使用。目前,完整效果图如下:
所谓的页面复用就是将网站各页面相同的部分单独抽取出来作为一个共享页面,其它页面在制作时只需要将共享页面包含进来即可,这种方式有两个明显的好处:
本项目将各个共享页面放置于项目根目录下的templates文件夹中,其它应用创建页面时只需要包含该文件夹下的共享页面即可。
Django提供的用于页面复用的模板标签主要有下面两个:
(1)继承标签,调用形式如下:
{% extends "base.html" %}
通过该标签的使用可以直接继承base.html的页面内容。
(2)动态内容申明标签,调用形式如下:
{% block content %} {% endblock %}
其中block和endblock是固定标签语句,后面跟着的content是可以自定义命名的。使用的时候将非共享的部分用该标签申明,在继承时也采用该方式将动态内容填入标签内即可。
接下来我们将前面制作好的页面按照动态和非动态内容拆分处理。所谓非动态内容即各个页面共享的内容,例如案例中的页面头部、导航栏、页脚等这些内容在本门户网站中都是固定的,与之相对的,动态内容即为各个页面特有的、与其它页面不相同的部分,例如案例中的页面主体部分。本小节将完成共享模板的制作,需要将前面制作的页面的头部、导航栏、页脚等抽取出来,制作成单独的一个共享页面,然后在主体部分嵌入{% block content %}{% endblock %}标签。调用的时候只需要继承该模板页面,然后将动态内容写入到该标签中即可。
首先在项目根目录的templates文件夹中创建一个名为base.html的文件,该文件将作为共享模板文件来使用。将前面编写好的science.html文件中的内容完整的复制到base.html中,然后编辑下面两处地方:
(1)在<head>标签中将:
<title>恒达科技|科研基地</title>
替换为:
<title>恒达科技|{% block title %}{% endblock %} </title>
上述代码中页面的标题中有部分内容是动态内容,因此在此处需要嵌入一个动态内容申明标签{% block %},并将该动态标签命名为title。
(2)修改页面广告横幅和主体内容部分,这部分内容一般情况下各个页面都不一样,因此将整个横幅和主体内容部分全部删除,然后嵌入一个动态内容申明标签如下:
{% block content %} {% endblock %}
自此,模板即制作完毕。完整的base.html代码如下:
{% load static %} <!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>恒达科技|{% block title %}{% endblock %} </title> <link href="{% static 'css/bootstrap.css' %}" rel="stylesheet"> <link href="{% static 'css/style.css' %}" rel="stylesheet"> <script src="{% static 'js/jquery.min.js' %}"></script> <script src="{% static 'js/bootstrap.min.js' %}"></script> </head> <body> <div class="container top"> <div class="row"> <div class="col-md-6"> <a> <img class="img-responsive" src="{% static 'img/logo.jpg' %}"> </a> </div> <div class="col-md-3 hidden-xs"> <a class="phone ant"> <span class="glyphicon glyphicon-phone"></span>电话:400 1111 0000 </a> </div> <div class="col-md-3 hidden-xs"> <a class="mail ant"> <span class="glyphicon glyphicon-envelope"></span>邮箱:hengda@126.com </a> </div> </div> </div> <!-- 导航条 --> <nav class="navbar navbar-default" role="navigation"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example" aria-expanded="false"> <span>导航栏</span> </button> </div> <div class="collapse navbar-collapse" id="bs-example"> <ul class="nav navbar-nav" style="width:100%;"> <li class="active nav-top"> <a href="{% url 'home' %}">首页</a> </li> <li class="dropdown nav-top"> <a href="#" class="dropdown-toggle on" data-toggle="dropdown"> 公司简介</a> <ul class="dropdown-menu"> <li><a href="{% url 'aboutApp:survey' %}">企业概况</a></li> <li><a href="{% url 'aboutApp:honor' %}">荣誉资质</a></li> </ul> </li> <li class="dropdown nav-top"> <a href="#" class="dropdown-toggle on" data-toggle="dropdown"> 新闻动态</a> <ul class="dropdown-menu"> <li><a href="{% url 'newsApp:company' %}">公司要闻</a></li> <li><a href="{% url 'newsApp:industry' %}">行业新闻</a></li> <li><a href="{% url 'newsApp:notice' %}">通知公告</a></li> </ul> </li> <li class="dropdown nav-top"> <a href="#" class="dropdown-toggle on" data-toggle="dropdown"> 产品中心</a> <ul class="dropdown-menu"> <li><a href="{% url 'productsApp:robot' %}"> 家用机器人</a></li> <li><a href="{% url 'productsApp:monitoring' %}"> 智能监控</a></li> <li><a href="{% url 'productsApp:face' %}"> 人脸识别解决方案</a></li> </ul> </li> <li class="dropdown nav-top"> <a href="#" class="dropdown-toggle on" data-toggle="dropdown"> 服务支持</a> <ul class="dropdown-menu"> <li><a href="{% url 'serviceApp:download' %}"> 资料下载</a></li> <li><a href="{% url 'serviceApp:platform' %}"> 人脸识别开放平台</a></li> </ul> </li> <li class="nav-top"> <a href="{% url 'scienceApp:science' %}">科研基地</a> </li> <li class="dropdown nav-top"> <a href="#" class="dropdown-toggle on" data-toggle="dropdown">人才招聘</a> <ul class="dropdown-menu"> <li><a href="{% url 'contactApp:contact' %}">欢迎咨询</a></li> <li><a href="{% url 'contactApp:recruit' %}">加入恒达</a></li> </ul> </li> </ul> </div> </div> </nav> <div class="line"></div> {% block content %} {% endblock %} <script> $(function () { $(".dropdown").mouseover(function () { $(this).addClass("open"); }); $(".dropdown").mouseleave(function () { $(this).removeClass("open"); }); }); </script> </body> </html>
接下来我们对science.html进行改写,通过使用共享模板base,html来简化代码。首先在science.html中继承base.html模板文件,代码如下:
{% extends "base.html" %} {% load static %}
根据之前制作的模板可以知道,共有两处动态标签需要填入动态内容,首先填入页面标题:
{% block title %} 科研基地 {% endblock %}
然后将广告横幅和主体内容填入另一处动态标签中,具体如下:
{% block content %} <!-- 广告横幅 --> <div class="container-fluid"> <div class="row"> <img class="img-responsive model-img" src="{% static 'img/science.jpg' %}"> </div> </div> <!-- 主体内容 --> <div class="container"> <div class="model-details-title"> 科研基地介绍 </div> <div class="model-details"> <p> 近二十年来,恒达致力打造“志存高远,一诺千钧”的企业文化,不断吸纳和培养人工智能高精尖人才,逐步形成了... </p> <img class="img-responsive" src="{% static 'img/kyjd.jpg' %}"> <h3>研究方向</h3> <h5>机器人导航:</h5> <p> 多传感器路径规划、物联网一体化平台、远程人机交互、强化学习控制。 </p> <h5>人体行为识别:</h5> <p> 单用户行为识别、人体骨骼大数据分析、鲁棒特征抽取、多用户行为识别。 </p> <h5>人脸属性识别:</h5> <p> 人脸检测、属性分析、行人再识别。 </p> </div> </div> {% endblock %}
由于我们的共享模板文件放置在根目录的templates,因此,为了能够让各个应用正确引用到这个templates下的base.html文件,我们需要在settings.py文件中进行配置。
编辑hengDaProject文件夹下的settings.py文件,找到TEMPLATES字段,该字段即为项目模板需要配置的地方。修改DIRS如下:
'DIRS': [os.path.join(BASE_DIR, 'templates')],
上述配置将项目根目录BASE_DIR下的templates文件夹添加到模板搜索路径中,这样在页面访问时就可以找到共享模板base.html。注意在settings.py文件头部需要引入os库:
import os
到这里就修改完毕了。我们重新运行下系统,然后打开科研基地页面看看是否能够正常渲染。
在访问“科研基地”页面时,可以通过输入网址http://127.0.0.1:8000/scienceApp/science/来跳转到页面,也可以先登录首页http://127.0.0.1:8000/,然后通过单击首页上的“科研基地”链接跳转到页面。这里会发现一个问题,在切换页面之后导航栏的激活效果并没有同步的切换,即单击“科研基地”页面后导航栏上的激活菜单依然是“首页”。
这时就需要在页面渲染的过程中根据实际情况进行导航栏激活状态转换,也就是说后台服务器会根据不同的渲染页面传入不同的模板参数,页面导航栏根据传入的参数选择不同的激活状态。
具体的,首先查看导航栏“首页”的样式代码(位于base.html文件中),其用于控制激活状态的样式类为Bootstrap提供的active样式:
<li class="nav-top active"> <a href="{% url 'home' %}">首页</a> </li>
为了实现激活状态切换需要动态的为导航链接<li>标签添加active样式。实现时,先给每个导航栏<li>标签指明id号以方便找到页面元素,例如为“科研基地”设置id如下:
<li class="nav-top" id='science'> <a href="{% url 'scienceApp:science' %}">科研基地</a> </li>
然后通过内嵌的JavaScript代码找到该id节点并添加样式,具体代码如下:
<script type="text/JavaScript"> $('#science').addClass("active"); </script>
此时就可以在页面初始渲染时动态的为“科研基地”链接进行激活。效果如下图所示:
这样我们就可以通过代码动态的激活某个导航链接。
这里注意JavaScript代码中使用jQuery查找节点的方式,通过节点id查找节点$('#science')。尽管可以实现“科研基地”页面的链接激活设置,但是查找的节点是固定的,只能是science,而base.html文件是共享的模板文件,当其它页面调用时也需要激活各自的导航链接。因此,id号不能是固定值,而应该是变量。Django提供了模板变量的使用,通过双括号进行标识:{{变量}}。继续修改上述JavaScript代码,如下所示:
<script type="text/JavaScript"> $('#{{active_menu}}').addClass("active"); </script>
这里用模板变量{{active_menu}}来代替固定id值,在页面渲染的过程中只需要在后台的视图函数中传递该变量即可。
打开scienceApp应用中的views.py文件,修改science函数如下:
def science(request): return render(request, 'science.html',{'active_menu': 'science',})
上述代码在render返回时额外添加了第三个参数,该参数是由Python字典构成,字典里面即为需要向模板传递的变量:字典键即为变量名,键值即为变量值。修改后保存所有文件并重新启动项目,查看导航栏链接切换效果是否正常。后续课程关于导航链接的切换操作全都按照上述步骤执行,即先为导航链接添加id号,然后在页面渲染时在对应的render函数中向模板额外传递active_menu变量。
效果图如下所示:
本节先来制作“公司简介”模块的基础页面。根据示例网站所示页面效果,其与上一节课制作的“科研基地”页面基本相同,仅在页面主体部分左侧多出一个侧边导航栏。下面进入具体的制作步骤。
按照前一节课渲染页面的方法,首先打开aboutApp应用,在该应用下创建一个templates文件夹,然后在该文件夹下创建一个名为survey.html的网页文件。根据前面一课创建的页面模板,以继承方式继承页面内容,包括:页面头部、导航栏、页脚。具体代码如下:
{% extends "base.html" %} {% load static %} {% block title %} 企业概况 {% endblock %} {% block content %} <!-- 广告横幅 --> <div class="container-fluid"> <div class="row"> <img class="img-responsive model-img" src="{% static 'img/about.jpg' %}"> </div> </div> <!-- 主体内容 --> {% endblock %}
上述代码通过{% extends "base.html" %}来继承模板base.html文件。对于广告横幅,修改相应的静态图片文件路径即可。主体部分暂时不填入内容,详细的主体内容设计将在下一小节进行阐述。
为了能够有效渲染survey.html文件,打开aboutApp应用下的views.py文件,修改视图处理函数survey如下:
def survey(request): return render(request, 'survey.html',{'active_menu': 'about',})
此处render函数第一个参数直接返回请求,第二个参数传入欲渲染的html文件名,第三个参数是为了在页面切换到“公司简介”时能够同步的切换导航栏激活状态,因此需要向模板添加active_menu参数(不要忘了给base.html的“公司简介”链接添加id)。效果图如下所示
按照上述开发方式,在aboutApp应用的templates文件夹下开发荣誉资质模块对应的页面honor.html,并修改其视图处理函数honor:
def honor(request): return render(request, 'honor.html',{'active_menu': 'about',})
至此,“公司简介”模块下的两个页面框架均已制作完成。可以看到,通过模板的复用,仅需使用几行继承代码,就可以将之前制作的子页面完整的导入进来,可以大幅提高项目开发效率。
最后还遗留一个小问题,如果当前已经在“企业概况”页面内,此时鼠标移到“荣誉资质”导航链接上会发现两个链接颜色是一样的。为了能够有效进行区分,在style.css文件中添加代码如下:
/* 二级菜鼠标移过时属性 */ .navbar-default .navbar-nav li ul a:hover{ color:#fff; background-color:#005197; } /* 一级菜单激活时二级菜单鼠标移过时属性 */ .navbar-default .navbar-nav li.active ul a:hover{ color:#fff; background-color:#022a4d; }
通过上述设置,两个子模块链接在鼠标移过时就会呈现不同的颜色。
接下来我们将制作主体部分中的侧边导航栏以方便用户切换子页面。根据示例网站所示效果,页面主体可以分为左右两部分,左边是侧边导航栏,右边是固定位置的图片和介绍性文字。根据页面结构,设计HTML基本结构如下:
<div class="container"> <div class="row row-3"> <!-- 侧边导航栏 --> <div class="col-md-3"> </div> <!-- 说明文字和图片 --> <div class="col-md-9"> </div> </div> </div>
上述代码对页面采用3-9栅格布局,侧边导航栏占3个栅格,右边内容占9个栅格。采用container容器将整个页面主体内容限制在指定宽度内,并包含在class=row的div中,占满一行。其中为行div添加额外的样式“.row-3”,编辑style.css文件添加样式设置:
.row-3{ margin-top:30px; /* 设置顶部边距 */ }
侧边导航栏部分采用Bootstrap提供的列表组件list-group实现,其中每一个链接列表项用样式list-group-item表示,具体代码如下:
<!-- 侧边导航栏 --> <div class="col-md-3"> <div class="model-title"> 公司简介 </div> <div class="model-list"> <ul class="list-group"> <li class="list-group-item" id="survey"> <a href="{% url 'aboutApp:survey' %}">企业概况</a> </li> <li class="list-group-item" id="honor"> <a href="{% url 'aboutApp:honor' %}">荣誉资质</a> </li> </ul> </div> </div>
上述代码中"{% url 'aboutApp:survey' %}"和"{% url 'aboutApp:honor' %}"用于逆向寻找路由,方便后期部署。model-title和model-list样式类分别用于定制导航栏标题和列表样式,编辑style.css文件,添加对应的样式:
/*侧边导航栏标题样式*/ .model-title { text-align: center; color: #fff; font-size: 22px; padding: 15px 0px; background: #005197; margin-top: 25px; } /*侧边导航栏列表项样式*/ .model-list li{ text-align:center; background-color:#f6f6f6; font-size:16px; } /*侧边导航栏列表项链接样式*/ .model-list li a{ color:#545353; } /*侧边导航栏列表项链接激活样式*/ .model-list li a:hover{ text-decoration:none; color:#005197; }
通过上述样式设置,可以完成侧边导航栏的基本设计。接下来针对侧边导航栏的页面切换链接进行样式设计。大致思路与设计一级导航栏链接一致,在侧边导航栏链接切换时处于激活状态的链接文字呈现蓝色,其它链接文字呈现灰色,这样用户切换侧边导航栏之后就可以明显的看到当前处于哪个子页面。实现方法可以参照一级导航栏的设计流程,只需要由后台向模板传递二级菜单变量,然后前端通过JavaScript脚本的addClass函数动态的对<li>标签添加active类即可实现。首先,编辑style.css文件,添加代码如下:
/*侧边导航栏激活样式*/ .model-list li.active{ text-align:center; background-color:#f6f6f6; font-size:16px; border-color: #ddd; } /*侧边导航栏激活状态下鼠标移过时样式*/ .model-list li.active:hover{ text-align:center; background-color:#f6f6f6; font-size:16px; border-color: #ddd; } /*侧边导航栏激活状态时链接样式*/ .model-list li.active a{ color:#005197; }
上述css代码可以使得当侧边导航栏中的某一链接处于激活状态时其链接文字显示蓝色,方便用户浏览和辨识。接下来在base.html文件的<body>标签最后添加JavaScript代码:
<script type="text/JavaScript"> $('#{{sub_menu}}').addClass("active"); </script>
最后修改aboutApp应用中的views.py文件,重新编辑survey和honor函数,在最后render函数返回时添加额外的submenu变量:
def survey(request): return render(request, 'survey.html', { 'active_menu': 'about', 'sub_menu': 'survey', }) def honor(request): return render(request, 'honor.html', { 'active_menu': 'about', 'sub_menu': 'honor', })
通过上述修改,即可实现侧边导航栏二级菜单切换。至此,已完成侧边导航栏的开发任务。接下来继续完善“企业概况”页面。主体右边部分是位置固定的介绍性文字和图片,其设计与“科研基地”页面基本一致,包括标题、下划线、段落文字和图片,这里不再过多阐述。“企业概况”最终效果如下图所示。
从最终效果来看,“企业概况”页面主体部分的文字和图片均为静态资源文件,即不需要与后台进行交互,直接在HTML文件中调用,这种页面称为静态页面,其开发相对较为简单。接下来我们将学习如何通过Django数据库模型来构建动态页面。
在计算机进行信息处理的过程中,经常需要保存或处理大量数据,这时就需要用数据库来存储和管理这些数据。简单来说,数据库就是一个存放数据的仓库,这个仓库是按照一定的数据结构来组织、存储的,可以通过数据库提供的多种方法来管理数据库里的数据。我们平时所说的数据库实际上是包含了数据库管理系统的数据库管理软件,主要用来实现对数据的新增、查找、更新、删除等操作。更简单的形象理解,数据库和仓储物流一样,区别只是存放的东西不同。
为了使Python语言对各类数据库访问均具有良好的兼容性和可移植性,Python对于数据库的访问接口制定了一个通用标准,所有的Python数据库引擎都遵守此标准规定的DB-API规范,数据库引擎的切换和配置步骤基本相同,这极大地为Python的数据库操作提供了便利。
通过Python操作数据库,一般步骤为创建数据库、设计表结构和字段、使用sqlite(MySql、PostgreSQL等常见数据库)引擎来连接数据库、编写数据访问层代码、业务逻辑层调用数据访问层执行数据库操作。这里注意到上述数据库操作流程较为繁琐,需要开发人员直接面向数据库进行数据的增删查改,开发效率较低。是否可以让开发人员直接面向代码中的对象来操作数据库呢?答案是可以的。这种面向对象的数据库编程方式即为对象关系映射(Object Relational Mapping, ORM)。具体的,在ORM中类名对应数据库中的表名,类属性对应数据库里的字段,类实例对应数据库表里的某一行数据。Django中内嵌了ORM框架,不需要直接面向数据库编程,而是通过模型类和对象完成数据表的增删改查操作。
具体的,Django已经在项目创建时自动的提供了一个sqlite数据库用于为项目提供数据库操作,同时已为该数据库的使用配置好了参数。打开配置文件夹hengDaProject下的settings.py文件,找到其中的DATABASES字段,默认配置如下:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3', } }
可以看到该项目默认数据库引擎ENGINE为django.db.backends.sqlite3,数据库路径为当前项目根目录下的db.sqlite3数据库文件(sqlite数据库本质上是一个文件)。如果需要采用其它数据库,那么数据库的配置就在该字段进行设置。在实际Web站点部署时,一般不会采用sqlite数据库,因为该数据库的并发量、响应速度等具有较大的限制,但是在开发阶段可以采用该数据库进行开发测试。在后面课程的部署环节将会详细阐述如何在Django中配置和使用MySql数据库。
在了解Django数据库的基本概念和配置后,下面我们将会通过Django数据库模型来进行具体的开发。
参照示例网站的“荣誉资质”页面所示效果,可以看到企业获得的每一项荣誉均采用“1张图片+1段简要文字描述”这种形式进行展示。为了能够在后期方便管理人员对荣誉信息进行管理,需要抽象出当前的荣誉数据,并在数据库中生成相应的数据模型。
Django数据模型是与数据库相关的,与数据库相关的代码通常写在models.py文件中。首先打开aboutApp中的models.py文件,在该文件中添加“荣誉”Award模型:
from django.db import models class Award(models.Model): # 荣誉模型 description = models.TextField(max_length=500, blank=True,null=True) # 文字描述 photo = models.ImageField(upload_to='Award/', blank=True) # 图片
上面的代码首先导入django.db中的models模块来方便创建数据库字段。接下来定义了一个Award类,对应“荣誉”模型,该类继承自models.Model。在Award类中定义了两个字段:description和photo,分别对应“荣誉”模型的文字描述和图片。文字描述采用models.TextField来进行字段申明,并且使用max_length参数来设置该字段允许的最大长度。另外通过设置参数blank=True, null=True表示该字段允许为空。图片信息采用models.ImageField来申明,通过设置upload_to参数来定义图片的上传目录,上传的图像信息会存储在服务器媒体资源路径的Award文件夹下面。
在本节实例中仅使用了Django数据库模型中的文本和图像字段,除此以外,Django还提供了很多其它有用的字段,包括整型数据、字符数据、浮点数据等,具体可以参考django官网。
注意,在使用Django模型字段时,其中关于文件上传有两个字段:ImageField和FileField,它们都需要额外的进行路径设置,即需要在项目中指定媒体资源文件存储目录。由于本项目采用了图像ImageField字段,因此需要进行配置。具体的,打开配置文件夹hengDaProject下的settings.py文件,在文件末尾添加代码:
# 添加媒体资源 MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')
上述配置可以告诉解释器当前项目的媒体资源文件(此处指图像)的存储根路径为/media/,即将所有上传的图片存储在项目根目录下的media文件夹中,结合数据库ImageField字段中参数upload_to的设置,最终上传的图片存储路径为/media/Award。
由于我们使用了图片字段,因此需要额外安装一个图像处理库Pillow:
pip install Pillow
模型创建完成后需要将创建的模型同步到数据库系统中。在终端中首先输入命令:
python manage.py makemigrations
此时,会在终端输出结果如下:
Migrations for 'aboutApp': aboutApp\migrations\0001_initial.py - Create model Award
此时,已经做好将模型数据存入数据库的准备。打开aboutApp应用中的migrations文件夹可以查看当前文件夹下已经创建了0001_initial.py文件,此文件即为在本地创建好的需要同步的数据模型文件。但是注意,此时并没有真正进行数据库同步,仅仅完成了同步的准备工作。接下来输入命令:
python manage.py migrate
此时输出结果如下:
Operations to perform: Apply all migrations: aboutApp, admin, auth, contenttypes, sessions Running migrations: Applying aboutApp.0001_initial... OK Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying auth.0010_alter_group_name_max_length... OK Applying auth.0011_update_proxy_permissions... OK Applying auth.0012_alter_user_first_name_max_length... OK Applying sessions.0001_initial... OK
此时才真正完成了数据库模型的同步操作,即在数据库中已经为Award模型创建好了对应的数据表。接下来可以对该数据库模型进行数据增删查改操作。可以直接在python shell中通过代码编辑数据库中的数据,但是这种通过代码对数据库进行增删查改的方式并不方便也不直观。
一个企业门户网站分为前台和后台两部分。前台主要为普通用户提供常规页面的访问,可以浏览基本信息。后台由网站的管理员负责网站数据的查看、添加、修改和删除。开发一套完整的后台管理系统是一件异常繁琐的工作,为此,Django提供了现成高效的后台管理系统,在我们创建项目的过程中已经自动为我们生成了这样一个便捷的后台。
具体的,Django能够根据定义的模型自动的生成管理模块,使用Django的管理功能只需要两步操作:
要使用Django的后台管理系统首先需要创建一个超级管理员账户来登录后台系统。具体的,在终端中输入命令:
python manage.py createsuperuser
然后按照提示依次输入用户名、邮箱、密码(输两次)即可完成超级用户的创建。创建成功后输出如下:
Superuser created successfully.
此时表明超级管理员已经成功创建。接下来启动项目并访问:http://127.0.0.1:8000/admin,访问效果如下:
输入创建的超级管理员用户名和密码即可登录后台管理系统。如下图所示:
进入后台管理系统后可以看到当前已经注册在系统中的模型,包括Group和Users,这是系统默认提供的账户管理模型。单击“Users”可以看到下图所示页面:
在该页面中列出了当前的所有用户数据,此处由于我们并没有创建其它用户数据,因此仅可看到超级管理员账户。
在后台管理系统中,可以通过可视化按钮以及系统提供的默认表单方便的对数据库模型进行操作。
下面继续完成本节课开发任务。从上图中可以看到当前管理系统主页面并没有Award模型的操作设置,这是因为我们在数据库中创建了Award模型,但是并没有将该模型注册到后台管理系统中,因此后台管理系统也就无法操作该模型。如果需要将模型注册到后台管理系统,只需要在admin.py文件中添加模型对应的注册信息即可。具体的,打开aboutApp中的admin.py文件,编辑代码如下:
from django.contrib import admin from .models import Award class AwardAdmin(admin.ModelAdmin): list_display = ['description','photo'] admin.site.register(Award, AwardAdmin)
首先引入Django中提供的管理员模块admin,然后从models.py文件中导入前面创建的Award类。接下来定义了一个名为AwardAdmin的荣誉管理类,该类继承自admin模块中的ModelAdmin类。在AwardAdmin中设置了展示列表list_display参数,在该参数中将需要编辑的模型字段添加进来,此处包括文字描述字段description和图像字段photo。最后通过admin.site.register函数将AwardAdmin类与Award类进行绑定并实现注册。保存所有修改,刷新浏览页面可以看到在后台管理系统中新增加了ABOUTAPP组(对应AboutApp应用),在该组中可以操作Award类,如图所示:
接下来,通过后台管理系统向Awards模型中添加几条数据用于后期使用。单击Award所在行对应的Add按钮,进入数据添加界面,如下图所示:
在定义Award模型时共有两个字段:description和photo,与后台数据添加界面中的两个字段一一对应。由于description采用了长文本字段TextField来申明,因此后台管理系统自动的为该字段形成多行输入框便于用户输入数据,而photo申明时使用了ImageField字段,因此后台管理系统自动形成文件上传按钮来执行数据添加操作。按照输入格式,在description中添加文字描述,然后单击浏览按钮选中一张图片(注意上传的照片名中不要含中文),最后单击右侧的save按钮保存数据。
按照上述方式继续添加几条类似的Award数据,最终Award列表如下图所示:
此时,Award模型中所有数据均已存入数据库文件中。值得注意的是,对于图像photo字段,在数据库中并不是直接存储了该图像数据,而是存储了对应的图像路径。图像的真实存储路径根据settings文件的MEDIA_URL和MEDIA_ROOT来设置。具体的针对本实例项目,可以查看当前项目根目录下的media文件夹中是否存在对应的图片(media/Award路径下)。
除了向模型添加数据以外,同样可以通过后台管理系统实现模型数据的删、改、查操作,具体的此处就不再一一展开演示。在数据库中有了上述的Award模型数据以后,就可以在用户请求页面的过程中动态的向页面嵌入数据实现动态页面的访问。接下来我们会阐述如何通过Django从数据库中取出模型数据并插入到模板中进行动态页面渲染。
Django提供了方便的ORM操作来对数据库模型进行管理。当用户在访问荣誉资质页面时,请求通过路由url分发至views.py文件中的honor函数进行处理,该函数收到请求后首先从数据库中取出Award模型的所有数据,然后将数据嵌入到honor.html模板文件中。
重新编辑views.py文件中的honor函数如下:
from .models import Award def honor(request): awards = Award.objects.all() return render(request, 'honor.html', { 'active_menu': 'about', 'sub_menu': 'honor', 'awards': awards, })
首先从当前应用下的model.py文件中导入Award模型,然后在honor函数中通过模型的objects.all()函数得到一个查询集并存放于变量awards中。在页面渲染时通过render函数将awards变量以参数形式添加到页面中。本实例通过Django模型管理器的objects.all()函数来获取模型数据信息。除此以外,Django还提供了很多其它的查询功能,具体可以参考官网。此处,我们并不需要一次性的掌握所有查询语句,只需要在后续章节中通过项目实例逐步学会常见的调用方式即可。
下面开始编辑honor.html页面。根据示例网站所示效果,“荣誉资质”页面的整体设计和“企业概况”页面一致,不同之处在于主体部分采用图片列表的形式展示企业所获荣誉。在布局时可以采用Bootstrap现成的缩略图组件(样式类thumbnail)略加修改即可实现。初始设计方案如下:
<div class="col-md-9"> <div class="model-details-title"> 荣誉资质 </div> <div class="row"> <div class="col-sm-6 col-md-4"> <div class="thumbnail"> <img src="{% static 'img/honor1.jpg' %}"> <div class="caption"> <p>2011年加入互联网协会</p> </div> </div> </div> <div class="col-sm-6 col-md-4"> <div class="thumbnail"> <img src="{% static 'img/honor2.jpg' %}"> <div class="caption"> <p>2012年加入互联网协会</p> </div> </div> </div> </div> </div>
上述代码共包含2两个缩略图,每个缩略图包括1个<img>标签用于显示图像以及1个class="caption"的<div>标签用于显示图片对应的描述信息。每个缩略图在大屏浏览器下占4个栅格,在小屏浏览器上占6个栅格。上述代码采用了静态页面方式将图像路径和文字描述信息显式的写在HTML中,无法根据数据库信息动态的修改图片和文字数据。接下来需要实现根据后台honor函数传入的awards参数将数据动态的写入HTML中,主要通过模板标签{% for %}{% endfor %}来实现。该模板标签可以动态的遍历传入的变量,实现页面数据循环写入。具体代码如下:
<div class="col-md-9"> <div class="model-details-title"> 荣誉资质 </div> <div class="row"> {% for award in awards %} <div class="col-sm-6 col-md-4"> <div class="thumbnail"> <img src="{{award.photo.url}}"> <div class="caption"> <p>{{award.description}}</p> </div> </div> </div> {% endfor %} </div> </div>
上述代码通过{% for award in awards %}语句逐个的取出awards中的每一条数据并赋值到新的临时变量award中,每个award都包含1项photo和description数据。在缩略图中分别使用模板变量{{award.photo.url}}和{{award.description}}对<img>的src属性以及段落<p>赋予动态内容。这种方式可以根据awards中的实际的条目数来生成缩略图。对该页面数据的增删查改不再需要开发人员变更代码实现,只需要通过后台管理系统操作数据即可完成数据更新。刷新页面后浏览效果如下图所示:
可以看到每个缩略图的文字信息均已正确显示,但是图片信息没有显示出来。主要原因在于当前debug模式下没有将动态资源路径MEDIA_URL添加到静态路由static下。编辑配置文件夹hengDaProject下的urls.py文件,添加代码如下:
from django.conf import settings from django.conf.urls.static import static if settings.DEBUG: urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
保存修改后,重新刷新网页即可看到正确的效果图,如下所示。
至此,本节完成了数据库模型的导出和渲染。通过本节“荣誉资质”子模块的开发,相信读者已经掌握动态页面的制作流程和基本的数据库操作方法。后续课程的其它页面均采用这种动态页面的制作方式来实现。因此,希望读者能够牢牢掌握本节内容的知识点,多动手多实践,对于其中不清楚的地方可以结合本课程配套资源代码来分析。
在上一节中介绍了Django的后台管理系统的使用方法,对于开发人员来说,掌握该后台管理系统的使用是必须的,但是该系统对于非开发人员来说其交互方式并不友好,包括语言(Django后台管理系统默认语言为英文)和界面设计等。本节重点阐述如何对后台管理系统进行优化以方便今后将网站交付给实际客户使用。
(1)登录界面优化
Django在创建项目时默认将英文作为项目主要语言,因此,在后台管理系统中大部分字段都是英文。下面首先对登录界面进行汉化处理。
打开项目配置文件settings.py,找到其中的LANGUAGE_CODE字段,该字段用于设置整个项目的语言,这里需要改为中文支持。另外需要修改时区字段TIME_ZONE为中国时区。具体的对其进行修改如下:
LANGUAGE_CODE = 'zh-Hans' # 设置语言为中文 TIME_ZONE = 'Asia/Shanghai' # 设置中国时区
修改后保存并启动项目。访问后台管理系统,可以发现关键字段英文都已转换为中文,效果如下:
Django提供的后台管理系统默认的系统名称为“Django管理”,而在实际交付给客户使用时需要按照网站主题定义后台系统名字,因此接下来需要完成后台管理系统名称的修改。
打开aboutApp应用下的admin.py文件,在文件末尾添加代码:
admin.site.site_header = '企业门户网站后台管理系统' admin.site.site_title = '企业门户网站后台管理系统'
上述代码分别对管理系统头部和页面标题进行了修改,保存修改后刷新页面如下图所示。这里注意,由于我们创建的项目hengDaProject是一个多应用项目,上述代码修改只需要放置在任一应用下的admin.py文件中即可生效。
(2)主界面优化
主界面部分首先来修改数据模型的显示。由于创建了Award模型,因此在ABOUTAPP下可以看到英文显示的“Awards”字样(默认会以模型名的复数形式表示)。尽管开发人员知道该模型含义,但是对于非开发人员来说英文字样较为突兀,这里希望能够改成“获奖荣誉”。一种有效的解决思路就是在模型的创建过程中为模型创建一个中文别名,这样在后台管理系统中就可以用别名替代模型的真实名来显示。Django为这种思路提供了简单的实现方法,只需要修改模型的Meta元信息即可。具体的打开aboutApp应用下的models.py文件,为定义的Award模型添加Meta元信息说明,代码如下:
from django.db import models class Award(models.Model): # 荣誉模型 description = models.TextField(max_length=500, blank=True,null=True) # 文字描述 photo = models.ImageField(upload_to='Award/', blank=True) # 图片 class Meta: verbose_name = '获奖和荣誉' verbose_name_plural = '获奖和荣誉'
其中verbose_name字段即为模型定义的别名,verbose_name_plural为别名对应的复数形式。保存后刷新页面即可看到下图所示效果:
接下来需要对应用名ABOUTAPP的显示进行修改。针对本章任务来说,需要将ABOUTAPP修改为“公司简介”。Django在后台默认显示的应用的名称为创建app时的名称,需要修改这个app的名称达到定制的要求。从Django 1.7版本以后不再使用app_label,修改app需要使用AppConfig。这里我们只需要在应用的__init__.py里面进行修改即可,打开aboutApp下的__init__.py文件,添加代码如下:
from os import path from django.apps import AppConfig VERBOSE_APP_NAME = '公司简介' def get_current_app_name(file): return path.dirname(file).replace('\\', '/').split('/')[-1] class AppVerboseNameConfig(AppConfig): name = get_current_app_name(__file__) verbose_name = VERBOSE_APP_NAME default_app_config = get_current_app_name( __file__) + '.__init__.AppVerboseNameConfig'
这里主要参考Django官方参考文档来实现,通过继承AppConfig类来设置app别名,该部分代码如果难以理解可以暂时不作深究,主要注意VERBOSE_APP_NAME字段,通过修改该字段可以为应该添加别名。保存修改后刷新页面,效果图如下图所示:
(3)列表界面优化
单击模型会进入模型列表页面,该页面中显示了模型的所有数据,注意到模型的每个字段依然是英文。接下来将对模型字段进行修改使得description和photo分别显示为“荣誉描述”和“荣誉照片”。解决方法与模型名称的修改基本一致,通过对模型每个字段取别名来进行显示。重新编辑Award模型中的description和photo字段,为每个字段添加verbose_name属性,具体修改如下:
from django.db import models class Award(models.Model): # 荣誉模型 description = models.TextField(max_length=500, blank=True, null=True, verbose_name='荣誉描述') photo = models.ImageField(upload_to='Award/', blank=True, verbose_name='荣誉照片') class Meta: verbose_name = '获奖和荣誉' verbose_name_plural = '获奖和荣誉'
保存修改后刷新页面,效果图如下图所示:
好了,到这里本节课结束。