【Flask】扩展:Jinja2 模板(三)

Jinja2 模版继承

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>

子模版

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

首先第一行定义子模版继承父模版,并且可以看到子模版实现了父模版的title,并填充了自己的内容,再看head这个block,里面调用了super()这个函数,这个函数的目的是执行父模版中的代码,把父模版中的内容添加到子模版中,如果没有这一句,则父模版中处于head这个block中的代码将会被子模版中的代码给覆盖掉。

另外,模版中不能出现重名的block,如果一个地方需要用到另一个block中的内容,可以使用self.back name的方式进行引用。

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

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

另外,在子模版中,所有的文本标签和代码都要添加到父模版中继承的block中,否则这些文本和标签将不会被渲染。

Jinja2 转义

什么是转义? 在模版渲染字符串的时候,字符串有可能包含一些非常危险的字符比如<>等,这些字符会破坏原来的html标签结构,更严重的可能会发生XSS跨域脚本攻击,因此如果碰到<>这些字符的时候,应该转义成HTML能正确表示这些字符的写法,比如>HTML中应该用<来表示等。

但是Flask中默认没有开启全局自动转义,针对那些.html.htm.xmlxhtml结尾的文件,如果采用render_template函数进行渲染的,则会开启自动转义。并且当用render_template_string函数的时候,会将所有的字符串进行转义后再渲染。

而对于Jinja2默认没有开启全局自动转义,原因如下:

  • • 渲染到模板中的字符串并不是所有都是危险的,大部分还是没有问题的,如果开启自动转义,那么将会带来大量的不必要的开销。

  • • Jinja2很难获取当前的字符串是否已经被转义过了,因此如果开启自动转义,将对一些已经被转义过的字符串发生二次转义,在渲染后会破坏原来的字符串。

在没有开启自动转义的模式下(比如以.conf结尾的文件),对于一些不信任的字符串,可以通过{{ content_html|e }}或者是{{ content_html|escape }}的方式进行转义。在开启自动转义的模式下,如果想关闭自动转义,可以通过{{ content_html|safe }}的方式关闭自动转义。

{%autoescape true/false%}...{%endautoescape%}可以将一段代码块放在中间,来关闭或开启自动转义。

{% autoescape false %}
  <p>autoescaping is disabled here
  <p>{{ will_not_be_escaped }}
{% endautoescape %}

Jinja2 数据类型和运算符

  • • 数据类型Jinja2支持许多数据类型,包括:字符串、整型、浮点型、列表、元组、字典、布尔

  • • 运算符

    • • +号运算符:可以完成数字相加,字符串相加,列表相加。但是并不推荐使用+运算符来操作字符串,字符串相加应该使用~运算符。

    • • -号运算符:只能针对两个数字相减。

    • • /号运算符:对两个数进行相除。

    • • %号运算符:取余运算。

    • • *号运算符:乘号运算符,并且可以对字符进行相乘。

    • • **号运算符:次幂运算符,比如2**3=8。

    • • in操作符:跟python中的in一样使用,比如{{1 in [1,2,3]}}返回true

    • • ~号运算符:拼接多个字符串,比如{{"Hello" ~ "World"}}将返回HelloWorld

Jinja2 配置静态文件

web应用中会出现大量的静态文件来使得网页更加生动美观。类似于CSS式样文件,JavaScript脚本文件,图片文件,字体文件等静态资源。在Jinja2中加载静态文件非常简单,只需要通过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这个文件夹下寻找。

Jinja2 高级视图

  • • 类视图视图可以基于类来实现,类视图的好处是支持继承,但是类视图不能跟函数视图一样,写完类视图还需要通过app.add_url_rule(url_rule, view_func)来进行注册。

    • • 标准类视图标准类视图是继承自flask.views.View,并且在子类中必须实现dispatch_request方法,这个方法类似于视图函数,也要返回一个基于Response或者其子类的对象。

    from flask.views import View
    class PersonalView(View):
        def dispatch_request(self):
            return "View"
    # 类视图通过add_url_rule方法和url做映射
    app.add_url_rule('/users/',view_func=PersonalView.as_view('personalview'))
  • • 基于调度方法的视图Flask还为我们提供了另外一种类视图flask.views.MethodView,对每个HTTP方法执行不同的函数(映射到对应方法的小写的同名方法上)。

    class LoginView(views.MethodView):
        # 当客户端通过get方法进行访问的时候执行的函数
        def get(self):
            return render_template("login.html")
    
        # 当客户端通过post方法进行访问的时候执行的函数
        def post(self):
            email = request.form.get("email")
            password = request.form.get("password")
            if email == 'xx@qq.com' and password == '111111':
                return "登录成功!"
            else:
                return "用户名或密码错误!"
    
    # 通过add_url_rule添加类视图和url的映射,并且在as_view方法中指定该url的名称,方便url_for函数调用
    app.add_url_rule('/myuser/',view_func=LoginView.as_view('loginview'))

