文章来源Python Django+SQL+Pandas+Pyecharts自建在线数据分析平台(一)
作者ccpic
感谢:感谢作者 ccpic 分享的优质内容,本网页主要用于学习知识的存档备份,欢迎点击原网页支持作者。

本篇是系列文章的第三篇:

(一)需求分析&技术实现

(二)初步搭建Django环境

(四)SQL+Pandas初步处理数据

(五)前端表单交互

(六)Ajax异步传参与加载

(七)前端数据格式的处理

(八)DataTables接管前端表格

(九)Pyecharts实现交互图表

(十)静态图表的展示

(十一)“导出数据至Excel”功能

(十二)添加和配置缓存

(十三)用户登录系统

(十四)部署Django至生产环境

在上一章初步打通前后端通信后,接下来的方向无疑是两端的分别扩展和完善。本章先来看一下前端页面布局和Django模板的问题。

本例的页面布局没有任何标新立异的地方,主页面采用header-body-footer的上中下布局,header和footer是几乎静态的,body则是动态的。body在部分功能下是一块整体,在核心的分析功能下分成filter-display的左右布局。filter承担向后端提交input的角色,display则展示后端根据input返回的response。

(在更进阶的情况下,负责分析的body页面还可以进一步分为filter-display-setting的左中右布局)。

我们构思的整个布局如下图所示:

根据此设计,我们需要搭建相应的Django模板架构,并充分利用其模板继承特性,其中主要需要理解base-extend机制以及block和include两个主要的模板语句。

在这里我省略了讲解Django模板加载器的部分,其实这一部分也是有讲究的,尤其是对于多个app的更大型的工程。

按照官网推荐的结构,先在本例的chpa_data新建文件夹templates,再在templates内新建和app同名的文件夹chpa_data,再在其内新建Django的html模板文件。

首先,创建一个base.html代表这个模板的基础结构(可以称之为基础模板或父模板):

