3.模板

作者: ArtioL | 来源:发表于2018-10-29 09:22 被阅读0次

    3.1 模板基本用法

        创建模板
    
        user = {
    
            "username" : "Artio",
    
            "bio": "A boy who loves movies and music"
    
        }
    
        movies = [
    
            {"name": "My nEIGHBORS Totoro", "year": "1998"},
    
            {"name": "Three Colous trilogy", "year": "1993"}
    
    ]
    

    电影清单模板

    watchlist.html
    
    <!DOCTYPE html>
    
    <html lang="en">
    
        <head>
    
            <meta charset="utf-8">
    
            <title>{{ user.username}}'s Watchlist</title>
    
        </head>
    
        <body>
    
            <a href="{{ url_for("index")  }}">&larr;Return</a>
    
            <h2>{{ user.username }}</h2>
    
            {% if user.bio %}
    
                <i>{{ user.bio}} </i>
    
            {% else %}
    
                <i>This user has not provided a bio.</i>
    
            {% endif %}
    
            <h5> {{ user.username }}'s WatchList</h5>
    
            <ul>
    
                    {% for movie in movies %}
    
                        <li>{{ movie.name}}-{{ movie.year }}</li>
    
                    {% endfor %}
    
            </ul>
    
        </body>
    
    </html>
    
    (1) 语句
    if 语句
    
    {% if %}
    
        ...
    
    {% elif %}
    
        ...
    
    {% else %}
    
        ...
    
    {% endif %}
    
        for 语句
    
    {% for item in items %}
    
    {% endfor %}
    
    (2)表达式
        比如字符串, 变量,函数调用等;
    
    {{ user  }}
    
    (3) 注释
        {# ... #}
    

    另外 在模板中 Jinja2支持使用"." 获取变量的属性 , 比如user字典中的username键值通过"."获取, 即user.username 在效果上等同于user["username"]

    3.1.2 模板语法

    { % if user.bio %}
    
            <i>{{ user.bio }}</i>
    
    { % else %}
    
            <i>This user has not provider a bio.</i>
    
    {% endif %}
    
    {% for movie in movies %}
    
        {{ movie.index()  }}-{{ movie.name }}
    
    { % endfor %}
    

    常用的JInja2 for模板特殊变量

    变量 说明
    loop.index 当前迭代数(从1开始)
    loop.index() 当前迭代数(从0开始)
    loop.reindex 当前反向迭代数(从1开始)
    loop.reindex() 当前反向迭代数(从0开始)
    loop.first 如果是第一个元素 则为True
    loop.last 如果是最后一个元素 则为True
    loop.preitem 上一个迭代的条目
    loop/nextitem 下一个迭代的条目
    loop.leghth 序列包含的元素数量

    3.1.3 渲染模板

    from flask import render_template, Flask
    
    ...
    
    @app.route("/watchlist")
    
    def watch_list():
    
            return render_template("watchlist.html", user=user, movies=movies)
    

    除了render_template Flask还提供了一个render_template_string()函数用来渲染模板字符串

    • {{ mylist[0] }} 获取列表的第一个元素
    • {{ my_tuple[0] }} 获取元祖的第一个元素
    • {{ my_dict["name"]}} 获取字典的键name的值
    • {{ my_object.name() }} 调用my_object调用某方法的返回值

    3.2.1 上下文

    除了渲染时传入变量 还可以在模板中定义变量 使用set标签

    {% set navigation = [("/", "Home"), ("/about", "About")] %}
    

    也可以将一部分模板数据定义为变量 使用set 标签和endset标签声明开始和结束

    {% set navigation %}
        <a href="/"> Home </a>
        <a href="/about">About</a>
    {% endset %}
    
    • 1.内置上下文
      Flask在木棒上下文提供了一些内置变量 可以再模板中直接使用
      变量 | 说明
      ----- | ------
      config| 当前的配置对象
      request| 当前的请求对象 ,在已激活的请求环境下可用
      session| 当前的会话对象,在已激活的请求环境下可用
      g | 与请求绑定的全局变量 在已激活的请求环境下可用
    • 2.自定义上下文
    注册模板上下文处理函数
    @app.content_processor
    def inject_foo():
      foo = "I am foo"
      return dict(foo=foo)
    

    当我们调用render_template()函数渲染任意一个模板时,所有使用app.content_processor装饰器的模板上下文处理函数都会被执行,这些函数的返回值会被添加到模板中, 因此我们可以再模板中直接使用foo变量

    • 除了使用app.context_processor装饰器 也可以直接将其作为方法调用 传入模板上下文处理函数
    def inject_foo():
        foo = "I am foo"
        return dict(foo=foo)
    app.context_processor(inject_foo)
    

    使用lambda可以简化为:

    app.context_processor(lambda : dict(foo="I am foo.")
    

    3.2.2 全局对象

    • 1.内置全局函数
      jinja2内置模板全局函数
    函数 说明
    range(start,] stop[, step]) 和python中的range()用法相同
    lipsum(n=5, html=True, min=20, max=100) 生成随机文本(lorem ipsum), 可以再测试时间用来填充页面。默认生成5段HTML文本 ,每段包含20~100个单词
    dict(**items) 和python中的dict()用法想通过

    除了Jinja2内置的全局函数 Flask模板内置了2个全局函数

    函数 说明
    url_for() 用于生成URL函数
    get_flashed_messages() 用于获取flask消息的函数
    • 2.自定义全局函数
      除了使用app_context_processor 注册模板上下文处理函数来传入函数 我们也可以使用app.template_global装饰器来将函数注册为模板全局函数。
    @app.template_global()
    def bar():
          return "I am bar."
    
    默认使用函数的原名称传入模板 在app.template_global() 装饰器中使用name参数可以指定一个自定义的名称

    3.2.3 过滤器

    过滤器使用的例子:

    {{ name| title }}

    Jinja2常用内置过滤器

    过滤器 说明
    default(value, default_value="", boolean=False) 设置默认值,默认值作为参数传入,别名为d
    escape(s) 转义HTML文本,别名为e
    first(seq) 返回序列的第一个元素
    last(seq) 返回序列的最后一个元素
    length(object) 返回变量的疮毒
    random(seq) 返回序列中的随机元素
    safe(value) 将变量值标记为安全, 避免转义
    trim(value) 清楚变量值前后的空格
    max(value, case_sensitive=False, attribute=None) 返回序列中的最大值
    min(value, case_sensitive=False, attribute=None) 返回序列中的最小值
    unique(value, case_sensitive=False, attribute=None) 返回序列中不重复的值
    striptags(value) 清楚变量值内的HTML标签
    urlize(value, trim_url_limit=None, nofollow=False, target=None, rel=None) 将URL文本转换为可单击的HTML连接
    wordcount(s) 计算单词数据
    tojson(value, indent=None 将变量值转换为JSON数据
    turncate(s, length=255, killwords=False, end="...", leeway=None) 截断字符串,常用于显示文本摘要, length参数设置截断的长度, killwords参数设置是否截断单词, end参数设置结尾的符号.
    <h1> Hello, {{  name|default("陌生人")|title  }}</h1>
    

    示例中name为变量 设置默认值 并将其标题化

    • 2.自定义过滤器
      from flask import Markup
      
      @app.template_filter()
      def musical(s):
            return s + Markup(' %#9835;')
      
      使用
      {{ name|musical }}
      

    3.2.4 测试器

    使用is连接变量和测试器

      {% if age is number %}
          {{ age * 365 }}
      {% else %}
          无效的数字
      {% endif %}
    

    1. 内置测试器

    Jinja2内置测试器

    测试器 说明
    callable(value) 判断对象是否可被调用
    defined(value) 判断变量是否已定义
    undefined(value) 判断对象是否未定义
    none(value) 判断对象是否为None
    number(value) 判断对象是否为数字
    string(value) 判断对象是否为字符串
    sequence(value) 判断变量是否是序列, 如字符串 列表 元祖
    iterable(value) 判断变量是否可迭代
    mapping(value) 判断变量是否是匹配对象 比如字典
    sameas(value, other) 判断变量与pther是否指向相同的内存地址

    其他参数可以添加括号传入 比如sameas

    • 判断foo是否和bar是同一地址
      {% if foo is sames(bar) %}
      

    2.自定义测试器

    @app.template_test()
    def baz(n):
        if n == "baz":
              return True
        return False
    

    同样的 测试器名称默认用函数名,需要自定义用name参数传入装饰器app.template_test(name=name)

    3.2.5

    模板环境对象

    在程序中, 我们可以使用app.jinja_env更改Jinja2设置 比如我们自定义所有的定界符
    使用variable_start_string, variable_end_string

    app = Flask(__name__)
    app.jinja_env.variable_start_string = "[["
    app.jinja_env.variable_start_string = "]]"
    

    添加自定义全局对象

      def bar():
          return "I am bar"
      foo = "I am foo"
      app.jinja_env.globals["bar"] = bar # 注册全局函数
      app.jinja_env.globals["foo"] = foo  # 注册全局变量
    

    添加自定义过滤器

    def smiling():
          return s + ” :)“
    app.jinja_env.filters["smiling"] = smiling
    

    添加自定义测试器

      def baz(n):
          if n == "baz":
                retrun True
        return False
      app.jinja_env.test["baz"] = baz
    

    3.3 模板结构组织

    3.3.1 局部模板

    {% include "_banner.html" %}
    

    为了和普通模板区分 局部模板的命名一般以一个下划线开始
    3.3.2 宏
    macors.html

      {% macro qux(amount=1) %}
            {% if amount == 1 %}
                    I am qux.
            {% elif  amount > 1 %}
                    We are quxs.
            {% endif %}
      {% endmacor %}
    

    在其他模板调用

    {% from 'macros.html' import qux %}
    ...
    {{ qux(amount=5) }}
    

    在使用宏时 我们需要注意上下文问题 在Jinja2中, 处于性能的考虑 并且为了让这一切保持显示,默认情况下包含(include)一个局部模板会传递当前上下文到局部模板中 但导入(import)却不会
    , 具体来说 当我们使用render_template()函数渲染一个foo.html模板时.这个foo.html的模板上下文包含下列对象

    • Flask使用内置的模板上下文处理函数提高的g. session, config, request
    • 扩展使用内置的模板上下文处理函数提供的变量
    • 自定义模板上下文处理器传入的变量
    • 使用render_template() 函数传入的变量
    • Jinja2和Flaks内置及自定义的全局对象
    • Jinja2内置及自定义过滤器
    • Jinja2内置及自定义测试器
      inclode标签插入的局部模板 同样可以使用上述上下文的变量和函数 ,而导入另一个并非直接渲染的模板 比如macros.html时, 这个模板仅包含下列这些对象
    • Jinja2和Flask内置的全局函数和自定义全局函数
    • Jinja2内置及自定义过滤器
    • Jinja2内置及自定义测试器

    如果我们需要在导入的宏中使用第一个列表的2, 3, 4项,就需要在导入时显式的使用with context声明传入当前模板的上下文

    {% from "macros.html" import foo with context %}
    

    虽然Flask使用内置的模板上下文处理函数传入session, g, request 和config, 但它同时也是用app.jinjia_env.globals字典将这几个变量设置为全局变量 所以我们仍然可以再不显示声明传入上下文的情况下,直接在导入的宏中使用他们.

    3.3.3 模板继承

    编写模板 base.html

    <!DOCTYPE html>
    <html>
        <head>
            {% block head %}
                  <meta charset="utf-8">
                  <title>{% block title %}Template-HelloFlask{% endblock %}</title>
                  {%block styles%}{% endblock %}
            {% endblock %}
        </head>
        <body>
            <nav>
                <ul><li><a heaf="{{url_for('index') }}"></a>Home</li></ul>
            <nav>
            <main>
                {% block content %}{% endblock %}
            </main>
            <footer>
                  {% block footer %}{% endblock %}
            </footer>
            {% block scripts%}{% endblock %}
        </body>
    </html>
    

    2.编写子模板

    • index.html
    {% extends "base.html"  %}
    {% from "macros.html" import qux %}
    {% block content %}
      {% set name="baz" %}
      <h1>Template</h1>
      <ul>
          <li><a href="{{ url_for('watchlist') }}">WatchList</a></li>
          <li>Filters: {{ foo|masical }}</li>
          <li>Flobal: {{ bar() }}</li>
          <li>Test: {% if name is bar %}I am baz.{% endif %}</li>
          <li>macro: {{ que(amount=5) }}</li>  
    </ul>
    {% endblock %}
    

    extends必须是子模板的第一个标签

    3.4 模板进阶实践
    3.4.1 空白控制

     {% if user.bio %}
           <i>{{  user.bio  }}</i>
     {%  else  %}
           <i> This user has not provided a bio.</i>
     {% endif %}
    

    渲染后输出的HTML

    <I>{{  user.bio  }}</i>
    
    <i>his user has not provided a bio.</i>
    

    如果想在渲染时去掉这些空行 可以再边界符内测添加减号 {%- endfor %}
    会移除该语句前的空白,同理在右边的定界符内测添加减号将移除该语句后的空白:

     {% if user.bio -%}
           <i>{{  user.bio  }}</i>
     {%  else  -%}
           <i> This user has not provided a bio.</i>
     {%- endif %}
    

    现在的输出结果

    <I>{{  user.bio  }}</i>
    <i>his user has not provided a bio.</i>
    

    除了在模板中使用减号控制空白外 我们可以再模板环境对象提供的trim_blocks和lstrip_blocks属性设置 前者用来删除Jinja2语句后的第一个空行 后者则用来删除Jinja2语句所在行之前的空格和制表符。

    app.jinja_env.trim_blocks = True
    app.jinja_env.lstrip_blocks = True
    

    需要注意的是 宏内的空白控制行为不受trim_bolcks 和lstrip_bolcks控制

      {% macro qux(amount=1) %}
            {% if amount == 1 -%}
                    I am qux.
            {% elif  amount > 1 -%}
                    We are quxs.
            {%- endif %}
      {% endmacor %}
    

    3.4.2 加载静态文件

    url_for('static', filename="icon.png")

    <img src="{{  url_for('static', filename='icon.png') }}" width="50">
    <link rel="stylesheet" type="text/css" href={{  url_for(’static', filename='styles.css') }}">
    <link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='icon.png') }}">
    

    s使用宏加载静态资源

    为了方便加载静态资源 我们可以创建一个专门用于加载静态资源的宏

    {%   macro static_file(type, filename_or_url, locak=True  %}
        {% if local %}
              {% set filename_or_url = url_for("static", filename=file_or_url)  %}
        {% endif %}
        {% if type == 'css' %}
              <link rel="stylesheet" href="{{ filename_or_url }}" type="text/css">
        {% elif type == 'js' %}
              <script type="text/javascript" src="{{  filename_or_url  }}"></script>
        {%  elif type == 'icon' %}
               <link rel="icon" href="{{ filename_or_url }}" >
        {% endif %}
    {% endmacro %}  
    

    使用该宏加载静态资源

    {% from "macros.html" import static_file %}
    {{  static_file('css', 'css/bootstrap.min.css')  }}
    {{  static_file('css', 'https://maxcdn.../css/bootstrap.min.css', local=False) }}
    

    3.4.3 消息闪现

    后端逻辑

    from flask import Flask, flash, render_template
    
    app = Flask(__name__)
    app.secrect_key = "secrect string"
    
    @app.route("/flash")
    def just_flash():
       flash("I am flash, whos is looking for me?")
        return redirect(url_for("index"))
    

    前端逻辑

    <main>
        {% for message in get_flash_messages() %}
            <div class="alert">{{ message }}</div>
      {% endfor %}
      {% block content %}{% endblock %}
    <main>
    

    3.4.4 自定义错误页面

    404.html

    {% extents "base.html" %}
    
    {% block title %} 404 - Page Not Found {% endblock %}
    {% block content %}
    <h1> Page not Found</h1>
    <p>You are lost...</p>
    {% endblock %}
    

    后端代码

    from flask import Flask, render_template
    
    @app.errorhandler(404)
    def page_not_found(e):
      return render_template("errors/404.html"), 404
    

    错误处理函数接收异常对象作为参数,内置的异常对象提供了下列常用属性

    属性 说明
    code 状态码
    name 原因短语
    description 错误描述,另外使用get_description()方法还可以获取HTML格式的错误代码

    3.4.5 定义为JavaScript/CSS 变量

    <span data-id="{{ user.id }}" data-username="{{ user.username }}">
    {{ user.username }}
    </span>
    

    在JavaScript中 我们可以使用Dom元素的dataset属性获取data-*属性值
    比如element.dataset.username 或者使用getAttribute()方法, 比如element.getAttribute('data-username')
    以及用Jquery时 可以直接对Jquery对象调用data方法 比如$element.data("username")
    对于需要全局使用的属性 则可以在页面中使用嵌入式JavaScript定义变量

    <script type="text/javascript">
        var foo = "{{  foo_variable  }}";
    </script>
    

    同理css文件

    <style>
      :root  {
      --theme-color: {{ thrme_color }};
      --backgroup-url: {{ url_for("static", filename="backgroup.jpg") }}
        }
    </style>
    

    在css文件中 使用var()函数并传入变量名即可获取对应的变量值

    #foo {
      color: var(--theme-color);
    }
    #bar {
      backgroup: var(--backgroup-url);
    }
    

    相关文章

      网友评论

          本文标题:3.模板

          本文链接:https://www.haomeiwen.com/subject/pgvvtqtx.html