类视图中使用装饰器,比方有时候需要做权限验证的时候。

  from flask import session
  def login_required(func):
      def wrapper(*args,**kwargs):
          if not session.get("user_id"):
              return 'auth failure'
          return func(*args,**kwargs)
      return wrapper

装饰器实装完成后,可以在类视图中定义一个属性叫做decorators,然后存储装饰器。以后每次调用这个类视图的时候,就会执行这个装饰器。

class UserView(views.MethodView):
    decorators = [user_required]
    ...
  • • 蓝图之前我们实装的url和视图函数都是处于同一个文件,如果项目比较大的话,这显然不是一个合理的结构,而蓝图可以优雅的帮我们实现这种需求。

  from flask import Blueprint
  bp = Blueprint('user',__name__,url_prefix='/user/')
  
  @bp.route('/')
  def index():
      return "用户首页"
  
  @bp.route('profile/')
  def profile():
      return "个人简介"

在主程序中,通过app.register_blueprint()方法将这个蓝图注册到url映射中。

from flask import Flask
import user # 导入蓝图

app = Flask(__name__)
app.register_blueprint(user.bp)  # 注册蓝图

if __name__ == '__main__':
    app.run(host='0.0.0.0',port=9000)

以后访问/user//user/profile/,都是执行的user.py文件中的视图函数,这样就实现了项目的模块化。

  • • 寻找静态文件默认不设置任何静态文件路径,Jinja2会在项目的static文件夹中寻找静态文件。也可以设置其他的路径,在初始化蓝图的时候,Blueprint这个构造函数,有一个参数static_folder可以指定静态文件的路径。

    bp = Blueprint('admin',__name__,url_prefix='/admin',static_folder='static')

static_floder可以是相对路径(相对蓝图文件所在的目录),也可以是绝对路径。

在模版中引用蓝图,使用蓝图名 + . + static来引用。

   <link href="{{ url_for('admin.static',filename='about.css') }}">
  • • 寻找模版文件跟静态文件一样,默认不设置任何模版文件的路径,将会在项目的templates中寻找模版文件。也可以设置其他的路径,在构造函数Blueprint中有一个template_floder参数可以设置模版的路径。

   bp = Blueprint('admin',__name__,url_prefix='/admin',template_folder='templates')

模版文件和静态文件有点区别,以上代码实装以后,如果渲染一个模版return render_template('admin.html')Flask默认会去项目根目录下的templates文件夹中查找admin.html文件,如果找到了就直接返回,如果没有找到,才会去蓝图文件所在的目录下的templates文件夹下去寻找。

  • • url_for生成url用url_for生成蓝图的url,使用的格式是:蓝图名称 + . + 视图函数名称。比如要获取admin这个蓝图下的index视图函数的url

    url_for('admin.index')

其中这个蓝图名称是在创建蓝图的时候,传入的第一个参数。

    bp = Blueprint('admin',__name__,url_prefix='/admin',template_folder='templates')
  • • 子域名子域名在很多网站中都会用到,比如一个网站叫做xxx.com,那么我们可以定义一个子域名oa.xxx.com来作为oa管理系统的网址,子域名的实现一般也会通过蓝图来实现,在之前的章节中,创建蓝图的时候添加了一个url_prefix=/user作为url前缀,那样我们就可以通过/usr/来访问user下的url。但使用子域则不需要。另外,还需要配置SERVER_NAME,比如app.config[SERVER_NAME]='example.com:9000'。并且在注册蓝图的时候,还需要添加一个subdomain的参数,这个参数就是子域名的名称。

    • • 参考示例admin.py

   from flask import Blueprint
   bp = Blueprint('admin',__name__,subdomain='admin')
   
   @bp.route('/')
   def admin():
       return 'Admin Page'

app.py

   from flask import Flask
   import admin
   
   # 配置`SERVER_NAME`
   app.config['SERVER_NAME'] = 'example.com:8000'
   # 注册蓝图,指定了subdomain
   app.register_blueprint(admin.bp)
   
   if __name__ == '__main__':
       app.run(host='0.0.0.0',port=8000,debug=True)

实装以上两个文件后,还是不能正常的访问admin.example.com:8000这个子域名,因为我们没有在host文件中添加域名解析,可以在最后添加一行127.0.0.1 admin.example.com,就可以访问了。另外,子域名不能在127.0.0.1上出现,也不能在localhost上出现。

来源:网络

欢迎关注我的公众号“壹葉筆記”,技术文章第一时间推送。


原文始发于微信公众号(壹葉筆記):【Flask】扩展:Jinja2 模板(三)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/36102.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!