flask与模板的应用

776 阅读7分钟

一、模板

想要开发出易于维护的应用,关键在于编写形式简洁且结构良好的代码。到目前为止你看到的示例都还过于简单,无法说明这一点,但flask视图函数的两个完全独立的作用却被融合到了一起,这就产生了一个问题。

视图函数的作用很明确,即生成请求的响应,如别再和我说你不会用flask编写简单应用了!示例中所示。对于简单的请求来说,这就足够了。但是在很多情况下,请求会改变应用的状态,而这种变化就发生在视图函数中。

以用户在网站上注册新用户为例,用户在表单中输入电子邮件和密码,然后点击提交按钮。服务器接收到包含用户数据的请求,然后flask把请求分派给处理注册请求的视图函数。这个视图函数需要访问数据库,添加新用户,然后生成响应返回给浏览器,指明操作成功还是失败。这两个过程分别称为业务逻辑表现逻辑

把业务逻辑和表现逻辑混在一起会导致代码难以理解和维护,假设要为一个大型表格构建HTML代码,表格中的数据由数据库中读取的数据以及必要的HTML字符串连接在一起,把表现逻辑迁移到模板中可以提升应用的维护性。

模板是包含响应文本的文件,其中包含占位变量表示的动态部分,其具体值只在请求上下文中才能知道。使用真实值替换变量,再返回最终得到的响应字符串,这一过程称为渲染。为了渲染模板,flask使用名为Jinja2的强大模板。

1.1 Jinja2模板引擎

形式最简单的Jinja2模板是一个响应文本的文件。

在templates文件夹下创建HTML文件

示例1-1 templates/index.html

<h1>Hello world</h1>

示例1-2 templates/user.html

<h1>Hello,{{name}}</h1>

1.1.1 渲染模板

默认情况下flask在应用目录中的templates子目录中寻找模板。

示例1-3 hello.py 渲染模板

from flask import Flask, render_template

app = Flask(__name__)


@app.route('/')
def index():
    return render_template('index.html')


@app.route('/user/<name>')
def user(name):
    return render_template('user.html', name=name)

flask提供**render_template()**函数把jinja2模板引擎集成到应用中,这个函数第一个参数是模板的文件名,随后的参数都是键——值对(关键字参数),表示模板变量对应的具体值。在这段代码中,第二个模板中收到一个名为name的变量。

示例1-4 hello.py

from flask import Flask, render_template

app = Flask(__name__)


@app.route('/')
def index():
    # 类
    class Person(object):
        name = u'p17bdw'
        age = 18

    person1 = Person()
    context = {
        'username': 'c17bdw',
        'gender': u'男',
        'age': 17,
        'person': person1,
        'websites': {
            'baidu': 'www.baidu.com',
            'google': 'www.google.com'
        }
    }
    return render_template('index.html', **context)

1.1.2 变量

在模板中使用{{name}}结构表示一个变量,这是一种特殊的占位符,告诉模板这个值从渲染模板时使用的数据中获取。

示例1-3中,通过对context的封装,就可以在模板中通过变量访问到context里面的数据。

示例1-5 index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>这个是HTML中出现的文字</h1>

<p>用户名:{{username}}</p>
<p>性别:{{gender}}</p>
<p>年龄:{{age}}</p>

<hr >
<p>名字:{{person.name}}</p>
<p>年龄:{{person.age}}</p>

<hr >
<p>百度:{{websites['baidu']}}</p>
<p>谷歌:{{websites['google']}}</p>

</body>
</html>

从上面的代码示例中不难发现Jinja2能识别所有的变量类型,甚至是复杂的类型,例如列表、字典和对象。

1.1.3 控制结构

Jinja2提供了多种控制结构可以用来改变模板的渲染过程。

示例 1-6 if_statement.py

# 输入http://127.0.0.1:5000/1/ 为登录状态,否则为未登录状态
from flask import Flask, render_template


app = Flask(__name__)


