美文网首页
Flask学习之旅 --- 中级篇

Flask学习之旅 --- 中级篇

作者: 成长之路丶 | 来源:发表于2019-07-22 16:12 被阅读0次

    jinja2模板

    在之前的章节中,视图函数只是直接返回文本,而在实际生产环境中其实很少这样用,因为实际的页面大多是带有样式和复杂逻辑的HTML代码,这可以让浏览器渲染出非常漂亮的页面。目前市面上有非常多的模板系统,其中最知名最好用的就是Jinja2Mako,我们先来看一下这两个模板的特点和不同:

    1. Jinja2:Jinja是日本寺庙的意思,并且寺庙的英文是temple和模板的英文template的发音类似。Jinja2是默认的仿Django模板的一个模板引擎,由Flask的作者开发。它速度快,被广泛使用,并且提供了可选的沙箱模板来保证执行环境的安全,它有以下优点:

      • 让前端开发者和后端开发者工作分离。
      • 减少Flask项目代码的耦合性,页面逻辑放在模板中,业务逻辑放在视图函数中,将页面逻辑和业务逻辑解耦有利于代码的维护。
      • 提供了控制语句、继承等高级功能,减少开发的复杂度。
    2. Marko:Marko是另一个知名的模板。他从DjangoJinja2等模板借鉴了很多语法和API,他有以下优点:

      • 性能和Jinja2相近,在这里可以看到。
      • 有大型网站在使用,有成功的案例。Reddit和豆瓣都在使用。
      • 有知名的web框架支持。PylonsPyramid这两个web框架内置模板就是Mako
      • 支持在模板中写几乎原生的Python语法的代码,对Python工程师比较友好,开发效率高。
      • 自带完整的缓存系统。当然也提供了非常好的扩展借口,很容易切换成其他的缓存系统。

    1.1 Flask模板简介

    1. 在渲染模版的时候,默认会从项目根目录下的templates目录下查找模版。
    2. 如果不想把模版文件放在templates目录下,那么可以在Flask初始化的时候指定template_folder来指定模版的路径。

    1.2 模版参数

    1. 在使用render_template渲染模版的时候,可以传递关键字参数。以后直接在模版中使用就可以了。
    2. 如果你的参数过多,那么可以将所有的参数放到一个字典中,然后在传这个字典参数的时候,使用两个星号,将字典打散成关键参数。

    flask代码:

    # 先导render_template模块
    from flask import Flask,render_template
    
    app = Flask(__name__)
    # 第一种方式
    @app.route("/")
    def hello_world():
        content = {
        "name":"张三",
        "age":18
        }    
        return render_template("index.html",**content)
    
    
    # 第二种方式
    @app.route("/list/")
    def my_list():  
        return render_template("list.html",name="张三",age=18)
    
    
    # 第三种方式
    @app.route("/detail/")
    def goods_detail():
        content = {
        "name":"张三",
        "age":18
        }    
        return render_template("detail.html",content=content)
    

    模板中的代码:

    3种方式对应的模板文件:

    第一种方式模板文件格式
    <p>{{name}}</p>
    <p>{{age}}</p>
    第二种方式模板文件格式
    <p>{{name}}</p>
    <p>{{age}}</p>
    第三种方式模板文件格式
    <p>{{content.name}}</p>
    <p>{{content.age}}</p>
    字典格式
    <p>{{content["name"]}}</p>
    <p>{{content["age"]}}</p>
    

    1.3 模板中使用url_for

    模版中的url_for跟我们后台视图函数中的url_for使用起来基本是一模一样的。也是传递视图函数的名字,也可以传递参数。
    使用的时候,需要在url_for左右两边加上一个{{ url_for('func') }}

    flask代码:

    from flask import Flask,render_template,url_for
    
    app = Flask(__name__)
    
    
    @app.route('/')
    def index():
        return render_template('index.html')
    
    @app.route('/accounts/login/<id>/')
    def login(id):
        return render_template('login.html')
    
    
    if __name__ == '__main__':
        app.run(debug=True)
    

    模板中的代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>知了课堂首页</title>
    </head>
    <body>
        这是从模版中渲染的
    {#    {{ 用来存放变量 }}#}
    {#    {% 用来执行函数或者逻辑代码 %}#}
        <p><a href="{{ url_for('login',ref='/',id='1') }}">登录</a></p>
    </body>
    </html>
    

    1.4 过滤器的基本使用

    1.4.1 什么是过滤器,语法是什么

    1. 有时候我们想要在模版中对一些变量进行处理,那么就必须需要类似于Python中的函数一样,可以将这个值传到函数中,然后做一些操作。在模版中,过滤器相当于是一个函数,把当前的变量传入到过滤器中,然后过滤器根据自己的功能,再返回相应的值,之后再将结果渲染到页面中。
    2. 基本语法:{{ variable|过滤器名字 }}。使用管道符号|进行组合。

    1.4.2 常用过滤器

    1. default过滤器:

    使用方式{{ value|default('默认值') }}。如果value这个key不存在,那么就会使用default过滤器提供的默认值。如果你想使用类似于python中判断一个值是否为False(例如:None、空字符串、空列表、空字典等),那么就必须要传递另外一个参数{{ value|default('默认值',boolean=True) }}
    可以使用or来替代default('默认值',boolean=True)。例如:{{ signature or '此人很懒,没有留下任何说明' }}

    2. 自动转义过滤器:
    1. safe过滤器:可以关闭一个字符串的自动转义。
    2. escape过滤器:对某一个字符串进行转义。
    3. autoescape标签,可以对他里面的代码块关闭或开启自动转义。
      {% autoescape off/on %}
          ...代码块
      {% endautoescape %}
      
    3. 常用过滤器

    1.first(value):返回一个序列的第一个元素。names|firstformat(value,*arags,**kwargs):格式化字符串。例如以下代码:
    {{ "%s" - "%s"|format('Hello?',"Foo!") }}
    将输出:Helloo? - Foo!

    1. last(value):返回一个序列的最后一个元素。示例:names|last
    2. length(value):返回一个序列或者字典的长度。示例:names|length
    3. join(value,d=u''):将一个序列用d这个参数的值拼接成字符串。
    4. safe(value):如果开启了全局转义,那么safe过滤器会将变量关掉转义。示例content_html|safe
    5. int(value):将值转换为int类型。
    6. float(value):将值转换为float类型。
    7. lower(value):将字符串转换为小写。
    8. upper(value):将字符串转换为小写。
    9. replace(value,old,new): 替换将old替换为new的字符串。
    10. truncate(value,length=255,killwords=False):截取length长度的字符串。
    11. striptags(value):删除字符串中所有的HTML标签,如果出现多个空格,将替换成一个空格。
    12. trim:截取字符串前面和后面的空白字符。
    13. string(value):将变量转换成字符串。
    14. wordcount(s):计算一个长字符串中单词的个数。

    1.4.3 自定义模版过滤器

    过滤器本质上就是一个函数。如果在模版中调用这个过滤器,那么就会将这个变量的值作为第一个参数传给过滤器这个函数,然后函数的返回值会作为这个过滤器的返回值。需要使用到一个装饰器:@app.template_filter('cut')

    @app.template_filter('cut')
    def cut(value):
        value = value.replace("hello",'')
        return value
    
    <p>{{ article|cut }}</p>
    
    flask代码:
    from flask import Flask,render_template
    from datetime import datetime
    
    app = Flask(__name__)
    app.config['TEMPLATES_AUTO_RELOAD'] = True
    
    @app.route('/')
    def index():
        context = {
            'position': -9,
            'signature': '<script>alert("hello")</script>',
            'persons':['python','ketang'],
            'age': "18",
            'article': 'hello pythonworld hello',
            'create_time': datetime(2017,10,20,16,19,0)
        }
        return render_template('index.html',**context)
    
    @app.template_filter('cut')
    def cut(value):
        value = value.replace("hello",'')
        return value
    
    @app.template_filter('handle_time')
    def handle_time(time):
        """
        time距离现在的时间间隔
        1. 如果时间间隔小于1分钟以内,那么就显示“刚刚”
        2. 如果是大于1分钟小于1小时,那么就显示“xx分钟前”
        3. 如果是大于1小时小于24小时,那么就显示“xx小时前”
        4. 如果是大于24小时小于30天以内,那么就显示“xx天前”
        5. 否则就是显示具体的时间 2017/10/20 16:15
        """
        if isinstance(time,datetime):
            now = datetime.now()
            timestamp = (now - time).total_seconds()
            if timestamp < 60:
                return "刚刚"
            elif timestamp>=60 and timestamp < 60*60:
                minutes = timestamp / 60
                return "%s分钟前" % int(minutes)
            elif timestamp >= 60*60 and timestamp < 60*60*24:
                hours = timestamp / (60*60)
                return '%s小时前' % int(hours)
            elif timestamp >= 60*60*24 and timestamp < 60*60*24*30:
                days = timestamp / (60*60*24)
                return "%s天前" % int(days)
            else:
                return time.strftime('%Y/%m/%d %H:%M')
        else:
            return time
    
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    
    模板中的代码:
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>python课堂</title>
    </head>
    <body>
    {#    <p>位置的绝对值是:{{ position|abs }}</p>#}
    {#    <p>个性签名:{{ signature or '此人很懒,没有留下任何说明' }}</p>#}
    {#        <p>个性签名:{{ signature|safe }}</p>#}
    {#    {% autoescape off %}#}
    {#    {% endautoescape %}#}
    {#    <p>{{ persons[0] }}</p>#}
    {#    <p>{{ "我的名字是:%s"|format('xiaoming') }}</p>#}
    {#    <p>人数{{ persons|length }}</p>#}
    {#    {% if age|int == 18 %}#}
    {#        <p>年龄是18岁</p>#}
    {#    {% else %}#}
    {#        <p>年龄不是18岁</p>#}
    {#    {% endif %}#}
    {#    <p>{{ article|replace('hello','aaa') }}</p>#}
    {#    <p>{{ article|truncate(length=5) }}</p>#}
    {#    <p>{{ signature|striptags }}</p>#}
    {#    <p>{{ article|cut }}</p>#}
        <p>发表时间:{{ create_time|handle_time }}</p>
    </body>
    </html>
    

    1.5 模板中的语句

    1.5.1 if条件判断语句

    if条件判断语句必须放在{% if statement %}中间,并且还必须有结束的标签{% endif %}。和python中的类似,可以使用>,<,<=,>=,==,!=来进行判断,也可以通过and,or,not,()来进行逻辑合并操作。

    flask代码:

    from flask import Flask,render_template
    
    app = Flask(__name__)
    app.config.update({
        'DEBUG': True,
        'TEMPLATES_AUTO_RELOAD': True
    })
    
    @app.route('/')
    def index():
        context = {
            'username': 'abc',
            'age': 17
        }
        return render_template('index.html',**context)
    
    
    if __name__ == '__main__':
        app.run()
    
    

    模板中的代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>首页</title>
    </head>
    <body>
        {% if username == 'xiaoming' %}
            <p>首页</p>
        {% else %}
            <p>我不是首页</p>
        {% endif %}
    
        {% if age >= 18 %}
            <p>你可以进入网吧了</p>
        {% else %}
            <p>未成年人禁止进入网吧</p>
        {% endif %}
    </body>
    </html>
    

    1.5.2 for循环语句

    jinja2中的for循环,跟python中的for循环基本上是一模一样的。也是for...in...的形式。并且也可以遍历所有的序列以及迭代器。但是唯一不同的是,jinja2中的for循环没有breakcontinue语句。

    flask代码:
    from flask import Flask,render_template
    
    app = Flask(__name__)
    app.config['TEMPLATES_AUTO_RELOAD'] = True
    
    @app.route('/')
    def index():
        context = {
            'users':['xiaoming1','xiaoming2','xiaoming3'],
            'person': {
                'username': 'xiaoming',
                'age': 18,
                'country': 'china'
            },
            'books':[
                {
                    'name': '三国演义',
                    'author':'罗贯中',
                    'price': 110
                },{
                    'name': '西游记',
                    'author':'吴承恩',
                    'price': 109
                },{
                    'name': '红楼梦',
                    'author':'曹雪芹',
                    'price': 120
                },{
                    'name': '水浒传',
                    'author':'施耐庵',
                    'price': 119
                }
            ]
        }
        return render_template('index.html',**context)
    
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    
    模板中的代码:
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>for循环</title>
    </head>
    <body>
        <ul>
            {% for user in users|reverse %}
                <li>{{ user }}</li>
            {% else %}
                <li>沒有任何值</li>
            {% endfor %}
        </ul>
    
        <table>
            <thead>
                <tr>
                    <th>用户名</th>
                    <th>年龄</th>
                    <th>国家</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    {% for key in person.keys() %}
                        <td>{{ key }}</td>
                    {% endfor %}
                </tr>
            </tbody>
        </table>
    
        <table>
            <thead>
                <tr>
                    <th>序号</th>
                    <th>书名</th>
                    <th>作者</th>
                    <th>价格</th>
                    <th>总数</th>
                </tr>
            </thead>
            <tbody>
                {% for book in books %}
                    {% if loop.first %}
                        <tr style="background: red;">
                    {% elif loop.last %}
                        <tr style="background: pink;">
                    {% else %}
                        <tr>
                    {% endif %}
                        <td>{{ loop.index0 }}</td>
                        <td>{{ book.name }}</td>
                        <td>{{ book.author }}</td>
                        <td>{{ book.price }}</td>
                        <td>{{ loop.length }}</td>
                    </tr>
                {% endfor %}
            </tbody>
        </table>
    
        <table border="1">
            <tbody>
                {% for x in range(1,10) %}
                    <tr>
                        {% for y in range(1,10) if y <= x %}
                            <td>{{ y }}*{{ x }}={{ x*y }}</td>
                        {% endfor %}
                    </tr>
                {% endfor %}
            </tbody>
        </table>
    </body>
    </html>
    

    1.5.3 set语句

    在模版中,可以使用set语句来定义变量。示例如下:

    {% set username='python课堂' %}
    <p>用户名:{{ username }}</p>
    

    一旦定义了这个变量,那么在后面的代码中,都可以使用这个变量,就类似于Python的变量定义是一样的。

    1.5.4 with语句

    with语句定义的变量,只能在with语句块中使用,超过了这个代码块,就不能再使用了。示例代码如下:

    {% with classroom = 'python1班' %}
    <p>班级:{{ classroom }}</p>
    {% endwith %}
    

    with语句也不一定要跟一个变量,可以定义一个空的with语句,以后在with块中通过set定义的变量,就只能在这个with块中使用了:

    {% with %}
        {% set classroom = 'python1班' %}
        <p>班级:{{ classroom }}</p>
    {% endwith %}
    

    1.6 宏详解

    1.6.1 宏的基本使用

    模板中的宏跟python中的函数类似,可以传递参数,但是不能有返回值,可以将一些经常用到的代码片段放到宏中,然后把一些不固定的值抽取出来当成一个变量。
    使用宏的时候,参数可以为默认值。相关示例代码如下:

    1. 定义宏:
      {% macro input(name, value='', type='text') %}
      <input type="{{ type }}" name="{{ name }}" value="{{
      value }}">
      {% endmacro %}
      
    2. 使用宏:
      <p>{{ input('username') }}</p>
      <p>{{ input('password', type='password') }}</p>
      

    1.6.2 导入宏

    1. import "宏文件的路径" as xxx
    2. from '宏文件的路径' import 宏的名字 [as xxx]
    3. 宏文件路径,不要以相对路径去寻找,都要以templates作为绝对路径去找。
    4. 如果想要在导入宏的时候,就把当前模版的一些参数传给宏所在的模版,那么就应该在导入的时候使用with context。示例:from 'xxx.html' import input with context
    flask代码:
    from flask import Flask,render_template
    
    app = Flask(__name__)
    app.config['TEMPLATES_AUTO_RELOAD'] = True
    
    @app.route('/')
    def hello_world():
        return render_template('index/index.html',username='xiaoming')
    
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    

    模板文件中的代码:

    {#{% from "macros.html" import input as input_field %}#}
    {% import "macros/macros.html" as macros with context %}
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>python宏</title>
    </head>
    <body>
        <h1>python登录</h1>
        <table>
            <tbody>
                <tr>
                    <td>用户名:</td>
                    <td>{{ macros.input('username') }}</td>
                </tr>
                <tr>
                    <td>密码:</td>
                    <td>{{ macros.input("password",type="password") }}</td>
                </tr>
                <tr>
                    <td></td>
                    <td>{{ macros.input(value="提交",type="submit") }}</td>
                </tr>
            </tbody>
        </table>
        <p>{{ username }}</p>
    </body>
    </html>
    

    宏文件:

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

    1.7 include详解

    1.7.1 include标签

    1. 这个标签相当于是直接将指定的模版中的代码复制粘贴到当前位置。
    2. include标签,如果想要使用父模版中的变量,直接用就可以了,不需要使用with context
    3. include的路径,也是跟import一样,直接从templates根目录下去找,不要以相对路径去找。

    flask代码:

    from flask import Flask,render_template
    
    app = Flask(__name__)
    app.config['TEMPLATES_AUTO_RELOAD'] = True
    
    @app.route('/')
    def hello_world():
        return render_template('index.html',username='xiaoming')
    
    @app.route('/detail/')
    def detail():
        return render_template('course_detail.html')
    
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    

    模板中的文件:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>python课堂</title>
    </head>
    <body>
        {% include "common/header.html" %}
        <div class="content">
            中间的
        </div>
        {% include "common/footer.html" %}
    </body>
    </html>
    

    footer.html中的代码:

    <footer>
        这是底部
    </footer>
    

    header.html中的代码:

    <style>
        .nav ul{
            overflow: hidden;
        }
        .nav ul li{
            float: left;
            margin: 0 20px;
        }
    </style>
    <nav class="nav">
        <ul>
            <li>首页</li>
            <li>课程详情</li>
            <li>视频教程</li>
            <li>关于我们</li>
            <li>{{ username }}</li>
        </ul>
    </nav>
    

    1.8 静态文件

    加载静态文件使用的是url_for函数。然后第一个参数需要为static,第二个参数需要为一个关键字参数filename='路径'。示例:

    {{ url_for("static",filename='xxx') }}
    

    路径查找,要以当前项目的static目录作为根目录。

    模板中的代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>python课堂</title>
        <link rel="stylesheet" href="{{ url_for('static',filename="css/index.css") }}">
        <script src="{{ url_for("static",filename='js/index.js') }}"></script>
    </head>
    <body>
    <img src="{{ url_for("static",filename='imgs/buyudaren.jpg') }}" alt="">
    </body>
    </html>
    

    1.9 模版继承

    1.9.1 为什么需要模版继承

    模版继承可以把一些公用的代码单独抽取出来放到一个父模板中。以后子模板直接继承就可以使用了。这样可以重复性的代码,并且以后修改起来也比较方便。

    1.9.2 模版继承语法

    使用extends语句,来指明继承的父模板。父模板的路径,也是相对于templates文件夹下的绝对路径。示例代码如下:

    {% extends "base.html" %}
    

    1.9.3 block语法

    一般在父模版中,定义一些公共的代码。子模板可能要根据具体的需求实现不同的代码。这时候父模版就应该有能力提供一个接口,让父模板来实现。从而实现具体业务需求的功能。
    在父模板中:

    {% block block的名字 %}
    {% endblock %}
    

    在子模板中:

    {% block block的名字 %}
    子模板中的代码
    {% endblock %}
    

    1.9.4 调用父模版代码block中的代码

    默认情况下,子模板如果实现了父模版定义的block。那么子模板block中的代码就会覆盖掉父模板中的代码。如果想要在子模板中仍然保持父模板中的代码,那么可以使用{{ super() }}来实现。示例如下:
    父模板:

    {% block body_block %}
            <p style="background: red;">这是父模板中的代码</p>
        {% endblock %}
    

    子模板:

    {% block body_block %}
        {{ super() }}
        <p style="background: green;">我是子模板中的代码</p>
    {% endblock %}
    

    1.9.5 调用另外一个block中的代码

    如果想要在另外一个模版中使用其他模版中的代码。那么可以通过{{ self.其他block名字() }}就可以了。示例代码如下:

    {% block title %}
        python课堂首页
    {% endblock %}
    
    {% block body_block %}
        {{ self.title() }}
        <p style="background: green;">我是子模板中的代码</p>
    {% endblock %}
    

    1.9.6 其他注意事项

    1. 子模板中的代码,第一行,应该是extends
    2. 子模板中,如果要实现自己的代码,应该放到block中。如果放到其他地方,那么就不会被渲染。

    相关文章

      网友评论

          本文标题:Flask学习之旅 --- 中级篇

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