一个web应用的诞生(2)--使用模板

一个应用程序大部分的时间都是在维护阶段,所以在实现功能的情况下,对于代码的可维护性提出了更高的要求,而对于可维护性来说,采用mvc架构,将视图与逻辑分离就是一个基本的要求了,对于flask来讲,使用的是jinja2模板引擎

基本用法

flask的繁多配置都有一个默认值,对于模板来讲,他会到templates文件夹中寻找,所以,首先在文件根目录创建一个名为templates的文件夹,并创建index.html文件,现在目录结构如下:

修改default.py中的部分代码为:

from flask import render_template #页头,导入函数

@app.route("/")
def index():
    return render_template("index.html",site_name='myblog')

index.html中的代码为:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>{{site_name}}</title>
</head>
<body>
<h1>这个站点的名字为 {{site_name}} </h1>
</body>
</html>

flask通过render_template函数使用模板,其中函数的第一个参数为模板名,之后可以提供若干参数,均为键值对,为模板中的变量提供数据。如此例子中,为site_name提供了myblog的值,而模板内使用{{参数名}}来表示一个变量

此时浏览器输入地址输出结果为:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>myblog</title>
</head>
<body>
<h1>这个站点的名字为 myblog </h1>
</body>
</html>

jinja2模板还提供了一些变量过滤器,如代码:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>{{site_name|upper}}</title>
</head>
<body>
<h1>这个站点的名字为 {{site_name}} </h1>
</body>
</html>

这时输出为:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>MYBLOG</title>
</head>
<body>
<h1>这个站点的名字为 myblog </h1>
</body>
</html>

常用过滤器如下

safe 不转义
capitalize 首字母大写
lower 转换为小写
upper 转换为大写
trim 去收尾空格
striptages 去除html标签

除此之外,Jinja2的变量还可以是一些复杂类型,甚至可以使用一些复杂类型的常用方法,如:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>{{site_name[2:]}}</title> 
</head>
<body>
<h1>这个站点的名字为 {{site_name}} </h1>
</body>
</html>

这时输出为:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>blog</title>
</head>
<body>
<h1>这个站点的名字为 myblog </h1>
</body>
</html>

控制语句

控制语句是一个模板的基本功能,同样的jinja2也提供了相应的功能:

选择
{% if name=='test' %}
    这是测试
{% else %}
    {{name}},你好
{% endif %}

循环
<ul>
    {% for blog in blogs%}
        {{ blog.title }}
    {% endfor%}
</ul>

除了这些基本用法,模板还为一些代码的重复使用提供了宏的功能,如将如下代码写入macros.html文件中

    {% macro  render_title(blog)%}
        <li>{{blog.title}}</li>
    {% endmacro%}

然后在之前的模板中:

{% import 'macros.html' as macros %}
<ul>
    {% for blog in blogs %}
        {{ macros.render_title(blog) }}
    {% endfor %}
</ul>

执行结果与之前的完全相同

Jinja2还提供了一个更为强大的功能,即模板集成,这个个人感觉有点像java的sitemesh框架,它首先需要创建一个base.html的基模板:

<!DOCTYPE html>
<html>
<head>
    {% block head %}
    <meta charset="UTF-8">
    <title>{% block title%} {% endblock %} - 牛博客</title>
    <script src="http://cdn.bootcss.com/jquery/2.2.4/jquery.min.js"></script>
    {% endblock %}
</head>
<body>
    {% block body %}
    {% endblock %}
</body>
</html>

其中block标签的意思表示在子模板中可以修改,具体到此例子,则可修改的部分为 head,title,body。下面为子模板代码:

{% extends "base.html" %}
{% block title %}{{site_name[2:]}}{% endblock %}
{% block head %}
    {{super()}}
{% endblock %}

{% block body %}
 <h1>这个站点的名字为 {{site_name}} </h1>
{% endblock %}