@app.route('/<is_login>/')
def index(is_login):
    if is_login == '1':
        user = {
            'username': u'啃书君',
            'age': 18
        }
        return render_template('index.html', user=user)
    else:
        return render_template('index.html')

示例1-7 index.html

<!DOCTYPE html>
<html lang="zh">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title></title>
</head>
<body>
	<!-- 当user存在,并age大于15时,页面渲染的效果 -->
	{% if user and user['age'] > 15 %}
		<a href="#">{{user['username']}}</a>
		<a href="#">注销</a>
	
	{% else %}
		<a href="#">登录</a>
		<a href="#">注册</a>
	{% endif %}
</body>
</html>

上述示例代码的运行差异是登录状态与未登录状态的区别。

1.1.4 循环结构

示例 1-8 for_statement.py

from flask import Flask, render_template

app = Flask(__name__)
# for遍历字典
@app.route('/')
def index():
    books = [
        {
            'name': '西游记',
            'author': '吴承恩',
            'price': 109
        },
        {
            'name': '红楼梦',
            'author': '曹雪芹',
            'price': 200
        },
        {
            'name': '三国演义',
            'author': '罗贯中',
            'price': 120
        },
        {
            'name': '水浒传',
            'author': '施耐庵',
            'price': 130
        }
    ]

    return render_template('index.html', books=books)

在这部分,需要注意最后一行代码,第一个books是等一下交给和HTML代码去访问的,第二个books是在代码中定义的books

示例1-9 index.html

<!DOCTYPE html>
<html lang="zh">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title></title>
</head>
<body>
	<table border="1">
		<thead>
			<th>书名</th>
			<th>作者</th>
			<th>价格</th>
		</thead>
	</table>
	<tbody>
		{% for book in books %}
			<tr>
				<td>{{book.name}}</td>
				<td>{{book.author}}</td>
				<td>{{book.price}}</td>
			</tr>
		{% endfor %}
	</tbody>
</body>
</html>

1.1.5 过滤器

1、介绍和用法

  • 介绍:过滤器可以处理变量,把原始的变量经过处理后再展示出来。作用的对象是变量。
  • 语法:

{{ avatar|default('xxx') }}

2、default过滤器:如果当前变量不存在,这时候可以指定默认值。

3、length过滤器:求列表、字符串、字典、元组的长度。

4、常用过滤器介绍

过滤器功能
abs(value)返回一个绝对值
first(value)返回序列的第一个值,示例:names|first
last(value)返回序列的最后一个值,示例:names|last
length(value)返回一个序列的长度,示例:names|length
join(value, d='u')将一个序列用d这个参数拼接成字符串

我只介绍上面这几个函数,flask可以识别所有的函数。

看下面的示例:

index.html

<!DOCTYPE html>
<html lang="zh">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title>过滤器</title>
</head>
<body>
	
	<p>评论数:({{comments|length}})</p>
	{% for comment in comments %}
		<li>
			<a href="#">{{comment.user}}</a>
			<p>{{comment.content}}</p>
		</li>
</body>
</html>		

filter_dmeo.py

from flask import Flask,render_template

app = Flask(__name__)


@app.route('/comments')
def index():
    comments = [
        {
            'user': '张三',
            'content': 'up加油'
        },
        {
            'user': '李四',
            'content': 'undo'
        }
    ]
    return render_template('index.html', comments=comments)

1.1.6 继承与block

为了避免代码的复用,可以使用模板继承,这个类似于Python代码中的继承。

1、继承的作用和语法

  • 作用:可以把公共的代码放入父模板中,避免每个模板写同样的代码。
  • 语法:{% extend base.html %}

2、block的实现

  • 作用:可以让子模板实现自己的需求。父模板需要提前定义好
  • 注意点:子模板的代码必须放在block块中。