<!-- 载入静态文件 更早版本可能会用load staticfiles -->
{&#37; load static &#37;}

![](images/python-djangosqlpand/v2-44934e23627fc7a9c1828acfbc8376d5_1440w.webp)
<!DOCTYPE html>
<!-- 网站主语言 -->
<html lang="zh-cn">

<head>
<!-- 网站采用的字符编码 -->
<meta charset="utf-8">
<title>数据分析</title>
<link rel="stylesheet" href="{&#37; static 'datatables/dataTables.semanticui.min.css' &#37;}">
<link rel="stylesheet" href="{&#37; static 'Semantic-UI-CSS-master/semantic.min.css' &#37;}">
<script src="{&#37; static 'jquery/jquery-3.4.1.js' &#37;}"></script>
<script src="{&#37; static 'datatables/jquery.dataTables.min.js' &#37;}"></script>
<script src="{&#37; static 'datatables/dataTables.semanticui.min.js' &#37;}"></script>
<script src="{&#37; static 'datatables/percentageBars.js' &#37;}"></script>
<script type="text/javascript" src="{&#37; static 'Semantic-UI-CSS-master/semantic.js' &#37;}"></script>
<script src="{&#37; static 'echarts/echarts.min.js' &#37;}"></script>
<script src="{&#37; static 'echarts/china.js' &#37;}"></script>
</head>



<body>

<!-- 引入导航栏 -->
{&#37; include 'chpa_data/header.html' &#37;}
<!-- 预留具体页面的位置 -->
{&#37; block body &#37;}{&#37; endblock body &#37;}
<!-- 引入注脚 -->
{&#37; include 'chpa_data/footer.html' &#37;}

</body>

</html>

在header部分我们引用了这个工程需要的静态文件,所有继承base的页面就可以不再引用了。这里需要在datasite下的settings.py加一条设置以识别静态文件夹的位置:

STATIC_URL = '/static/' # 静态文件夹相对于工程根目录的相对位置
STATICFILES_DIRS = (
os.path.join(BASE_DIR, "static"),
) # 该语句建议保留,对低版本的Django也是指明静态文件的位置,后续版本有功能改变

而base.html代码中最重要的是需要理解 {% include %}和{% block %}{% endblock %}的区别。{% include %}是直接引用一个相对独立的代码块,{% block %}{% endblock %}则是给所有extend base.html的子模板预留可以复写的位置,在子模板要有对应的block。从另一个角度说,子模版block之间的内容可以覆盖父模板中的相同block。

这里的概念有些容易混淆,其实这么理解就可以了,当代码块是通用的相对静态的时候,使用include语句直接引用;而当可能有多个子模板多次继承重写同一部分时,预留block。或者简单说,include是多对一的关系(但因为继承,大部分实际情况下是一对一),而block是一对多的关系。

所以对于我们的app,header和footer是相对静态的,不会出现多个不同的header和footer。我们在templates文件夹下创建header.html和footer.html,只是为了方便单独管理。使用semantic ui的语句简单写一点header和footer。

header.html:

<div class="ui large top fixed inverted menu">
<div class="ui container">
<a href={&#37; url "chpa:index" &#37;} class="item">首页</a>
</div>
</div>

这里第一次出现Django特色的url tag,{%url”chpa:index”%}的结构实际是对应chpa_data的urls.py文件里的{% url “app_name:view_name” %}。一定注意这里是urls.py里为urls们命名的app_name参数,而不是实际上这个工程里app的name。所以这里是chpa而不是chpa_data。

footer.html:

<div class="ui inverted vertical footer segment">
<div class="ui center aligned container">
占位
</div>
</div>

注意header和footer的代码就是这么短,是不需要extends语句的。

而base里预留的{% block body %}{% endblock body %}部分是可以复用的,我们可以测试两个页面分别继承的情况,这两个页面的开头语句都是:

{&#37; extends "chpa_data/base.html" &#37;}

我们可以有一个静态的项目首页index.html,他的内容是极其简单的,以后可以是个含有导航信息或说明文字的静态首页,这次我们先预留一个{{ data }} tag准备接受测试时后台传来的数据:

<!-- extends表明此页面继承自 base.html 文件 -->
{&#37; extends "chpa_data/base.html" &#37;}

<!-- 隐藏分隔条 -->
<div class="ui hidden divider"></div>

<!-- 写入 base.html 中定义的 body content -->
{&#37; block body &#37;}
<!-- 因为semantic ui的特性需要在body部分写个pusher避免内容被顶部导航栏遮挡 -->
<div id="pusher" class="pusher" style="padding-top:50px">
{&#123; data &#125;}
</div>
{&#37; endblock body &#37;}

再创建一个相对复杂的analysis.html,根据本章开头的设计,这个页面应该是左边是filter,而右边是display。这里涉及到进一步嵌套的模板继承,filter一般不会变,所以直接include,而display有可能有多个子模板再复用,继续预留block位置:

<!-- extends表明此页面继承自 base.html 文件 -->
{&#37; extends "chpa_data/base.html" &#37;}

<!-- 隐藏分隔条 -->
<div class="ui hidden divider"></div>

<!-- 写入 base.html 中定义的 body content -->
{&#37; block body &#37;}
<!-- 因为semantic ui的特性需要在body部分写个pusher避免内容被顶部导航栏遮挡 -->
<div id="pusher" class="pusher" style="padding-top:50px">
<div class="ui celled grid">
<!-- 左边3/16为filter页面, include -->
<div class="three wide column">
{&#37; include 'chpa_data/filter.html' &#37;}
</div>

<!-- 右边13/16为display页面, 预留block -->
<div class="thirteen wide column">
{&#37; block display &#37;}{&#37; endblock display &#37;}
</div>
</div>
</div>
{&#37; endblock body &#37;}

最后再创建filter.html文件,可以暂时为空。

**display.html文件,注意这里的第一行不是extends base.html而是extends analysis.html。**我们再和index.html一样预留 {{ data }} tag准备测试:

{&#37; extends "chpa_data/analysis.html" &#37;}
{&#37; block display &#37;}
{&#123; data &#125;}
{&#37; endblock display &#37;}

此时Django模板架构完毕,再确认一次项目结构:

我们可以修改上一章中views.py的index方法做个测试,因为在模板里预留了{{ data }}tag,我们不希望再传一个整体的HttpResponse了,而是希望传一个包含data键值的context。修改代码如下:

from django.shortcuts import render

...

def index(request):
sql = "Select count(*) from data" #标准sql语句,此处为测试返回数据库data表的数据条目n,之后可以用python处理字符串的方式动态扩展
df = pd.read_sql_query(sql, ENGINE) #将sql语句结果读取至Pandas Dataframe
context = {'data': df }
return render(request, 'chpa_data/index.html', context)

可以看到首页已经发生了改变。

如果render到display模板会怎样呢?

return render(request, 'chpa_data/display.html', context)

很明显我们为分析功能设计的filter-display左右布局也生效了。

本章是反馈出问题最多的地方,特再次总结一遍容易踩坑的要点:

  1. 项目结构建议完全按照截图。在教程的早期版本我曾经把模板html文件直接放在templates文件夹下,后来一些朋友反馈Django高级版本会报错,所以请不要这么做。
  2. 按照截图推荐的结构的情况下,所有要引用模板path的extends, include和render等语句,路径一定都是’chpa_data/xxx.html’而不是’xxx.html’。这是因为加载模板是根据绝对位置(templates文件夹)而不是相对位置。
  3. 只有预留block的子模板要在头部加入对应的extends语句,include的子模版不需要加。

下一篇SQL+Pandas初步处理数据,见: