Flask框架——(Jinja2模板)

Flask框架——(Jinja2模板)

1. Jinja2模板介绍

模板是⼀个web开发必备的模块。因为我们在渲染⼀个⽹⻚的时候,并不是只渲染⼀个纯⽂本字符串,⽽是需要渲染⼀个有富⽂本标签的⻚⾯。这时候我们就需要使⽤模板了。在Flask中,配套的模板是Jinja2,Jinja2的作者也是Flask的作者。这个模板⾮常的强⼤,并且执⾏效率⾼。

2. Flask渲染Jinja模板

render_template方法:

注意: flask默认.html文件在templates文件夹下,如果要更改模板文件地址,应在创建时传递template_folder,指定具体路径,详见代码示例2。

代码示例1:

# render_template需要导入
from flask import Flask,render_template
app = Flask(__name__)

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

代码示例2:

from flask import Flask,render_template
app = Flask(__name__,template_folder=r'C:\templates')

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

注意:模板文件自动更新

# 模板文件自动更新
app.config['TEMPLATES_AUTO_RELOAD'] = True

3. 给模板的传参的三种方式

3-1. 直接指定

当传递参数极少时,可以直接传递参数render_template("xxx.html", username="xxxx")

代码示例:

# .py文件
return render_template("index.html", username="xxxx")

# index.html文件
{
     {
     username}}

3-2. dict的形式

当传递参数较多时,可以采取dict的方式

代码示例:

# .py文件
# 用字典,key:value的形式,定义参数
context = {
     
	xxx: "xxx"
}
return render_template("index.html", context=context)

# index.html文件
{
     {
     context.xxx}}

3-3. dict进阶 **context的方式

该方式是render_template中的一种方法,能将dict中自动改为xx1=xx1,xx2=xx2的关键字参数。减少.html中的代码量

代码示例:

# .py文件
context = {
     
xxx: "xxx"
}
return render_template("index.html", **context)
# .html文件
{
     {
      xxx }}

4. Jinja2模板过滤器

介绍:
过滤器是通过管道符号(|)进⾏使⽤的,例如:{ { name|length }},将返回name的⻓度。过滤器相当于是⼀个函数,把当前的变量传⼊到过滤器中,然后过滤器根据⾃⼰的功能,再返回相应的值,之后再将结果渲染到⻚⾯中。Jinja2中内置了许多过滤器,在这⾥可以看到所有的过滤器。

Flask中内置的所有过滤器:

  • abs(value):返回⼀个数值的绝对值。
  • default(value,default_value,boolean=false):如果当前变量没有值,则会使⽤参数中的值来代替。name|default('name1')——如果name不存在,则会使⽤juran来替代。boolean=False默认是在只有这个变量为undefined的时候才会使⽤default中的值,如果想使⽤python的形式判断是否为false,则可以传递boolean=true。也可以使⽤or来替换。
  • escape(value)或e:转义字符,会将<、>等符号转义成HTML中的符号。例如:content|escapecontent|e
  • first(value):返回⼀个序列的第⼀个元素。names|first
  • format(value,*arags,**kwargs):格式化字符串。例如以下代码:
    { { "%s" - "%s"|format('Hello?',"Foo!") }}将输出:Helloo? - Foo!
  • last(value):返回⼀个序列的最后⼀个元素。示例:names|last
  • length(value):返回⼀个序列或者字典的⻓度。示例:names|length
  • join(value,d=u''):将⼀个序列⽤d这个参数的值拼接成字符串。
  • safe(value):如果开启了全局转义,那么safe过滤器会将变量关掉转义。示例:content_html|safe
  • int(value):将值转换为int类型。
  • float(value):将值转换为float类型。
  • lower(value):将字符串转换为⼩写。
  • upper(value):将字符串转换为⼩写。
  • replace(value,old,new): 替换将old替换为new的字符串。
  • truncate(value,length=255,killwords=False):截取length⻓度的字符串。
  • striptags(value):删除字符串中所有的HTML标签,如果出现多个空格,将替换成⼀个空格。
  • trim:截取字符串前⾯和后⾯的空⽩字符。
  • string(value):将变量转换成字符串。
  • wordcount(s):计算⼀个⻓字符串中单词的个数。

自定义过滤器:
如果自带的所有过滤器不能满足需求,可以用@app.template_filter('自定义过滤器名字')自定义flask过滤器。

使用示例:
实现类似于下图评论中多少分钟前发布效果。
Flask框架——(Jinja2模板)_第1张图片
代码示例:

# .py 中
@app.template_filter("test")
def handle_time(time):
    """
    小于1分钟=> 刚刚
    大于1分钟小于1小时=>xx分钟之前
    大于1小时小于24小时=>xxx小时之前
    """
    if isinstance(time, datetime):
        now = datetime.now()
        # total_seconds 得到总秒数
        timestamp = (now - time).total_seconds()
        if timestamp < 60:
            return "刚刚"
        elif timestamp >=60  and timestamp <= 60*60:
            return "%s分钟之前" % int(timestamp/60)
        elif timestamp>= 60*60 and timestamp <=  60*60*24:
            return "%s小时之前" % int(timestamp/(60*60))
    else:
        return time

# .html中
<p>文章发表时间:{
     {
      now_time|test }}</p>

5. 控制语句

介绍:
每一种语言都有自己的控制语句,if…else之类的。
Flask种也有自己的控制语句,并且所有的控制语句都是放在{% ... %}中,并且有⼀个语句{% endxxx %}来进⾏结束,Jinja中常⽤的控制语句有if/for..in..

5-1. 控制语句 if

if:
if语句和python中的类似,可以使⽤>,<,<=,>=,==,!=来进⾏判断,也可以通过and,or,not,()来进⾏逻辑合并操作。

代码示例:

{
     % if kenny.sick %}
	Kenny is sick.
{
     % elif kenny.dead %}
	You killed Kenny! You bastard!!!
{
     % else %}
	Kenny looks okay --- so far
{
     % endif %}

5-2. 控制语句 for...in...

for...in...:
for循环可以遍历任何⼀个序列包括列表、字典、元组。并且可以进⾏反向遍历。

代码示例1:普通的遍历

<ul>
	{
     % for user in users %}
		<li>{
     {
      user }}</li>
	{
     % endfor %}
</ul>

代码示例2:遍历字典

<dl>
    {
     % for key, value in my_dict.items() %}
        <dt>{
     {
      key }}</dt>
        <dd>{
     {
      value }}</dd>
    {
     % endfor %}
</dl>

代码示例3:如果序列中没有值的时候,进⼊else

<ul>
    {
     % for user in users %}
        <li>{
     {
      user.username }}</li>
    {
     % else %}
        <li><em>no users found</em></li>
    {
     % endfor %}
</ul>

for循环中可用来获取当前遍历状态的变量:

变量 描述
loop.index 当前迭代的索引(从1开始)
loop.index0 当前迭代的索引(从0开始)
loop.first 是否是第⼀次迭代,返回True或False
loop.last 是否是最后⼀次迭代,返回True或False
loop.length 序列的⻓度

代码示例:

# .py
@app.route("/")
def index():
    context = {
     
        "username": "zero1",
        "books": ["Python", "PHP", "Java"],
        "users": {
     
            "name": "zero2",
            "age": 18,
            "address": "csc"
        }

    }
    return render_template("if_for.html", **context)

# .html
<hr>
{
     % for value in users.values() %}
    <p>{
     {
      loop.first }}</p>		# 返回Flase
    <p>{
     {
      value }}</p>
{
     % endfor %}

注意: 不可以使⽤continue和break表达式来控制循环的执⾏。

6. 宏和import语句

宏介绍:
模板中的宏跟python中的函数类似,可以传递参数,但是不能有返回值,可以将⼀些经常⽤到的代码⽚段放到宏中,然后把⼀些不固定的值抽取出来当成⼀个变量。

{
     % macro input(name, value='', type='text') %}
    <input type="{
     { type }}" name="{
     { name }}" value="{
     { value }}">
{
     % endmacro %}

以上例⼦可以抽取出了⼀个input标签,指定了⼀些默认参数。那么我们以后创建input标签的时候,可以通过他快速的创建:

<p>{
     {
      input('username') }}</p>
<p>{
     {
      input('password', type='password') }}</p>

import语句介绍:

在真实的开发中,会将⼀些常⽤的宏单独放在⼀个⽂件中,在需要使⽤的时候,再从这个⽂件中进⾏导⼊。import语句的⽤法跟python中的import类似,可以直接import...as...,也可以from...import...或者from...import...as...,假设现在有⼀个⽂件,叫做forms.html,⾥⾯有两个宏分别为input和textarea。

forms.html:
{
     % macro input(name, value='', type='text') %}
    <input type="{
     { type }}" value="{
     { value|e }}" name="{
     { name }}">
{
     % endmacro %}

{
     % macro textarea(name, value='', rows=10, cols=40) %}
    <textarea name="{
     { name }}" rows="{
     { rows }}" cols="{
     {
      cols
    }}">{
     {
      value|e }}</textarea>
{
     % endmacro %}

6-1. 导入宏代码示例:

import...as...形式:

{
     % import 'macro.html' as macro  %}
<tr>
    <td>用户名:</td>
    <td>{
     {
      macro.input('username') }}</td>
</tr>

from...import...as.../from...import...形式

{
     % from "macro.html" import input %}
<tr>
    <td>密码:</td>
    <td>{
     {
      input("password",type="password") }}</td>
</tr>

注意: 导⼊模板并不会把当前上下⽂中的变量添加到被导⼊的模板中,如果你想要导⼊⼀个需要访问当前上下⽂变量的宏,有两种可能的⽅法

  • 显式地传⼊请求或请求对象的属性作为宏的参数
  • 与上下⽂⼀起(with context)导⼊宏。