此时执行结果仍为:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>blog</title>
</head>
<body>
<h1>这个站点的名字为 myblog </h1>
</body>
</html>

好了,现在基础内容已经完成,是时候建立一个比较实际的页面了,新建login.html模板

现在大体了解了Jinja2模板引擎的用法,但是即使有了模板引擎,我相信对于站点最基础的用于新增修改的表单也是很多人最苦恼的部分之一,而flask的flask-wtf扩展则将表单处理进行了丰富的简化。

首先当然要安装flask-wtf扩展,同样非常简单,命令为:

pip3.6 install flask-wtf

现在,假定我们需要一个登陆页面,页面中包含一个用户名的文本域和一个密码域,则创建表单对象:
from flask.ext.wtf import Form
from wtforms import StringField,PasswordField,SubmitField
from wtforms.validators import Required

class LoginForm(Form):          #表示此LoginForm类继承于Form类
    username=StringField("请输入用户名",validators=[Required])
    password=PasswordField("请输入密码")
    submit=SubmitField("登录")

注意这里出现了中文,所以需要在文件的第一行输入

# coding=utf-8

如此设置编码后,才可在此py文件中使用中文

同时创建模板文件login.html:

{% extends "base.html" %}
{% block title %}{{site_name[2:]}}{% endblock %}
{% block head %}
    {{super()}}
{% endblock %}

{% block body %}
 <form method="post">
     {{form.hidden_tag()}}
     {{form.username.label}} {{form.username()}}
     {{form.password.label}} {{form.password()}}
     {{form.submit()}}
 </form>

{% if username%}
您输入的用户名为 {{username}}<Br>
您输入的密码为 {{password}}<Br>
{% endif %}
{% endblock %}

然后创建登录页的路由控制函数:

@app.route("/login",methods=["GET","POST"])
def login():
    username=None;
    password=None;
    form=LoginForm()
    if form.validate_on_submit():
        username=form.username.data;
        password=form.password.data;
    return render_template("login.html",form=form,username=username,password=password)

好了,执行命令运行,并在地址栏输入/login的url,结果为:


阿欧,结果好像和预想的不一致,出现了错误,报错信息显示为"使用CSRF必须有一个秘钥"好了现在解释一下什么是CSRF:

CSRF(Cross-site request forgery跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,并且攻击方式几乎相左。XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性.-----摘自百度百科
简单说,就是A通过B的某些信息,如地址,cookie等,模拟一个表单进行提交,进行攻击

好了,我们了解了CSRF已经他的危害,那么这个问题如何解决呢?对于flask-wtf来说,非常的简单,设置一个秘钥即可,每次在form中包含秘密信息,进行用户cookie等之外的额外验证,在程序上需进行如下设置:

app=Flask(__name__)
app.config["SECRET_KEY"]="Niu_blog"

其中app.config是一个字典,可用来存储一些程序本身的变量,如db配置,log配置等
app.config['SECRET_KEY']为通用秘钥。
注意出于安全性,此秘钥一般不会写在硬编码中,要么写在一个config的文件中,要么设置在系统的环境变量里。
配置完成后,再次请求服务器,结果为:

此时生成的源代码为(注意csrf部分):

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>blog - 牛博客</title>
    <script src="http://cdn.bootcss.com/jquery/2.2.4/jquery.min.js"></script>
</head>
<body>
 <form method="post">
     <input id="csrf_token" name="csrf_token" type="hidden" value="IjQ0MmVlMTMyOWMzMDgyNDM0NGE4OGZiODg2MmMxNjI0ZmQxYjQ1YzQi.C2PJ8A.UmKkAGwKvACzM6qNbH0bN5bbn2M">
     <label for="username">请输入用户名</label> <input id="username" name="username" type="text" value="">
     <label for="password">请输入密码</label> <input id="password" name="password" type="password" value="">
     <input id="submit" name="submit" type="submit" value="登录">
 </form>
</body>
</html>

输入用户名密码后显示为:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容