base.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>{% block title %}{% endblock %}</title>

	<style type="text/css">
		.nav {
			background: #3a3a3a;
			height: 65px;

		}
		ul {
			overflow: hidden;

		}

		ul li {
			float: left;
			list-style: none;
			padding: 0 10px;
			line-height: 65px;

		}

		ul li a {
			color: #fff
		}
	</style>
	{% block head %}{% endblock %}
</head>
<body>

	<div class="nav">
		<ul>
			<li><a href="#">首页</a></li>
			<li><a href="/login">发布问答</a></li>
		</ul>
	</div>
	{% block main %}{% endblock %}


	
</body>
</html>

index.html

{% extends base.html %}

{% block head %}
<style type="text/css">
	
</style>

<link rel="stylesheet" type="text/css" href="">
<script type="text/javascript"></script>

{% endblock %}

{% block title %}
首页
{% endblock %}

{% block main %}
这是首页
{% endblock %}

login.html

{% extends "base.html" %}

{% block title %}
登录界面
{% endblock % }

{% block main %}
这个是登录界面
{% endblock %}

app.py

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
	return render_template('index.html')

@app.route('/login/')
def login():
	return render_template('login.html')

上面的就是继承的案例,把公共的代码放入base.html(父类),在该文件中需要使用block和endblock进行占位,而后在子类中进行替换,即可使用。

2.1 使用Flask-Bootstrap集成Bootstrap

bootstrap是Twitter开发的开源框架,它提供的用户组件可以创建一个简洁的web页面。而且兼容现代桌面和移动平台的web浏览器。

我们现在要使用的是flask-bootstrap拓展,可以使用pip安装

pip install flask-bootstrap

接下来,看一下bootstrap的使用方法。

user.html

{% extends "bootstrap/base.html" %}

{% block title %}flask{% endblock %}

{% block navbar %}
<div class="navbar navbar-inverse" role="navigation">
	
	<div class="container">
		 <div class="navbar-header">
      <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
        <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <a class="navbar-brand" href="/">flasky</a>
    </div>
	</div>

	 <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
      <ul class="nav navbar-nav">
        <li class="active"><a href="/">Home</a></li>
      </ul>
    </div><!-- /.navbar-collapse -->
</div>
{% endblock %}

{% block content %}
<div class="container">
	<div class="page-header">
		<h1>Hello,{{name}}</h1>
	</div>
</div>
{% endblock %}

hello.py

from flask import Flask, render_template
from flask_bootstrap import Bootstrap


app = Flask(__name__)
bootstrap = Bootstrap(app)

@app.route('/user/<name>/')
def user(name):
    return render_template('user.html', name=name)

@app.route('/'):
def index():
	return '<h1>Hello world</h1>'

2.2 链接

任何具有多个路由的应用都需要可以连接不同页面的链接。例如导航栏。

在模板中直接编写简单路由的URL并不难,但是对于包含可变部分的动态路由、在模板中构建URL就很困难了。

为了避免这些问题,Flask提供了url_for( )辅助函数,它使URL映射中保存的信息生成URL。

url_for( )函数最简单的用法是以视图函数名作为参数,返回对应的URL。

url_for()生成动态url时,将动态部分作为关键字参数传入。

使用方法:

在模板引擎中的的\换成url_for('index')即可。

2.3 静态文件

web应用不仅由Python源码与模板组成,还有很多的静态文件,例如,css、图片和js文件。

在默认设置下,Flask在应用根目录中名为static的子目录中寻找静态文件,如果需要的话,可以在static文件夹下添加子文件

使用方法:

url_for('static', filename='css/style.css')

最后

聪明人知道世界很大,他们所知甚少。

愿意包容不同的人和意见,有能力学习和改变自己,不断通过输入进步的信息,来更新自己的观念。

点个【在看】,让我们一起在人云亦云的网络时代,做个清醒的聪明人,而不是被大数据操控,却浑然不知的笨蛋。

我是啃书君,一个专注于学习的人。你懂的越多,你不懂的越多,更多精彩内容我们下期再见!