与上下⽂⼀起(with context)导⼊宏的方式:

 {
     % import 'macro.html' as macro with context %}

7. include和set语句

7-1. include语句

介绍: include语句可以把⼀个模板引⼊到另外⼀个模板中,类似于把⼀个模板的代码copy到另外⼀个模板的指定位置。

代码示例:

{
     % include 'header.html' %}
        主体内容
{
     % include 'footer.html' %}

该方法可用于导入重复部分,如常见的网页头和网页底部。减少代码重复量。

7-2. 赋值(set)语句

介绍: 赋值语句(set)可以为模板中添加变量。

代码示例1:

 {
     % set name='zero' %}

那么以后就可以使⽤name来代替zero这个值了,同时,也可以给他赋值为列表和元组:

代码示例2:

 {
     % set navigation = [('index.html', 'Index'), ('about.html', 'About')] %}

赋值语句创建的变量在其之后都是有效的,如果不想让⼀个变量污染全局环境,可以使⽤with语句来创建⼀个内部的作⽤域,将set语句放在其中,这样创建的变量只在with代码块中才有效。

代码示例3:

{
     % with %}
    {
     % set foo = 42 %}
    {
     {
      foo }}           foo is 42 here
{
     % endwith %}

也可以在with的后⾯直接添加变量,⽐如以上的写法可以修改成这样:

代码示例4:

{
     % with foo = 42 %}
    {
     {
      foo }}
{
     % endwith %}

这代码3、代码4两种方式都是等价的,一旦超出with代码块,就不能再使用foo这个变量了。

8. 模板继承

介绍:
Flask中的模板可以继承,通过继承可以把模板中许多重复出现的元素抽取出来,放在⽗模板中,并且⽗模板通过定义block给⼦模板开⼀个⼝,⼦模板根据需要,再实现这个block,假设现在有⼀个base.html这个⽗模板。

模板代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="stylesheet" href="base.css" />
    <title>{
     % block title %}{
     % endblock %}</title>
    {
     % block head %}{
     % endblock %}
</head>
<body>
    <div id="body">{
     % block body %}{
     % endblock %}</div>
    <div id="footer">
        {
     % block footer %}
        &copy; Copyright 2008 by <a href="http://domain.invalid/">you</a>
        {
     % endblock %}
    </div>
</body>
</html>

以上⽗模板中,抽取了所有模板都需要⽤到的元素html、body等,并且对于⼀些所有模板都要⽤到的样式⽂件style.css也进⾏了抽取,同时对于⼀些⼦模板需要重写的地⽅,⽐如title、head、body都定义成了block,然后
⼦模板可以根据⾃⼰的需要,再具体的实现。

代码示例:

{
     % extends "base.html" %}
{
     % block title %}首页{
     % endblock %}
{
     % block head %}
    {
     {
      super() }}
    <style type="text/css">
        .detail{
     
            color: red;
        }
    </style>
{
     % endblock %}
{
     % block content %}
    <h1>这里是首页</h1>
    <p class="detail">
      首页的内容
    </p>
{
     % endblock %}

⾸先第⼀⾏就定义了⼦模板继承的⽗模板,并且可以看到⼦模板实现了title
这个block,并填充了⾃⼰的内容,再看head这个block,⾥⾯调⽤了
super()这个函数,这个函数的⽬的是执⾏⽗模板中的代码,把⽗模板中的内容添加到⼦模板中,如果没有这⼀句,则⽗模板中处在head这个block中的代码将会被⼦模板中的代码给覆盖掉。

另外,模板中不能出现重名的block,如果⼀个地⽅需要⽤到另外⼀个block中的内容,可以使⽤self.blockname的⽅式进⾏引⽤。

<title>
    {
     % block title %}
        这是标题
    {
     % endblock %}
</title>
<h1>{
     {
      self.title() }}</h1>

以上示例中h1标签重⽤了title这个block中的内容,⼦模板实现了title这个block,h1标签也能拥有这个值。

另外,在⼦模板中,所有的⽂本标签和代码都要添加到从⽗模板中继承的
block中。否则,这些⽂本和标签将不会被渲染。

8-1. 静态文件的配置

介绍:
Web应⽤中会出现⼤量的静态⽂件来使得⽹⻚更加⽣动美观。类似于CSS样式⽂件、JavaScript脚本⽂件、图⽚⽂件、字体⽂件等静态资源。在Jinja中加载静态⽂件⾮常简单,只需要通过url_for全局函数就可以实现。

<link href="{
     { url_for('static',filename='about.css') }}">

url_for函数默认会在项⽬根⽬录下的static⽂件夹中寻找about.css⽂件,如果找到了,会⽣成⼀个相对于项⽬根⽬录下的/static/about.css路径。当然我们也可以把静态⽂件不放在static⽂件夹中,此时就需要具体指定了。

app = Flask(__name__,static_folder='C:\static')

那么访问静态⽂件的时候,将会到/static这个⽂件夹下寻找

你可能感兴趣的:(Flask框架)