flask_flask框架 - CSDN
flask 订阅
Flask是一个使用 Python 编写的轻量级 Web 应用框架。其 WSGI 工具箱采用 Werkzeug ,模板引擎则使用 Jinja2 。Flask使用 BSD 授权。Flask也被称为 “microframework” ,因为它使用简单的核心,用 extension 增加其他功能。Flask没有默认使用的数据库、窗体验证工具。 [1] 展开全文
Flask是一个使用 Python 编写的轻量级 Web 应用框架。其 WSGI 工具箱采用 Werkzeug ,模板引擎则使用 Jinja2 。Flask使用 BSD 授权。Flask也被称为 “microframework” ,因为它使用简单的核心,用 extension 增加其他功能。Flask没有默认使用的数据库、窗体验证工具。 [1]
信息
核心语言
python
授    权
BSD 授权
特    点
使用简单的核心
中文名
Flask
也被称
“microframework”
外文名
Flask
Flask简介
Flask是一个轻量级的可定制框架,使用Python语言编写,较其他同类型框架更为灵活、轻便、安全且容易上手。它可以很好地结合MVC模式进行开发,开发人员分工合作,小型团队在短时间内就可以完成功能丰富的中小型网站或Web服务的实现。另外,Flask还有很强的定制性,用户可以根据自己的需求来添加相应的功能,在保持核心功能简单的同时实现功能的丰富与扩展,其强大的插件库可以让用户实现个性化的网站定制,开发出功能强大的网站。Flask是目前十分流行的web框架,采用Python编程语言来实现相关功能。它被称为微框架(microframework),“微”并不是意味着把整个Web应用放入到一个Python文件,微框架中的“微”是指Flask旨在保持代码简洁且易于扩展,Flask框架的主要特征是核心构成比较简单,但具有很强的扩展性和兼容性,程序员可以使用Python语言快速实现一个网站或Web服务。一般情况下,它不会指定数据库和模板引擎等对象,用户可以根据需要自己选择各种数据库。Flask自身不会提供表单验证功能,在项目实施过程中可以自由配置,从而为应用程序开发提供数据库抽象层基础组件,支持进行表单数据合法性验证、文件上传处理、用户身份认证和数据库集成等功能。Flask主要包括Werkzeug和Jinja2两个核心函数库,它们分别负责业务处理和安全方面的功能,这些基础函数为web项目开发过程提供了丰富的基础组件。Werkzeug库十分强大,功能比较完善,支持URL路由请求集成,一次可以响应多个用户的访问请求;支持Cookie和会话管理,通过身份缓存数据建立长久连接关系,并提高用户访问速度;支持交互式Javascript调试,提高用户体验;可以处理HTTP基本事务,快速响应客户端推送过来的访问请求。Jinja2库支持自动HTML转移功能,能够很好控制外部黑客的脚本攻击。系统运行速度很快,页面加载过程会将源码进行编译形成python字节码,从而实现模板的高效运行;模板继承机制可以对模板内容进行修改和维护,为不同需求的用户提供相应的模板。目前Python的web框架有很多。除了Flask,还有django、Web2py等等。其中Diango是目前Python的框架中使用度最高的。但是Django如同java的EJB(EnterpriseJavaBeansJavaEE服务器端组件模型)多被用于大型网站的开发,但对于大多数的小型网站的开发,使用SSH(Struts+Spring+Hibernat的一个JavaEE集成框架)就可以满足,和其他的轻量级框架相比较,Flask框架有很好的扩展性,这是其他Web框架不可替代的。 [2] 
收起全文
精华内容
参与话题
  • Python全栈(七)Flask框架之3.Flask模板

    千次阅读 多人点赞 2020-04-23 11:11:50
    过滤器将变量处理后再返回,内置过滤器包括abs、escape、length等;还可以自定义过滤器。if判断语句可以用各种比较运算符判断,还可以进行逻辑合并操作;for循环语句可以遍历列表、字典和元组,可以用else语句退出...

    一、Flask模板过滤器

    1.Jinja2模版内置过滤器

    过滤器相当于是一个函数,把当前的变量传入过滤器,过滤器根据自己的功能对变量进行相应的处理,再返回对应的值,并将结果渲染到页面中。
    过滤器是通过管道符号 | 实现的,例如{{ name|length }}返回name的长度。
    Jinja2中内置了许多过滤器,常见的如下:

    abs(value)

    返回一个数值的绝对值。
    测试如下:

    from flask import Flask,render_template
    
    app = Flask(__name__)
    # 修改模板后,自动重新加载
    app.config['TEMPLATE_AUTO_RELOAD'] = True
    
    @app.route('/')
    def index():
        context = {
            'username':'小度',
            'age':-18
        }
        return render_template('index.html', **context)
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    

    模板index.html代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        <h1>首页</h1>
        <h2>{{username}}</h2>
        <h2>{{age | abs}}</h2>
    </body>
    </html>
    

    显示:
    flask template filter abs
    显然,负数被转换成其绝对值。
    因为模板文件修改后不一定会重新加载,所以需要配置参数app.config['TEMPLATE_AUTO_RELOAD'] = True,当模板文件修改后就会自动更新。

    default(value, default_value, boolean=false)

    如果当前变量没有值,则会使用参数中的值来代替。
    例如name|default('Corley')中,如果name未在视图函数中定义,就会使用Corley来替代。
    参数boolean=false默认是在只有这个变量为undefined的时候才会使用default中的值,如果想使用python的形式判断是否为false,则可以传递boolean=true,或者使用or来替换。
    测试如下:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        <h1>首页</h1>
        <h2>{{username}}</h2>
        <h2>{{age | abs}}</h2>
        <h2>{{name | default('这个人很懒,并没有留下签名...')}}</h2>
    
    </body>
    </html>
    

    显示:
    flask template filter default
    因为在Python代码中并未定义和传name参数,所以渲染给定的默认值;
    如果在Python中定义了参数,会渲染给定的参数对应的值。
    这种情况下只有当name参数未定义的时候才会渲染default()的参数值,否则都会渲染name参数对应的值。
    也可以在default()传入参数boolean=true,此时会根据Python的规则来判断name的布尔值是True还是False,如果是True则渲染name的值,如果是False则渲染default()中给定的值,测试如下:

    from flask import Flask,render_template
    
    app = Flask(__name__)
    # 修改模板后,自动重新加载
    app.config['TEMPLATE_AUTO_RELOAD'] = True
    
    @app.route('/')
    def index():
        context = {
            'username':'小度',
            'age':-18,
            'name1':None,
            'name2':'Corley',
        }
        return render_template('index.html', **context)
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    

    模板代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        <h1>首页</h1>
        <h2>{{username}}</h2>
        <h2>{{age | abs}}</h2>
        <h2>{{name1 | default('这个人很懒,并没有留下签名', boolean=true)}}</h2>
        <h2>{{name2 | default('这个人很懒,并没有留下签名', boolean=true)}}</h2>
    
    </body>
    </html>
    

    显示:
    flask template filter default boolean true
    显然,此时default()中加了参数boolean=true,因为name1为None,其在Python中的布尔值为False,所以会渲染default()中给定的值,而name2为一个非空字符串,对应的布尔值为True,所以会渲染name2对应的值。

    escape(value)

    转义字符,会将<、>等特殊符号转义成HTML中的符号,使其成为普通字符串,避免渲染成HTML元素。
    例如:content|escapecontent|e
    escape测试如下:

    from flask import Flask,render_template
    
    app = Flask(__name__)
    # 修改模板后,自动重新加载
    app.config['TEMPLATE_AUTO_RELOAD'] = True
    
    @app.route('/')
    def index():
        context = {
            'username':'小度',
            'age':-18,
            'name':'Corley',
            'script':'<script>alert("hello")</script>'
        }
        return render_template('index.html', **context)
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    

    模板代码为:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        <h1>首页</h1>
        <h2>{{username}}</h2>
        <h2>{{age | abs}}</h2>
        <h2>{{name | default('这个人很懒,并没有留下签名', boolean=true)}}</h2>
        <h2>{{script | escape}}</h2>
    
    </body>
    </html>
    

    显示:
    flask template filter escape
    显然,此时Flask模板渲染时并未将script变量对应的值渲染为Javascript代码,而是当作普通的字符串。
    查看网页源代码如下:
    flask template filter escape source
    显然,在渲染时将特殊字符自动转义。
    在flask中,默认是开启了自动转义的功能的,所以不使用escape过滤器也是可以的。
    这是Jinja2的一种安全机制,可以防止对页面进行篡改、注入等操作。

    safe(value)

    如果开启了全局转义,那么safe过滤器会将变量关掉转义。
    例如:content_html|safe
    上例中,如果要取消转义,可以关闭escape过滤器,测试如下:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        <h1>首页</h1>
        <h2>{{username}}</h2>
        <h2>{{age | abs}}</h2>
        <h2>{{name | default('这个人很懒,并没有留下签名', boolean=true)}}</h2>
        {% autoescape off %}
        <h2>{{script}}</h2>
        {% endautoescape %}
    </body>
    </html>
    

    显示:
    flask template filter escape off
    同时还可以使用safe过滤器达到同样的效果,如下:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        <h1>首页</h1>
        <h2>{{username}}</h2>
        <h2>{{age | abs}}</h2>
        <h2>{{name | default('这个人很懒,并没有留下签名', boolean=true)}}</h2>
        <h2>{{script | safe}}</h2>
    </body>
    </html>
    

    效果与之前相同。
    很明显,可以使用safe过滤器渲染HTML代码,如下:

    from flask import Flask,render_template
    
    app = Flask(__name__)
    # 修改模板后,自动重新加载
    app.config['TEMPLATE_AUTO_RELOAD'] = True
    
    @app.route('/')
    def index():
        context = {
            'username':'小度',
            'age':-18,
            'name':'Corley',
            'h3':'<h3>Flask</h3>'
        }
        return render_template('index.html', **context)
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    

    模板代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        <h1>首页</h1>
        <h2>{{username}}</h2>
        <h2>{{age | abs}}</h2>
        <h2>{{name | default('这个人很懒,并没有留下签名', boolean=true)}}</h2>
        <h2>{{h3 | safe}}</h2>
    </body>
    </html>
    

    显示:
    flask template filter escape safe html

    first(value)、last(value)和length(value)

    first(value)返回一个序列的第一个元素,如names|first
    last(value)返回一个序列的最后一个元素,如names|last
    length(value)返回一个序列或者字典的长度,如names|length

    测试如下:

    from flask import Flask,render_template
    
    app = Flask(__name__)
    # 修改模板后,自动重新加载
    app.config['TEMPLATE_AUTO_RELOAD'] = True
    
    @app.route('/')
    def index():
        context = {
            'username':'小度',
            'age':-18,
            'name':'Corley',
            'h3':'<h3>Flask</h3>',
            'books':['Python', 'Java', 'PHP']
        }
        return render_template('index.html', **context)
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    

    模板代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        <h1>首页</h1>
        <h2>{{username}}</h2>
        <h2>{{age | abs}}</h2>
        <h2>{{name | default('这个人很懒,并没有留下签名', boolean=true)}}</h2>
        <h2>{{h3 | safe}}</h2>
        <h2>{{books | first}}</h2>
        <h2>{{books | last}}</h2>
        <h2>{{books | length}}</h2>
    </body>
    </html>
    

    显示:
    flask template filter first
    还可以嵌套过滤,求列表中某个字符串的长度:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        <h1>首页</h1>
        <h2>{{username}}</h2>
        <h2>{{age | abs}}</h2>
        <h2>{{name | default('这个人很懒,并没有留下签名', boolean=true)}}</h2>
        <h2>{{h3 | safe}}</h2>
        <h2>{{books | first}}</h2>
        <h2>{{books | last}}</h2>
        <h2>{{books | first | length}}</h2>
    </body>
    </html>
    

    显示:
    flask template filter first length

    lower(value)、upper(value)

    分别将字符串转换为小写、大写。
    测试如下:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        <h1>首页</h1>
        <h2>{{username}}</h2>
        <h2>{{age | abs}}</h2>
        <h2>{{name | default('这个人很懒,并没有留下签名', boolean=true)}}</h2>
        <h2>{{name | lower}}</h2>
        <h2>{{name | upper}}</h2>
    </body>
    </html>
    

    显示:
    flask template filter lower upper

    replace(value, old, new)

    将字符串中old字串替换为new字符串。
    测试如下:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        <h1>首页</h1>
        <h2>{{username}}</h2>
        <h2>{{age | abs}}</h2>
        <h2>{{name | default('这个人很懒,并没有留下签名', boolean=true)}}</h2>
        <h2>{{name | replace('ley','lin')}}</h2>
    
    </body>
    </html>
    

    显示:
    flask template filter replace

    truncate(value,length=255,killwords=False)

    截取length长度的字符串。
    测试如下:

    from flask import Flask,render_template
    
    app = Flask(__name__)
    # 修改模板后,自动重新加载
    app.config['TEMPLATE_AUTO_RELOAD'] = True
    
    @app.route('/')
    def index():
        context = {
            'username':'小度',
            'age':-18,
            'name':'Corley',
            'title':'很“聪明”的小夜灯!靠近时自动亮起、冷暖光可调、还可随身携带'
        }
        return render_template('index.html', **context)
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    

    模板代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        <h1>首页</h1>
        <h2>{{username}}</h2>
        <h2>{{age | abs}}</h2>
        <h2>{{name | default('这个人很懒,并没有留下签名', boolean=true)}}</h2>
        <h2>{{title | truncate(length=15)}}</h2>
    
    </body>
    </html>
    

    显示:
    flask template filter truncate
    truncate()过滤器一般用于显示较长的标题省略末尾一部分内容;
    注意,truncate()传入的length必须大于等于3,因为3表示省略号3个点,大于3时剩下的显示变量具体内容,小于3时渲染会报错。

    striptags(value)

    删除字符串中所有的HTML标签,如果出现多个空格,将替换成一个空格。
    测试如下:

    from flask import Flask,render_template
    
    app = Flask(__name__)
    # 修改模板后,自动重新加载
    app.config['TEMPLATE_AUTO_RELOAD'] = True
    
    @app.route('/')
    def index():
        context = {
            'username':'小度',
            'age':-18,
            'name':'Corley',
            'script':'<script>alert("hello")</script>'
        }
        return render_template('index.html', **context)
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    

    模板代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        <h1>首页</h1>
        <h2>{{username}}</h2>
        <h2>{{age | abs}}</h2>
        <h2>{{name | default('这个人很懒,并没有留下签名', boolean=true)}}</h2>
        <h2>{{script | striptags}}</h2>
    
    </body>
    </html>
    

    显示:
    flask template filter striptags
    显然,标签都被过滤掉,只显示内部内容。

    wordcount(s)

    计算一个长字符串中单词的个数。
    测试如下:

    from flask import Flask,render_template
    
    app = Flask(__name__)
    # 修改模板后,自动重新加载
    app.config['TEMPLATE_AUTO_RELOAD'] = True
    
    @app.route('/')
    def index():
        context = {
            'username':'小度',
            'age':-18,
            'name':'Corley',
            'hello':'hello world',
            'address':'中国 四川省',
        }
        return render_template('index.html', **context)
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    

    模板代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        <h1>首页</h1>
        <h2>{{username}}</h2>
        <h2>{{age | abs}}</h2>
        <h2>{{name | default('这个人很懒,并没有留下签名', boolean=true)}}</h2>
        <h2>{{hello}}</h2>
        <h2>{{hello | wordcount}}</h2>
        <h2>{{address}}</h2>
        <h2>{{address | wordcount}}</h2>
    
    </body>
    </html>
    

    显示:
    flask template filter wordcount
    易知,wordcount是根据空格来计数的,对英文、中文都能识别。

    其他过滤器

    • format(value,*arags,**kwargs)
      格式化字符串。
      例如{{ "%s" - "%s"|format('Hello',"World") }}将输出Hello - World
    • join(value,d='xx')
      将一个序列用d参数的值拼接成字符串。
    • int(value)
      将值转换为int类型。
    • float(value)
      将值转换为float类型。
    • string(value)
      将变量转换成字符串。
    • trim(value)
      截取字符串前面和后面的空白字符。

    2.自定义过滤器

    除了使用Jinja2模板内置的过滤器,还可以自定义过滤器。

    简单示例如下:

    from flask import Flask,render_template
    
    app = Flask(__name__)
    # 修改模板后,自动重新加载
    app.config['TEMPLATE_AUTO_RELOAD'] = True
    
    @app.route('/')
    def index():
        context = {
            'username':'小度',
            'age':-18,
            'name':'Corley',
            'hello':'hello world',
            'address':'中国 四川省',
        }
        return render_template('index.html', **context)
    
    @app.template_filter('my_replace')
    def cut(value):
        return value.replace(' ', '*')
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    

    模板代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        <h1>首页</h1>
        <h2>{{username}}</h2>
        <h2>{{age | abs}}</h2>
        <h2>{{name | default('这个人很懒,并没有留下签名', boolean=true)}}</h2>
        <h2>{{hello}}</h2>
        <h2>{{hello | my_replace}}</h2>
    
    </body>
    </html>
    

    显示:
    flask template filter custom
    显然,空格被替换为*。
    自定义过滤器相当于定义函数,同时需要用装饰器@app.template_filter('my_replace')来装饰,传入的参数即为过滤器名。

    再定义一个过滤器,实现以下功能:
    小于1分钟,显示刚刚;
    大于1分钟小于1小时,显示XX分钟前;
    大于1小时小于24小时,显示XX小时前;

    测试如下:

    from datetime import datetime
    
    from flask import Flask,render_template
    
    
    app = Flask(__name__)
    # 修改模板后,自动重新加载
    app.config['TEMPLATE_AUTO_RELOAD'] = True
    
    @app.route('/')
    def index():
        context = {
            'username':'小度',
            'age':-18,
            'name':'Corley',
            'hello':'hello world',
            'release_time1':datetime(2020, 4, 11, 9, 30, 0),
            'release_time2':datetime(2020, 4, 11, 12, 30, 0)
        }
        return render_template('index.html', **context)
    
    @app.template_filter('time_handler')
    def handle_time(time):
        if isinstance(time, datetime):
            now = datetime.now()
            time_delta = (now - time).total_seconds()
            if time_delta < 60:
                return '刚刚'
            elif time_delta >= 60 and time_delta < 60 * 60:
                return '%d分钟之前' % (time_delta // 60)
            elif time_delta >= 60 * 60 and time_delta < 60 * 60 * 24:
                return '%d小时之前' % (time_delta / 60 // 60)
    
        else:
            return time
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    

    模板代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        <h1>首页</h1>
        <h2>{{username}}</h2>
        <h2>{{age | abs}}</h2>
        <h2>{{name | default('这个人很懒,并没有留下签名', boolean=true)}}</h2>
        <h2>{{hello}}</h2>
        <h2>文章发表时间:{{release_time1}}</h2>
        <h2>文章发表时间:{{release_time1 | time_handler}}</h2>
        <h2>文章发表时间:{{release_time2}}</h2>
        <h2>文章发表时间:{{release_time2 | time_handler}}</h2>
    </body>
    </html>
    

    显示:
    flask template filter custom timedelta

    二、模板控制语句

    所有的控制语句都是放在{% ... %}中,并且有一个语句{% endxxx %}来结束,Jinja中常用的控制语句有iffor...in...等。

    1.if判断语句

    if语句和Python中类似,可以使用>、<、<=、>=、==和!=来进行判断,也可以通过and、or、not、()来进行逻辑合并操作。
    例如:

    {% if weather == 'sunny' %}
    	出去玩
    {% elif weather == 'rainy'  %}
    	在家玩
    {% else %}
    	睡大觉
    {% endif %}
    

    if判断语句测试如下:

    from datetime import datetime
    from flask import Flask, render_template
    
    
    app = Flask(__name__)
    # 修改模板后,自动重新加载
    app.config['TEMPLATE_AUTO_RELOAD'] = True
    
    @app.route('/')
    def index():
        context = {
            'username':'Corley',
        }
        return render_template('index.html', **context)
    
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    

    模板代码为:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        <h1>首页</h1>
        <h2>{{username}}</h2>
        {% if username == 'Corlin' %}
        <p>{{username}}</p>
        {% else %}
        <p>Current user is not Corlin!!!</p>
        {% endif %}
    </body>
    </html>
    

    显示:
    flask control if

    2.for循环语句

    for循环可以遍历任何一个序列包括列表、字典、元组,并且可以进行反向遍历。
    for循环遍历列表和字典测试:

    from datetime import datetime
    from flask import Flask,render_template
    
    
    app = Flask(__name__)
    # 修改模板后,自动重新加载
    app.config['TEMPLATE_AUTO_RELOAD'] = True
    
    @app.route('/')
    def index():
        context = {
            'username': 'Corley',
            'books': ['Python', 'Java', 'PHP'],
            'info': {
                'name': '小度',
                'age': 18,
                'major': 'EC'
            }
        }
        return render_template('index.html', **context)
    
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    

    模板代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        <h1>首页</h1>
        <h2>{{username}}</h2>
        {% for book in books %}
            <p>{{book}}</p>
        {% endfor %}
        <hr>
        {% for key, value in info.items() %}
            <p>{{key}} : {{value}}</p>
        {% endfor %}
    </body>
    </html>
    

    显示:
    flask control for
    for循环还可以在序列为空时使用else语句退出循环,测试如下:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        <h1>首页</h1>
        <h2>{{username}}</h2>
        {% for subject in subjects %}
            <p>{{subject}}</p>
        {% else %}
            <em>No Subjects Found</em>
        {% endfor %}
    </body>
    </html>
    

    视图函数中不变,显示:
    flask template for else
    并且Jinja中的for循环还包含变量用来获取当前的遍历状态,常见变量如下:

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

    获取当前遍历状态测试:

    from datetime import datetime
    
    from flask import Flask,render_template
    
    
    app = Flask(__name__)
    # 修改模板后,自动重新加载
    app.config['TEMPLATE_AUTO_RELOAD'] = True
    
    @app.route('/')
    def index():
        context = {
            'username': 'Corley',
            'books': ['Python', 'Java', 'PHP'],
        }
        return render_template('index.html', **context)
    
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    

    模板代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        <h1>首页</h1>
        <h2>{{username}}</h2>
        {% for book in books %}
            <p>{{loop.index}}--:--{{book}}</p>
        {% endfor %}
        <hr>
        {% for book in books %}
            <p>{{loop.index0}}--:--{{book}}</p>
        {% endfor %}
        <hr>
        {% for book in books %}
            <p>{{loop.first}}--:--{{book}}</p>
        {% endfor %}
    </body>
    </html>
    

    显示:
    flask control for state
    注意:
    不能用continue和break表达式来控制循环的执行,它们在模板中控制循环是无效的。

    三、宏和import语句

    1.宏的定义和使用

    模板中的宏跟python中的函数类似,可以传递参数,但是不能有返回值,可以将一些经常用到的代码片段放到宏中,然后把一些不固定的值抽取出来作为参数。
    宏定义的语法:

    {% macro 宏名字(参数) %}
        xxx
    {% endmacro %}
    

    进行测试:

    from flask import Flask,render_template
    
    
    app = Flask(__name__)
    # 修改模板后,自动重新加载
    app.config['TEMPLATE_AUTO_RELOAD'] = True
    
    @app.route('/')
    def index():
        context = {
            'username': 'Corley',
            'books': ['Python', 'Java', 'PHP'],
        }
        return render_template('index.html', **context)
    
    @app.route('/macro')
    def macro():
        return render_template('macro.html')
    
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    

    新建模板macro.html如下:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        {% macro input(name, value='', type='text') %}
            <input name="{{name}}" value="{{value}}" type="{{type}}">
        {% endmacro %}
        <table>
            <tr>
                <td>用户名:</td>
                <td>{{input('username')}}</td>
            </tr>
            <tr>
                <td>密码:</td>
                <td>{{input('password', type='password')}}</td>
            </tr>
            <tr>
                <td>提交</td>
                <td>{{input(value='提交', type='submit')}}</td>
            </tr>
        </table>
    </body>
    </html>
    

    定义的宏抽取出了一个input标签,指定了一些默认参数,那么以后创建input标签的时候,就可以通过宏快速的创建。
    显示:
    flask macro test
    显然,{% macro input(name, value='', type='text') %}定义了一个类似于函数的结构,传入参数就可以生成对应的模板直接使用。

    2.import语句

    在真实的开发中,会将一些常用的宏单独放在一个文件中,在需要使用的时候,再从这个文件中进行导入。
    import语句的用法跟python中的import类似,可以直接import...as...,也可以from...import...或者from...import...as...

    macro.html文件用于专门保存宏:

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

    测试如下:

    from flask import Flask,render_template
    
    
    app = Flask(__name__)
    # 修改模板后,自动重新加载
    app.config['TEMPLATE_AUTO_RELOAD'] = True
    
    @app.route('/')
    def index():
        context = {
            'username': 'Corley',
            'books': ['Python', 'Java', 'PHP'],
        }
        return render_template('index.html', **context)
    
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    

    模板代码为:

    {% import 'macro.html' as macro %}
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        <h1>首页</h1>
        <h2>{{username}}</h2>
        {% for book in books %}
            <p>{{loop.index}}--:--{{book}}</p>
        {% endfor %}
        <hr>
        请输入:{{ macro.input('username') }}
    </body>
    </html>
    

    显示:
    flask macro import
    显然,访问主页时,渲染导入的宏生成输入框。
    也可以用from xxx import xxx语句,如下:

    {% from 'macro.html' import input %}
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        <h1>首页</h1>
        <h2>{{username}}</h2>
        {% for book in books %}
            <p>{{loop.index}}--:--{{book}}</p>
        {% endfor %}
        <hr>
        <p>请输入用户名:{{ input('username') }}</p>
        <p>请输入密码:{{ input('password', type='password') }}</p>
    </body>
    </html>
    

    显示:
    flask macro import password
    用这种导入方式也可以重命名,如from 'macro.html' import input as inputbox
    需要注意,导入模板并不会把当前上下文中的变量添加到被导入的模板中,如果需要导入一个需要访问当前上下文变量的宏,有两种可能的方法:

    • 显式地传入请求或请求对象的属性作为宏的参数
    • 与上下文一起(with context)导入宏

    在导入宏时用with context可以把当前模板的一些参数传给宏所在的模板。
    测试如下:

    {% from 'macro.html' import input with context %}
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        <h1>首页</h1>
        <h2>{{username}}</h2>
        {% for book in books %}
            <p>{{loop.index}}--:--{{book}}</p>
        {% endfor %}
        <hr>
        <p>请输入用户名:{{ input('username') }}</p>
        <p>请输入密码:{{ input('password', type='password') }}</p>
    </body>
    </html>
    

    宏文件macro.html中修改:

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

    显示:
    flask macro import pass params
    要传递参数,需要在导入宏的时候加上with context修饰,这样可以将视图函数中定义的参数传给宏,再渲染到模板中。

    四、include和set语句

    1.include语句

    include语句可以把一个模板引入到另外一个模板中,类似于把一个模板的代码copy到另外一个模板的指定位置。
    测试过程如下:
    先在templates目录下创建common文件夹,再在common文件夹下创建header.html和footer.html文件
    header.html文件如下:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Header</title>
    </head>
    <body>
        <ul>
            <li>军事</li>
            <li>科技</li>
            <li>财经</li>
            <li>娱乐</li>
        </ul>
    </body>
    </html>
    

    footer.html文件如下:

    <footer>
        这是页面底部
    </footer>
    

    在templates目录下创建article.html文件,如下:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Article</title>
    </head>
    <body>
        {% include 'common/header.html' %}
        <ul>
            <li>第一篇文章</li>
            <li>第二篇文章</li>
            <li>第三篇文章</li>
        </ul>
        {% include 'common/footer.html' %}
    </body>
    </html>
    

    index.html文件如下:

    {% from 'macro.html' import input with context %}
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        {% include 'common/header.html' %}
        <h2>{{username}}</h2>
        {% for book in books %}
            <p>{{loop.index}}--:--{{book}}</p>
        {% endfor %}
        <hr>
        <p>请输入用户名:{{ input('username') }}</p>
        <p>请输入密码:{{ input('password', type='password') }}</p>
        {% include 'common/footer.html' %}
    </body>
    </html>
    

    视图函数文件如下:

    from flask import Flask,render_template
    
    
    app = Flask(__name__)
    # 修改模板后,自动重新加载
    app.config['TEMPLATE_AUTO_RELOAD'] = True
    
    @app.route('/')
    def index():
        context = {
            'username': 'Corley',
            'books': ['Python', 'Java', 'PHP'],
        }
        return render_template('index.html', **context)
    
    @app.route('/article/')
    def article():
        return render_template('article.html')
    
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    

    显示:
    flask include

    2.set赋值语句

    一般情况下是在视图函数中定义变量、在模板中引用,但是也可以在模板中添加变量,这时候会用到set赋值语句。
    测试如下:

    from flask import Flask, render_template
    
    app = Flask(__name__)
    # 修改模板后,自动重新加载
    app.config['TEMPLATE_AUTO_RELOAD'] = True
    
    
    @app.route('/')
    def index():
        context = {
            'username': 'Corley',
            'books': ['Python', 'Java', 'PHP'],
            'name': '小爱'
        }
        return render_template('index.html', **context)
    
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    

    模板代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        {%  set name='小度' %}
        <h2>{{username}}</h2>
        <p>{{  name  }}</p>
    </body>
    </html>
    

    显示:
    flask set global
    显然,此时设置的是全局变量,不需要结束标签。
    视图函数中未定义name变量时,模板可以渲染set定义的name变量,视图函数中定义了name变量时会覆盖视图函数中的值。
    通过set定义全局变量一次只能定义一个,并且赋值语句创建的变量在其之后都是有效的。
    如果不想让一个变量影响到全局环境,可以使用with语句来创建一个内部的作用域,将set语句放在其中,这样创建的变量只在with代码块中才有效,即相当于创建了一个局部变量
    测试如下:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        {%  set name='小度' %}
        <h2>{{username}}</h2>
        <p>{{  name  }}</p>
        {% with  %}
            {%  set name='天猫精灵' %}
            <p>{{ name }}</p>
        {% endwith %}
    
    </body>
    </html>
    

    显示:
    flask set local
    显然,在with语句内部定义的局部变量,不会受外部的影响。
    with语句也可以直接定义变量,如下:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        {%  set name='小度' %}
        <h2>{{username}}</h2>
        <p>{{  name  }}</p>
        {% with name='天猫精灵' %}
            <p>{{ name }}</p>
        {% endwith %}
    
    </body>
    </html>
    

    这与前者的效果相同,一旦超出with代码块,在该代码块中定义的变量就失效了。
    也可以定义字典和列表:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Flask</title>
    </head>
    <body>
        {%  set name='小度' %}
        <h2>{{username}}</h2>
        <p>{{  name  }}</p>
        {% with name='天猫精灵' %}
            <p>{{ name }}</p>
        {% endwith %}
    
        {% with dict = {'name':'Google Home', 'country':'USA'} %}
            <p>{{ dict.name }}</p>
            <p>{{ dict.country }}</p>
        {% endwith %}
    
        {% with list = ['小度', '小爱', '天猫精灵']  %}
            {% for item in list %}
                <p>{{ item }}</p>
            {% endfor %}
        {% endwith %}
    </body>
    </html>
    

    显示:
    flask set list dict

    展开全文
  • Flask 快速入门

    万次阅读 多人点赞 2017-04-13 02:20:04
    Flask是一个Python编写的Web 微框架,让我们可以使用Python语言快速实现一个网站或Web服务。本文参考自Flask官方文档,大部分代码引用自官方文档。安装Flask首先我们来安装Flask。最简单的办法就是使用pip。pip ...

    Flask标志

    Flask是一个Python编写的Web 微框架,让我们可以使用Python语言快速实现一个网站或Web服务。本文参考自Flask官方文档,大部分代码引用自官方文档。

    安装Flask

    首先我们来安装Flask。最简单的办法就是使用pip。

    pip install flask

    然后打开一个Python文件,输入下面的内容并运行该文件。然后访问localhost:5000,我们应当可以看到浏览器上输出了Hello Flask!

    from flask import Flask
    
    app = Flask(__name__)
    
    
    @app.route('/')
    def hello_world():
        return 'Hello Flask!'
    
    
    if __name__ == '__main__':
        app.run()
    

    快速开始

    调试模式

    我们修改代码中的输出,然后查看浏览器上是否有变化。如果你照做的话,可以看到什么变化都没有。其实Flask内置了调试模式,可以自动重载代码并显示调试信息。这需要我们开启调试模式,方法很简单,设置FLASK_DEBUG环境变量,并将值设置为1

    然后再次运行程序,会看到有这样的输出。这时候如果再次修改代码,会发现这次Flask会自动重启。

     * Restarting with stat
     * Debugger is active!
     * Debugger PIN: 157-063-180
     * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

    路由

    在上面的例子里可以看到路由的使用。如果了解Spring Web MVC的话,应该对路由很熟悉。路由通过使用Flask的app.route装饰器来设置,这类似Java的注解。

    @app.route('/')
    def index():
        return 'Index Page'
    
    @app.route('/hello')
    def hello():
        return 'Hello, World'

    路径变量

    如果希望获取/article/1这样的路径参数,就需要使用路径变量。路径变量的语法是/path/<converter:varname>。在路径变量前还可以使用可选的转换器,有以下几种转换器。

    转换器 作用
    string 默认选项,接受除了斜杠之外的字符串
    int 接受整数
    float 接受浮点数
    path 和string类似,不过可以接受带斜杠的字符串
    any 匹配任何一种转换器
    uuid 接受UUID字符串

    下面是Flask官方的例子。

    @app.route('/user/<username>')
    def show_user_profile(username):
        # show the user profile for that user
        return 'User %s' % username
    
    @app.route('/post/<int:post_id>')
    def show_post(post_id):
        # show the post with the given id, the id is an integer
        return 'Post %d' % post_id

    构造URL

    在Web程序中常常需要获取某个页面的URL,在Flask中需要使用url_for('方法名')来构造对应方法的URL。下面是Flask官方的例子。

    >>> from flask import Flask, url_for
    >>> app = Flask(__name__)
    >>> @app.route('/')
    ... def index(): pass
    ...
    >>> @app.route('/login')
    ... def login(): pass
    ...
    >>> @app.route('/user/<username>')
    ... def profile(username): pass
    ...
    >>> with app.test_request_context():
    ...  print url_for('index')
    ...  print url_for('login')
    ...  print url_for('login', next='/')
    ...  print url_for('profile', username='John Doe')
    ...
    /
    /login
    /login?next=/
    /user/John%20Doe

    HTTP方法

    如果需要处理具体的HTTP方法,在Flask中也很容易,使用route装饰器的methods参数设置即可。

    from flask import request
    
    @app.route('/login', methods=['GET', 'POST'])
    def login():
        if request.method == 'POST':
            do_the_login()
        else:
            show_the_login_form()

    静态文件

    Web程序中常常需要处理静态文件,在Flask中需要使用url_for函数并指定static端点名和文件名。在下面的例子中,实际的文件应放在static/文件夹下。

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

    模板生成

    Flask默认使用Jinja2作为模板,Flask会自动配置Jinja 模板,所以我们不需要其他配置了。默认情况下,模板文件需要放在templates文件夹下。

    使用 Jinja 模板,只需要使用render_template函数并传入模板文件名和参数名即可。

    from flask import render_template
    
    @app.route('/hello/')
    @app.route('/hello/<name>')
    def hello(name=None):
        return render_template('hello.html', name=name)

    相应的模板文件如下。

    <!doctype html>
    <title>Hello from Flask</title>
    {% if name %}
      <h1>Hello {{ name }}!</h1>
    {% else %}
      <h1>Hello, World!</h1>
    {% endif %}

    日志输出

    Flask 为我们预配置了一个 Logger,我们可以直接在程序中使用。这个Logger是一个标准的Python Logger,所以我们可以向标准Logger那样配置它,详情可以参考官方文档或者我的文章Python 日志输出

    app.logger.debug('A value for debugging')
    app.logger.warning('A warning occurred (%d apples)', 42)
    app.logger.error('An error occurred')

    处理请求

    在 Flask 中获取请求参数需要使用request等几个全局对象,但是这几个全局对象比较特殊,它们是 Context Locals ,其实就是 Web 上下文中局部变量的代理。虽然我们在程序中使用的是全局变量,但是对于每个请求作用域,它们都是互不相同的变量。理解了这一点,后面就非常简单了。

    Request 对象

    Request 对象是一个全局对象,利用它的属性和方法,我们可以方便的获取从页面传递过来的参数。

    method属性会返回HTTP方法的类似,例如postgetform属性是一个字典,如果数据是POST类型的表单,就可以从form属性中获取。下面是 Flask 官方的例子,演示了 Request 对象的methodform属性。

    from flask import request
    
    @app.route('/login', methods=['POST', 'GET'])
    def login():
        error = None
        if request.method == 'POST':
            if valid_login(request.form['username'],
                           request.form['password']):
                return log_the_user_in(request.form['username'])
            else:
                error = 'Invalid username/password'
        # the code below is executed if the request method
        # was GET or the credentials were invalid
        return render_template('login.html', error=error)

    如果数据是由GET方法传送过来的,可以使用args属性获取,这个属性也是一个字典。

    searchword = request.args.get('key', '')

    文件上传

    利用Flask也可以方便的获取表单中上传的文件,只需要利用 request 的files属性即可,这也是一个字典,包含了被上传的文件。如果想获取上传的文件名,可以使用filename属性,不过需要注意这个属性可以被客户端更改,所以并不可靠。更好的办法是利用werkzeug提供的secure_filename方法来获取安全的文件名。

    from flask import request
    from werkzeug.utils import secure_filename
    
    @app.route('/upload', methods=['GET', 'POST'])
    def upload_file():
        if request.method == 'POST':
            f = request.files['the_file']
            f.save('/var/www/uploads/' + secure_filename(f.filename))

    Cookies

    Flask也可以方便的处理Cookie。使用方法很简单,直接看官方的例子就行了。下面的例子是如何获取cookie。

    from flask import request
    
    @app.route('/')
    def index():
        username = request.cookies.get('username')
        # 使用 cookies.get(key) 代替 cookies[key] 避免
        # 得到 KeyError 如果cookie不存在

    如果需要发送cookie给客户端,参考下面的例子。

    from flask import make_response
    
    @app.route('/')
    def index():
        resp = make_response(render_template(...))
        resp.set_cookie('username', 'the username')
        return resp

    重定向和错误

    redirectabort函数用于重定向和返回错误页面。

    from flask import abort, redirect, url_for
    
    @app.route('/')
    def index():
        return redirect(url_for('login'))
    
    @app.route('/login')
    def login():
        abort(401)
        this_is_never_executed()

    默认的错误页面是一个空页面,如果需要自定义错误页面,可以使用errorhandler装饰器。

    from flask import render_template
    
    @app.errorhandler(404)
    def page_not_found(error):
        return render_template('page_not_found.html'), 404

    响应处理

    默认情况下,Flask会根据函数的返回值自动决定如何处理响应:如果返回值是响应对象,则直接传递给客户端;如果返回值是字符串,那么就会将字符串转换为合适的响应对象。我们也可以自己决定如何设置响应对象,方法也很简单,使用make_response函数即可。

    @app.errorhandler(404)
    def not_found(error):
        resp = make_response(render_template('error.html'), 404)
        resp.headers['X-Something'] = 'A value'
        return resp

    Sessions

    我们可以使用全局对象session来管理用户会话。Sesison 是建立在 Cookie 技术上的,不过在 Flask 中,我们还可以为 Session 指定密钥,这样存储在 Cookie 中的信息就会被加密,从而更加安全。直接看 Flask 官方的例子吧。

    from flask import Flask, session, redirect, url_for, escape, request
    
    app = Flask(__name__)
    
    @app.route('/')
    def index():
        if 'username' in session:
            return 'Logged in as %s' % escape(session['username'])
        return 'You are not logged in'
    
    @app.route('/login', methods=['GET', 'POST'])
    def login():
        if request.method == 'POST':
            session['username'] = request.form['username']
            return redirect(url_for('index'))
        return '''
            <form method="post">
                <p><input type=text name=username>
                <p><input type=submit value=Login>
            </form>
        '''
    
    @app.route('/logout')
    def logout():
        # remove the username from the session if it's there
        session.pop('username', None)
        return redirect(url_for('index'))
    
    # set the secret key.  keep this really secret:
    app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'

    模板简介

    这里简单的介绍一下Jinja 模板的使用方法,详细资料直接看原文档吧。

    模板标签

    其实Jinja 模板和其他语言和框架的模板类似,反正都是通过某种语法将HTML文件中的特定元素替换为实际的值。如果使用过JSP、Thymeleaf 等模板,应该可以非常容易的学会使用 Jinja模板。

    其实从上面的例子中我们应该可以看到Jinja 模板的基本语法了。代码块需要包含在{% %}块中,例如下面的代码。

    {% extends 'layout.html' %}
    {% block title %}主页{% endblock %}
    {% block body %}
    
        <div class="jumbotron">
            <h1>主页</h1>
        </div>
    
    {% endblock %}

    双大括号中的内容不会被转义,所有内容都会原样输出,它常常和其他辅助函数一起使用。下面是一个例子。

    <a class="navbar-brand" href={{ url_for('index') }}>Flask小例子</a>

    继承

    模板可以继承其他模板,我们可以将布局设置为父模板,让其他模板继承,这样可以非常方便的控制整个程序的外观。

    例如这里有一个layout.html模板,它是整个程序的布局文件。

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>{% block title %}{% endblock %}</title>
        <link rel="stylesheet" href="{{ url_for('static',filename='css/bootstrap.css') }}"/>
        <link rel="stylesheet" href="{{ url_for('static',filename='css/bootstrap-theme.css') }}"/>
    
    </head>
    <body>
    
    <div class="container body-content">
        {% block body %}{% endblock %}
    </div>
    
    <div class="container footer">
        <hr>
        <p>这是页脚</p>
    </div>
    
    <script src="{{ url_for('static',filename='js/jquery.js') }}"></script>
    <script src="{{ url_for('static',filename='js/bootstrap.js') }}"></script>
    
    </body>
    </html>

    其他模板可以这么写。对比一下面向对象编程的继承概念,我们可以很容易的理解。

    {% extends 'layout.html' %}
    {% block title %}主页{% endblock %}
    {% block body %}
    
        <div class="jumbotron">
            <h1>主页</h1>
            <p>本项目演示了Flask的简单使用方法,点击导航栏上的菜单条查看具体功能。</p>
        </div>
    
    {% endblock %}

    控制流

    条件判断可以这么写,类似于JSP标签中的Java 代码,{% %}中也可以写Python代码。下面是Flask官方文档的例子。

      <div class=metanav>
      {% if not session.logged_in %}
        <a href="{{ url_for('login') }}">log in</a>
      {% else %}
        <a href="{{ url_for('logout') }}">log out</a>
      {% endif %}
      </div>

    循环的话可以这么写,和在Python中遍历差不多。

            <tbody>
            {% for key,value in data.items() %}
                <tr>
                    <td>{{ key }}</td>
                    <td>{{ value }}</td>
                </tr>
            {% endfor %}
            <tr>
                <td>文件</td>
                <td></td>
            </tr>
            </tbody>

    需要注意不是所有的Python代码都可以写在模板里,如果希望从模板中引用其他文件的函数,需要显式将函数注册到模板中。可以参考这个爆栈提问

    写在最后

    这篇文章主要参考了Flask的官方文档,但是只介绍了 Flask的最基本的一部分。了解了这部分,我们可以用Python 搭一个小服务器做点事情。如果希望详细了解 Flask的使用用法,请关注更详细的资料。本文就是起一个抛砖引玉的效果。

    顺便说,通过Flask 我也了解了Python 语言的执行速度。我们都知道编译器编译出来的代码执行起来要比解释器解释代码要快大约几十倍到几千倍不等。以前学Java的时候,感觉Java 慢,主要原因就是等待编译时间比较长。相对来说用Python写脚本就很块了,因为没有编译过程。

    但是从Flask的运行速度来看,我切身感受到了Python 执行确实不快。举个例子,在Spring中写一个控制器,接受HTTP参数,并显示到页面上,如果程序编译完之后,这个显示过程基本是瞬时的。但是同样的需求在Flask中,我居然可以感觉到明显的延迟(大概几百毫秒的等待时间)。所以,如果你想写一个比较快的Web程序,还是用Java或者JVM语言吧,虽然看着土,性能确实杠杠的 。

    最后,我写了一个小练习,试了试Flask 的基本功能,如果有兴趣可以上我的Github查看代码。

    我的代码

    展开全文
  • Python+Flask框架Web应用开发

    千人学习 2019-10-30 14:11:11
    系统介绍基于Flask框架的Web应用开发,小快轻,当然依然水煮风格
  • Flask从入门到做出一个博客的大型教程(一)

    万次阅读 多人点赞 2018-05-19 11:42:56
    Flask从入门到做出一个博客的大型教程(一) 本项目全部在虚拟环境中运行,因此请参照前面的文章,链接为https://blog.csdn.net/u014793102/article/details/80302975 建立虚拟环境后,再接着完成本教程的学习。 ...

    Flask从入门到做出一个博客的大型教程(一)

    本项目全部在虚拟环境中运行,因此请参照前面的文章,链接为https://blog.csdn.net/u014793102/article/details/80302975 建立虚拟环境后,再接着完成本教程的学习。

    0 开始之前

    网上看了很多教程,都不是很满意,因此自己写一个大型教程,从入门到做出一个比较完整的博客。此次教程不是直接把整个博客直接代码整理出来然后运行一遍就完事,我会从flask的各个模块讲起。所以,如果你没有接触过flask,按照整个教程完整做一遍会掌握flask。(前提是你要有一定Python和web基础)

    1 Hello world !

    如果你接触过任何编程语言,对这个小标题都会很熟悉,此次对flask的学习也是从这个小例子开始。

    准备工作环境

    duke@coding:~$ mkdir flask_tutorial
    
    duke@coding:~$ cd flask_tutorial/
    
    duke@coding:~/flask_tutorial$ virtualenv --no-site-package venv
    Using base prefix '/home/duke/.pyenv/versions/3.6.4'
    New python executable in /home/duke/flask_tutorial/venv/bin/python3.6
    Also creating executable in /home/duke/flask_tutorial/venv/bin/python
    Installing setuptools, pip, wheel...done.
    
    duke@coding:~/flask_tutorial$ source venv/bin/activate
    #进入Python虚拟环境
    (venv) duke@coding:~/flask_tutorial$
    (venv) duke@coding:~/flask_tutorial$ pip install flask
    #创建flask目录
    (venv) duke@coding:~/flask_tutorial$ mkdir flask
    
    (venv) duke@coding:~/flask_tutorial$ cd flask/
    
    (venv) duke@coding:~/flask_tutorial/flask$

    正式开始

    (venv) duke@coding:~/flask_tutorial/flask$ mkdir app
    
    (venv) duke@coding:~/flask_tutorial/flask$ cd app/
    #创建初始化文件
    (venv) duke@coding:~/flask_tutorial/flask/app$ touch __init__.py

    在_ _ init _ _.py中写如下代码,你可以使用pycharm,vscode等IDE来更快的书写代码。

    app/_ _ init _ _.py : 项目初始化

    from flask import Flask
    #创建app应用,__name__是python预定义变量,被设置为使用本模块.
    app = Flask(__name__)
    #如果你使用的IDE,在routes这里会报错,因为我们还没有创建呀,为了一会不要再回来写一遍,因此我先写上了
    from app import routes

    创建路由模块,你可以使用IDE直接新建,没有必要要使用命令行创建

    (venv) duke@coding:~/flask_tutorial/flask/app$ touch routes.py

    app/routes.py : 主页路由

    #从app模块中即从__init__.py中导入创建的app应用
    from app import app
    
    #建立路由,通过路由可以执行其覆盖的方法,可以多个路由指向同一个方法。
    @app.route('/')
    @app.route('/index')
    def index():
        return "Hello,World!"

    现在基本都齐全了,但是我们还是需要在app模块之外创建一个主入口,即执行这个主入口就可以达成运行整个项目的目的。

    (venv) duke@coding:~/flask_tutorial/flask/app$ cd ..
    (venv) duke@coding:~/flask_tutorial/flask$ touch myblog.py

    myblog.py : 项目入口

    #从app模块中导入app应用
    from app import app
    
    #防止被引用后执行,只有在当前模块中才可以使用
    if __name__=='__main__':
        app.run()

    现在整个小demo就完成了,结构应该是这样的

    flask
    ├── app
    │   ├── __init__.py
    │   └── routes.py
    └── myblog.py

    ok,接下来就让项目跑起来

    (venv) duke@coding:~/flask_tutorial/flask$ export FLASK_APP=myblog.py
    
    (venv) duke@coding:~/flask_tutorial/flask$ flask run
     * Serving Flask app "myblog.py"
     * Environment: production
       WARNING: Do not use the development server in a production environment.
       Use a production WSGI server instead.
     * Debug mode: off
     * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
    

    在浏览器里输入http://127.0.0.1:5000/ 或者 http://127.0.0.1:5000/index 都可以访问你的项目啦!

    demo

    2 模板

    在1里,我们完成了一个返回值的显示,但这肯定远远不能满足我们需求的。因为我们希望看到的是丰富多彩的网页呀~,有什么办法呢?上一步咱们返回了一个值,那咱们返回一个网页会怎么样呢?

    app/routes.py : 返回一个网页

    from app import app
    
    @app.route('/')
    @app.route('/index')
    def index():
        user = {'username':'duke'}
        html = '''
        <html>
        <head>
            <title>Home Page - Microblog</title>
        </head>
        <body>
            <h1>Hello, ''' + user['username'] + '''!</h1>
        </body>
    </html>
    
        '''
        return html

    页面
    如图所示,这样就完成了返回网页的目的,但是这样实在是太麻烦了,因此肯定有解决这个问题的办法喽。对,模板就是解决这个问题的办法。

    (venv) duke@coding:~/flask_tutorial/flask$ mkdir app/templates
    #在新建的templates中新建一个index.html
    (venv) duke@coding:~/flask_tutorial/flask/app/templates$ touch index.html

    app/templates/index.html : 主页模板,将数据显示在其中

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>{{ title }} - 博客</title>
    </head>
    <body>
            <h1> Hello ,{{ user.username }} !</h1>
    </body>
    </html>

    这里出现{{ }} 意味着变量,可以接受数据的地方。既然在这里写了变量,那么就要在路由中修改对应的格式,因为要把数据返回才可以。

    app/routes.py : 将写在路由中的html删除,并增加一些变量。

    #导入模板模块
    from flask import render_template
    from app import app
    
    @app.route('/')
    @app.route('/index')
    def index():
        user = {'username':'duke'}
        #将需要展示的数据传递给模板进行显示
        return render_template('index.html',title='我的',user=user)

    从新运行后,效果还是符合预期的。但是,如果你传递至模板的数据是空的,那页面显示岂不是很难看?因此我们需要在模板中加上一些判断,看数据是否为空。

    app/templates/index.html : 主页模板,完善模板

    <!DOCTYPE html>
    <html >
    <head>
        <meta charset="UTF-8">
         {% if title %}
            <title>{{ title }} - 博客</title>
            {% else %}
            <title>欢迎来到博客!</title>
            {% endif %}
        </head>
        <body>
            <h1>Hello, {{ user.username }}!</h1>
        </body>
    </html>

    如果传进来的数据很多,那么就可以使用循环来展示数据。首先,还是对路由里的方法进行修改。

    app/routes.py : 增加一些数据

    from flask import render_template
    from app import app
    
    @app.route('/')
    @app.route('/index')
    def index():
        user = {'username':'duke'}
        posts = [
            {
                'author':{'username':'刘'},
                'body':'这是模板模块中的循环例子~1'
    
            },
            {
                'author': {'username': '忠强'},
                'body': '这是模板模块中的循环例子~2'
            }
        ]
        return render_template('index.html',title='我的',user=user,posts=posts)

    增加了这些数据之后要对模板中的结构进行一些修改。

    app/templates/index.html : 循环展示数据

    <!DOCTYPE html>
    <html >
    <head>
        <meta charset="UTF-8">
         {% if title %}
            <title>{{ title }} - 博客</title>
            {% else %}
            <title>欢迎来到博客!</title>
            {% endif %}
        </head>
        <body>
            <h1>你好呀, {{ user.username }} !</h1>
            {% for post in posts %}
                <div><p>{{ post.author.username }} 说:<b>{{ post.body }}</b></p></div>
            {% endfor %}
    
        </body>
    </html>

    tem1
    我们发现,每次修改页面都是要有很多重复的不必要代码,因此把重复的代码放到一个基类模板里,在里面留上占位符,这样只需要修改其中一部分就可以了,十分方便。

    创建一个基类模板

    (venv) duke@coding:~/flask_tutorial/flask/app/templates$ touch base.html

    app/templates/base.html : 填充内容

    <!DOCTYPE html>
    <html >
    <head>
        <meta charset="UTF-8">
         {% if title %}
            <title>{{ title }} - 博客</title>
            {% else %}
            <title>欢迎来到博客!</title>
            {% endif %}
        </head>
        <body>
           <div>博客 : <a href="/index">首页</a></div>
            {% block content %}
    
            {% endblock %}
        </body>
    </html>

    那么这有什么作用呢?注意这里面有{% block content %} {% endblock %}这一对标签,因此其他页面只需要继承这个页面,然后写上相同的标签,在标签内写上内容,就可以完整的在页面上显示所有内容。既然有了基类模板,那么index.html显示就不需要那么多代码了,这里对index.html进行修改。

    app/templates/index.html : 修改格式和内容

    {% extends 'base.html' %}
    
         {% block content %}
    
           <h1>你好呀, {{ user.username }} !</h1>
    
            {% for post in posts %}
                <div><p>{{ post.author.username }} 说:<b>{{ post.body }}</b></p></div>
            {% endfor %}
    
    
         {% endblock %}
    
    
    

    再此访问主页面的地址试一试!

    templates

    展开全文
  • Flask---框架快速入门

    万次阅读 多人点赞 2018-03-29 11:44:36
    本页提供了一个很好的 Flask 介绍,并假定你已经安装好了 Flask。如果没有,请跳转到 安装 章节。一个最小的应用一个最小的 Flask 应用看起来会是这样:from flask import Flask app = Flask(__name__) @app.route...

    迫不及待要开始了吗?本页提供了一个很好的 Flask 介绍,并假定你已经安装好了 Flask。如果没有,请跳转到 安装 章节。

    一个最小的应用

    一个最小的 Flask 应用看起来会是这样:

    from flask import Flask
    app = Flask(__name__)
    
    @app.route('/')
    def hello_world():
        return 'Hello World!'
    
    if __name__ == '__main__':
        app.run()
    

    把它保存为 hello.py (或是类似的),然后用 Python 解释器来运行。 确保你的应用文件名不是 flask.py ,因为这将与 Flask 本身冲突。

    $ python hello.py
     * Running on http://127.0.0.1:5000/
    

    现在访问 http://127.0.0.1:5000/ ,你会看见 Hello World 问候。

    那么,这段代码做了什么?

    1. 首先,我们导入了 Flask 类。这个类的实例将会是我们的 WSGI 应用程序。
    2. 接下来,我们创建一个该类的实例,第一个参数是应用模块或者包的名称。 如果你使用单一的模块(如本例),你应该使用 __name__ ,因为模块的名称将会因其作为单独应用启动还是作为模块导入而有不同( 也即是 '__main__' 或实际的导入名)。这是必须的,这样 Flask 才知道到哪去找模板、静态文件等等。详情见 Flask的文档。
    3. 然后,我们使用 route() 装饰器告诉 Flask 什么样的URL 能触发我们的函数。
    4. 这个函数的名字也在生成 URL 时被特定的函数采用,这个函数返回我们想要显示在用户浏览器中的信息。
    5. 最后我们用 run() 函数来让应用运行在本地服务器上。 其中 if __name__ =='__main__': 确保服务器只会在该脚本被 Python 解释器直接执行的时候才会运行,而不是作为模块导入的时候。

    欲关闭服务器,按 Ctrl+C。

    外部可访问的服务器

    如果你运行了这个服务器,你会发现它只能从你自己的计算机上访问,网络中其它任何的地方都不能访问。在调试模式下,用户可以在你的计算机上执行任意 Python 代码。因此,这个行为是默认的。

    如果你禁用了 debug 或信任你所在网络的用户,你可以简单修改调用 run() 的方法使你的服务器公开可用,如下:

    app.run(host='0.0.0.0')
    

    这会让操作系统监听所有公网 IP。

    调试模式

    虽然 run() 方法适用于启动本地的开发服务器,但是你每次修改代码后都要手动重启它。这样并不够优雅,而且 Flask 可以做到更好。如果你启用了调试支持,服务器会在代码修改后自动重新载入,并在发生错误时提供一个相当有用的调试器。

    有两种途径来启用调试模式。一种是直接在应用对象上设置:

    app.debug = True
    app.run()
    

    另一种是作为 run 方法的一个参数传入:

    app.run(debug=True)
    

    两种方法的效果完全相同。

    注意

    尽管交互式调试器在允许 fork 的环境中无法正常使用(也即在生产服务器上正常使用几乎是不可能的),但它依然允许执行任意代码。这使它成为一个巨大的安全隐患,因此它 绝对不能用于生产环境 。

    运行中的调试器截图:

    screenshot of debugger in action

    想用其它的调试器? 参见 调试器操作 。

    路由

    现代 Web 应用的 URL 十分优雅,易于人们辨识记忆,这一点对于那些面向使用低速网络连接移动设备访问的应用特别有用。如果可以不访问索引页,而是直接访问想要的那个页面,他们多半会笑逐颜开而再度光顾。

    如上所见, route() 装饰器把一个函数绑定到对应的 URL 上。

    这里是一些基本的例子:

    @app.route('/')
    def index():
        return 'Index Page'
    
    @app.route('/hello')
    def hello():
        return 'Hello World'
    

    但是,不仅如此!你可以构造含有动态部分的 URL,也可以在一个函数上附着多个规则。

    变量规则

    要给 URL 添加变量部分,你可以把这些特殊的字段标记为 <variable_name> , 这个部分将会作为命名参数传递到你的函数。规则可以用 <converter:variable_name> 指定一个可选的转换器。这里有一些不错的例子:

    @app.route('/user/<username>')
    def show_user_profile(username):
        # show the user profile for that user
        return 'User %s' % username
    
    @app.route('/post/<int:post_id>')
    def show_post(post_id):
        # show the post with the given id, the id is an integer
        return 'Post %d' % post_id
    

    转换器有下面几种:

    int接受整数
    float同 int ,但是接受浮点数
    path和默认的相似,但也接受斜线

    唯一 URL / 重定向行为

    Flask 的 URL 规则基于 Werkzeug 的路由模块。这个模块背后的思想是基于 Apache 以及更早的 HTTP 服务器主张的先例,保证优雅且唯一的 URL。

    以这两个规则为例:

    @app.route('/projects/')
    def projects():
        return 'The project page'
    
    @app.route('/about')
    def about():
        return 'The about page'
    

    虽然它们看起来着实相似,但它们结尾斜线的使用在 URL 定义 中不同。 第一种情况中,指向 projects 的规范 URL 尾端有一个斜线。这种感觉很像在文件系统中的文件夹。访问一个结尾不带斜线的 URL 会被 Flask 重定向到带斜线的规范 URL 去。

    然而,第二种情况的 URL 结尾不带斜线,类似 UNIX-like 系统下的文件的路径名。访问结尾带斜线的 URL 会产生一个 404 “Not Found” 错误。

    这个行为使得在遗忘尾斜线时,允许关联的 URL 接任工作,与 Apache 和其它的服务器的行为并无二异。此外,也保证了 URL 的唯一,有助于避免搜索引擎索引同一个页面两次。

    构造 URL

    如果 Flask 能匹配 URL,那么 Flask 可以生成它们吗?当然可以。你可以用 url_for()来给指定的函数构造 URL。它接受函数名作为第一个参数,也接受对应 URL 规则的变量部分的命名参数。未知变量部分会添加到 URL 末尾作为查询参数。这里有一些例子:

    >>> from flask import Flask, url_for
    >>> app = Flask(__name__)
    >>> @app.route('/')
    ... def index(): pass
    ...
    >>> @app.route('/login')
    ... def login(): pass
    ...
    >>> @app.route('/user/<username>')
    ... def profile(username): pass
    ...
    >>> with app.test_request_context():
    ...  print url_for('index')
    ...  print url_for('login')
    ...  print url_for('login', next='/')
    ...  print url_for('profile', username='John Doe')
    ...
    /
    /login
    /login?next=/
    /user/John%20Doe
    

    (这里也用到了 test_request_context() 方法,下面会解释。即使我们正在通过 Python 的 shell 进行交互,它依然会告诉 Flask 要表现为正在处理一个请求。请看下面的解释。 环境局部变量 )

    为什么你要构建 URL 而非在模板中硬编码?这里有三个绝妙的理由:

    1. 反向构建通常比硬编码的描述性更好。更重要的是,它允许你一次性修改 URL, 而不是到处边找边改。
    2. URL 构建会转义特殊字符和 Unicode 数据,免去你很多麻烦。
    3. 如果你的应用不位于 URL 的根路径(比如,在 /myapplication 下,而不是 / ),url_for() 会妥善处理这个问题。

    HTTP 方法

    HTTP (与 Web 应用会话的协议)有许多不同的访问 URL 方法。默认情况下,路由只回应 GET 请求,但是通过 route() 装饰器传递 methods 参数可以改变这个行为。这里有一些例子:

    @app.route('/login', methods=['GET', 'POST'])
    def login():
        if request.method == 'POST':
            do_the_login()
        else:
            show_the_login_form()
    

    如果存在 GET ,那么也会替你自动地添加 HEAD,无需干预。它会确保遵照 HTTP RFC(描述 HTTP 协议的文档)处理 HEAD 请求,所以你可以完全忽略这部分的 HTTP 规范。同样,自从 Flask 0.6 起, 也实现了 OPTIONS 的自动处理。

    你不知道一个 HTTP 方法是什么?不必担心,这里会简要介绍 HTTP 方法和它们为什么重要:

    HTTP 方法(也经常被叫做“谓词”)告知服务器,客户端想对请求的页面  些什么。下面的都是非常常见的方法:

    GET
    浏览器告知服务器:只 获取 页面上的信息并发给我。这是最常用的方法。
    HEAD
    浏览器告诉服务器:欲获取信息,但是只关心 消息头 。应用应像处理 GET 请求一样来处理它,但是不分发实际内容。在 Flask 中你完全无需 人工 干预,底层的 Werkzeug 库已经替你打点好了。
    POST
    浏览器告诉服务器:想在 URL 上 发布 新信息。并且,服务器必须确保 数据已存储且仅存储一次。这是 HTML 表单通常发送数据到服务器的方法。
    PUT
    类似 POST 但是服务器可能触发了存储过程多次,多次覆盖掉旧值。你可 能会问这有什么用,当然这是有原因的。考虑到传输中连接可能会丢失,在 这种 情况下浏览器和服务器之间的系统可能安全地第二次接收请求,而 不破坏其它东西。因为 POST它只触发一次,所以用 POST 是不可能的。
    DELETE
    删除给定位置的信息。
    OPTIONS
    给客户端提供一个敏捷的途径来弄清这个 URL 支持哪些 HTTP 方法。 从 Flask 0.6 开始,实现了自动处理。

    有趣的是,在 HTML4 和 XHTML1 中,表单只能以 GET 和 POST 方法提交到服务器。但是 JavaScript 和未来的 HTML 标准允许你使用其它所有的方法。此外,HTTP 最近变得相当流行,浏览器不再是唯一的 HTTP 客户端。比如,许多版本控制系统就在使用 HTTP。

    静态文件

    动态 web 应用也会需要静态文件,通常是 CSS 和 JavaScript 文件。理想状况下, 你已经配置好 Web 服务器来提供静态文件,但是在开发中,Flask 也可以做到。 只要在你的包中或是模块的所在目录中创建一个名为 static 的文件夹,在应用中使用 /static 即可访问。

    给静态文件生成 URL ,使用特殊的 'static' 端点名:

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

    这个文件应该存储在文件系统上的 static/style.css 。

    模板渲染

    用 Python 生成 HTML 十分无趣,而且相当繁琐,因为你必须手动对 HTML 做转义来保证应用的安全。为此,Flask 配备了 Jinja2 模板引擎。

    你可以使用 render_template() 方法来渲染模板。你需要做的一切就是将模板名和你想作为关键字的参数传入模板的变量。这里有一个展示如何渲染模板的简例:

    from flask import render_template
    
    @app.route('/hello/')
    @app.route('/hello/<name>')
    def hello(name=None):
        return render_template('hello.html', name=name)
    

    Flask 会在 templates 文件夹里寻找模板。所以,如果你的应用是个模块,这个文件夹应该与模块同级;如果它是一个包,那么这个文件夹作为包的子目录:

    情况 1: 模块:

    /application.py
    /templates
        /hello.html
    

    情况 2: 包:

    /application
        /__init__.py
        /templates
            /hello.html
    

    关于模板,你可以发挥 Jinja2 模板的全部实例。更多信息请见 Jinja2 模板文档 。

    这里有一个模板实例:

    <!doctype html>
    <title>Hello from Flask</title>
    {% if name %}
      <h1>Hello {{ name }}!</h1>
    {% else %}
      <h1>Hello World!</h1>
    {% endif %}
    

    在模板里,你也可以访问 request 、 session 和 g [1] 对象, 以及get_flashed_messages() 函数。

    模板继承让模板用起来相当顺手。如欲了解继承的工作机理,请跳转到 模板继承 模式的文档。最起码,模板继承能使特定元素 (比如页眉、导航栏和页脚)可以出现在所有的页面。

    自动转义功能默认是开启的,所以如果 name 包含 HTML ,它将会被自动转义。如果你能信任一个变量,并且你知道它是安全的(例如一个模块把 Wiki 标记转换为 HTML),你可以用 Markup 类或 |safe 过滤器在模板中把它标记为安全的。在 Jinja 2 文档中,你会看到更多的例子。

    这里是一个 Markup 类如何使用的简单介绍:

    >>> from flask import Markup
    >>> Markup('<strong>Hello %s!</strong>') % '<blink>hacker</blink>'
    Markup(u'<strong>Hello &lt;blink&gt;hacker&lt;/blink&gt;!</strong>')
    >>> Markup.escape('<blink>hacker</blink>')
    Markup(u'&lt;blink&gt;hacker&lt;/blink&gt;')
    >>> Markup('<em>Marked up</em> &raquo; HTML').striptags()
    u'Marked up \xbb HTML'
    

    在 0.5 版更改: 自动转义不再在所有模板中启用。下列扩展名的模板会触发自动转义:.html 、 .htm 、.xml 、 .xhtml 。从字符串加载的模板会禁用自动转义。

    [1]不确定 g 对象是什么?它允许你按需存储信息, 查看( g )对象的文档和 在 Flask 中使用 SQLite 3 的文档以获取更多信息。

    访问请求数据

    对于 Web 应用,与客户端发送给服务器的数据交互至关重要。在 Flask 中由全局的request 对象来提供这些信息。如果你有一定的 Python 经验,你会好奇,为什么这个对象是全局的,为什么 Flask 还能保证线程安全。答案是环境作用域:

    环境局部变量

    内幕

    如果你想理解其工作机制及如何利用环境局部变量实现自动化测试,请阅读此节,否则可跳过。

    Flask 中的某些对象是全局对象,但却不是通常的那种。这些对象实际上是特定环境的局部对象的代理。虽然很拗口,但实际上很容易理解。

    想象一下处理线程的环境。一个请求传入,Web 服务器决定生成一个新线程( 或者别的什么东西,只要这个底层的对象可以胜任并发系统,而不仅仅是线程)。 当 Flask 开始它内部的请求处理时,它认定当前线程是活动的环境,并绑定当前的应用和 WSGI 环境到那个环境上(线程)。它的实现很巧妙,能保证一个应用调用另一个应用时不会出现问题。

    所以,这对你来说意味着什么?除非你要做类似单元测试的东西,否则你基本上可以完全无视它。你会发现依赖于一段请求对象的代码,因没有请求对象无法正常运行。解决方案是,自行创建一个请求对象并且把它绑定到环境中。单元测试的最简单的解决方案是:用 test_request_context() 环境管理器。结合 with 声明,绑定一个测试请求,这样你才能与之交互。下面是一个例子:

    from flask import request
    
    with app.test_request_context('/hello', method='POST'):
        # now you can do something with the request until the
        # end of the with block, such as basic assertions:
        assert request.path == '/hello'
        assert request.method == 'POST'
    

    另一种可能是:传递整个 WSGI 环境给 request_context() 方法:

    from flask import request
    
    with app.request_context(environ):
        assert request.method == 'POST'
    

    请求对象

    API 章节对请求对象作了详尽阐述(参见 request ),因此这里不会赘述。此处宽泛介绍一些最常用的操作。首先从 flask 模块里导入它:

    from flask import request
    

    当前请求的 HTTP 方法可通过 method 属性来访问。通过:attr:~flask.request.form 属性来访问表单数据( POST 或 PUT 请求提交的数据)。这里有一个用到上面提到的那两个属性的完整实例:

    @app.route('/login', methods=['POST', 'GET'])
    def login():
        error = None
        if request.method == 'POST':
            if valid_login(request.form['username'],
                           request.form['password']):
                return log_the_user_in(request.form['username'])
            else:
                error = 'Invalid username/password'
        # the code below is executed if the request method
        # was GET or the credentials were invalid
        return render_template('login.html', error=error)
    

    当访问 form 属性中的不存在的键会发生什么?会抛出一个特殊的 KeyError 异常。你可以像捕获标准的 KeyError 一样来捕获它。 如果你不这么做,它会显示一个 HTTP 400 Bad Request 错误页面。所以,多数情况下你并不需要干预这个行为。

    你可以通过 args 属性来访问 URL 中提交的参数 ( ?key=value ):

    searchword = request.args.get('q', '')
    

    我们推荐用 get 来访问 URL 参数或捕获 KeyError ,因为用户可能会修改 URL,向他们展现一个 400 bad request 页面会影响用户体验。

    欲获取请求对象的完整方法和属性清单,请参阅 request 的文档。

    文件上传

    用 Flask 处理文件上传很简单。只要确保你没忘记在 HTML 表单中设置enctype="multipart/form-data" 属性,不然你的浏览器根本不会发送文件。

    已上传的文件存储在内存或是文件系统中一个临时的位置。你可以通过请求对象的files 属性访问它们。每个上传的文件都会存储在这个字典里。它表现近乎为一个标准的 Python file 对象,但它还有一个 save() 方法,这个方法允许你把文件保存到服务器的文件系统上。这里是一个用它保存文件的例子:

    from flask import request
    
    @app.route('/upload', methods=['GET', 'POST'])
    def upload_file():
        if request.method == 'POST':
            f = request.files['the_file']
            f.save('/var/www/uploads/uploaded_file.txt')
        ...
    

    如果你想知道上传前文件在客户端的文件名是什么,你可以访问 filename 属性。但请记住, 永远不要信任这个值,这个值是可以伪造的。如果你要把文件按客户端提供的文件名存储在服务器上,那么请把它传递给 Werkzeug 提供的 secure_filename() 函数:

    from flask import request
    from werkzeug import secure_filename
    
    @app.route('/upload', methods=['GET', 'POST'])
    def upload_file():
        if request.method == 'POST':
            f = request.files['the_file']
            f.save('/var/www/uploads/' + secure_filename(f.filename))
        ...
    

    一些更好的例子,见 上传文件 模式。

    Cookies

    你可以通过 cookies 属性来访问 Cookies,用响应对象的 set_cookie 方法来设置 Cookies。请求对象的 cookies 属性是一个内容为客户端提交的所有 Cookies 的字典。如果你想使用会话,请不要直接使用 Cookies,请参考 会话 一节。在 Flask 中,已经注意处理了一些 Cookies 安全细节。

    读取 cookies:

    from flask import request
    
    @app.route('/')
    def index():
        username = request.cookies.get('username')
        # use cookies.get(key) instead of cookies[key] to not get a
        # KeyError if the cookie is missing.
    

    存储 cookies:

    from flask import make_response
    
    @app.route('/')
    def index():
        resp = make_response(render_template(...))
        resp.set_cookie('username', 'the username')
        return resp
    

    可注意到的是,Cookies 是设置在响应对象上的。由于通常视图函数只是返回字符串,之后 Flask 将字符串转换为响应对象。如果你要显式地转换,你可以使用make_response() 函数然后再进行修改。

    有时候你想设置 Cookie,但响应对象不能醋在。这可以利用 延迟请求回调 模式实现。

    为此,也可以阅读 关于响应 。

    重定向和错误

    你可以用 redirect() 函数把用户重定向到其它地方。放弃请求并返回错误代码,用abort() 函数。这里是一个它们如何使用的例子:

    from flask import abort, redirect, url_for
    
    @app.route('/')
    def index():
        return redirect(url_for('login'))
    
    @app.route('/login')
    def login():
        abort(401)
        this_is_never_executed()
    

    这是一个相当无意义的例子因为用户会从主页重定向到一个不能访问的页面 (401 意味着禁止访问),但是它展示了重定向是如何工作的。

    默认情况下,错误代码会显示一个黑白的错误页面。如果你要定制错误页面, 可以使用errorhandler() 装饰器:

    from flask import render_template
    
    @app.errorhandler(404)
    def page_not_found(error):
        return render_template('page_not_found.html'), 404
    

    注意 render_template() 调用之后的 404 。这告诉 Flask,该页的错误代码是 404 ,即没有找到。默认为 200,也就是一切正常。

    关于响应

    视图函数的返回值会被自动转换为一个响应对象。如果返回值是一个字符串, 它被转换为该字符串为主体的、状态码为 200 OK``的  MIME 类型是 ``text/html 的响应对象。Flask 把返回值转换为响应对象的逻辑是这样:

    1. 如果返回的是一个合法的响应对象,它会从视图直接返回。
    2. 如果返回的是一个字符串,响应对象会用字符串数据和默认参数创建。
    3. 如果返回的是一个元组,且元组中的元素可以提供额外的信息。这样的元组必须是(response, status, headers) 的形式,且至少包含一个元素。 status 值会覆盖状态代码, headers 可以是一个列表或字典,作为额外的消息标头值。
    4. 如果上述条件均不满足, Flask 会假设返回值是一个合法的 WSGI 应用程序,并转换为一个请求对象。

    如果你想在视图里操纵上述步骤结果的响应对象,可以使用 make_response() 函数。

    譬如你有这样一个视图:

    @app.errorhandler(404)
    def not_found(error):
        return render_template('error.html'), 404
    

    你只需要把返回值表达式传递给 make_response() ,获取结果对象并修改,然后再返回它:

    @app.errorhandler(404)
    def not_found(error):
        resp = make_response(render_template('error.html'), 404)
        resp.headers['X-Something'] = 'A value'
        return resp
    

    会话

    除请求对象之外,还有一个 session 对象。它允许你在不同请求间存储特定用户的信息。它是在 Cookies 的基础上实现的,并且对 Cookies 进行密钥签名。这意味着用户可以查看你 Cookie 的内容,但却不能修改它,除非用户知道签名的密钥。

    要使用会话,你需要设置一个密钥。这里介绍会话如何工作:

    from flask import Flask, session, redirect, url_for, escape, request
    
    app = Flask(__name__)
    
    @app.route('/')
    def index():
        if 'username' in session:
            return 'Logged in as %s' % escape(session['username'])
        return 'You are not logged in'
    
    @app.route('/login', methods=['GET', 'POST'])
    def login():
        if request.method == 'POST':
            session['username'] = request.form['username']
            return redirect(url_for('index'))
        return '''
            <form action="" method="post">
                <p><input type=text name=username>
                <p><input type=submit value=Login>
            </form>
        '''
    
    @app.route('/logout')
    def logout():
        # remove the username from the session if it's there
        session.pop('username', None)
        return redirect(url_for('index'))
    
    # set the secret key.  keep this really secret:
    app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'
    

    这里提到的 escape() 可以在你模板引擎外做转义(如同本例)。

    如何生成强壮的密钥

    随机的问题在于很难判断什么是真随机。一个密钥应该足够随机。你的操作系统可以基于一个密钥随机生成器来生成漂亮的随机值,这个值可以用来做密钥:

    >>> import os
    >>> os.urandom(24)
    '\xfd{H\xe5<\x95\xf9\xe3\x96.5\xd1\x01O<!\xd5\xa2\xa0\x9fR"\xa1\xa8'
    

    把这个值复制粘贴进你的代码中,你就有了密钥。

    使用基于 cookie 的会话需注意: Flask 会将你放进会话对象的值序列化至 Cookies。如果你发现某些值在请求之间并没有持久存在,然而确实已经启用了 Cookies,但也没有得到明确的错误信息。这时,请检查你的页面响应中的 Cookies 的大小,并与 Web 浏览器所支持的大小对比。

    消息闪现

    反馈,是良好的应用和用户界面的重要构成。如果用户得不到足够的反馈,他们很可能开始厌恶这个应用。 Flask 提供了消息闪现系统,可以简单地给用户反馈。 消息闪现系统通常会在请求结束时记录信息,并在下一个(且仅在下一个)请求中访问记录的信息。展现这些消息通常结合要模板布局。

    使用 flash() 方法可以闪现一条消息。要操作消息本身,请使用get_flashed_messages() 函数,并且在模板中也可以使用。完整的例子见 消息闪现 部分。

    日志记录

    0.3 新版功能.

    有时候你会处于这样一种境地,你处理的数据本应该是正确的,但实际上不是。 比如,你会有一些向服务器发送请求的客户端代码,但请求显然是畸形的。这可能是用户篡改了数据,或是客户端代码的粗制滥造。大多数情况下,正常地返回 400 Bad Request 就可以了,但是有时候不能这么做,并且要让代码继续运行。

    你可能依然想要记录下,是什么不对劲。这时日志记录就派上了用场。从 Flask 0.3 开始,Flask 就已经预置了日志系统。

    这里有一些调用日志记录的例子:

    app.logger.debug('A value for debugging')
    app.logger.warning('A warning occurred (%d apples)', 42)
    app.logger.error('An error occurred')
    

    附带的 logger 是一个标准日志类 Logger ,所以更多信息请查阅 logging 的文档 。

    整合 WSGI 中间件

    如果你想给你的应用添加 WSGI 中间件,你可以封装内部 WSGI 应用。例如若是你想用 Werkzeug 包中的某个中间件来应付 lighttpd 中的 bugs ,可以这样做:

    from werkzeug.contrib.fixers import LighttpdCGIRootFix
    app.wsgi_app = LighttpdCGIRootFix(app.wsgi_app)
    

    部署到 Web 服务器

    准备好部署你的 Flask 应用了?你可以立即部署到托管平台来圆满完成快速入门,以下厂商均向小项目提供免费的方案:

    托管 Flask 应用的其它选择:

    如果你有自己的主机,并且准备自己托管,参见 部署选择 章节。

    展开全文
  • ValueError: signal only works in main thread

    千次阅读 2019-02-13 17:15:40
    jupyter notebook 无法正常运行 查看 notebook.log 报错: ERROR:tornado.general:Uncaught exception in zmqstream callback Traceback (most recent call last): File &.../mnt/home2/zxm/anaconda3/lib/...
  • 如果flask开启了多线程,那么运行就不应该错误【gunicorn+gevent的linux运行服务器】运行flask时,Pyppeteer异步是没问题的 而如果使用flask自带的run方法,则会报错:signal only works in main thread【因为...
  • 报错信息: ...此处省略 File "/usr/lib/python3.5/signal.py", line 47, in signal handler = _signal.signal(_enum_to_int(signalnum), _enum_to_int(handler)) ValueError: signal only works in main thread ...
  • Flask --- 框架快速入门

    万次阅读 多人点赞 2019-08-31 17:19:48
    From:http://docs.jinkan.org/docs/flask/quickstart.html Flask 用户指南:http://docs.jinkan.org/docs/flask/index.html w3cschool.cn的Flask教程:...《Flask Web开发:基于Python的Web应用...
  • Windows下安装Flask

    万次阅读 多人点赞 2013-05-24 17:32:08
    Windows下安装Flask Flask介绍 是一个轻量级的Web应用框架, 使用Python编写。基于 WerkzeugWSGI工具箱和 Jinja2模板引擎。Flask使用 BSD 授权。 Flask也被称为 “microframework” ,因为它使用简单的核心,...
  • Python Flask Web 框架入门

    万次阅读 多人点赞 2019-03-11 15:03:27
    Flask是一个轻量级的基于Python的web框架。 本文适合有一定HTML、Python、网络基础的同学阅读。 1. 简介 这份文档中的代码使用 Python 3 运行。 是的,所以读者需要自己在电脑上安装Python 3 和 pip3。建议安装...
  • FlaskFlask解决跨域的问题

    万次阅读 2018-04-15 17:39:23
    Flask开发RESTful后端时,前端请求会遇到跨域的问题。下面是解决方法。Python版本:3.5.1 下载flask_cors包 pip install flask-cors 使用flask_cors的CORS,代码示例 from flask_cors import * app = ...
  • Flask框架

    万次阅读 2020-05-03 17:33:24
    Flask框架 一、初识Flask 二、Flask路由注册 三、Flask的HTTP请求 四、Flask视图函数 五、Flask模板 六、Flask应用数据库 七、Flask脚本 八、Flask cookie、session、闪现 九、Flask请求扩展、中间件、蓝图 十、...
  • flask web后台启动后会发现默认是 localhost 127.0.0.1:5000 可以采用以下方式运行 from flask import Flask from flask import request app = Flask(__name__) @app.route('/') def index(): user_...
  • FLASK接受POST请求,并解析参数

    万次阅读 2019-03-21 10:30:32
    1、首先表明我要接收的数据源是通过Postman发动的json请求体,如下格式 2、FLASK接收请求的方法: 3、结果显示: 之所以打印了request.form和value是为了看看这些值到底是什么样子 ...
  • flask接口设置外网访问

    万次阅读 2018-08-07 11:32:41
    from flask import Flask from flask import request from flask import redirect from flask import jsonify import json app = Flask(__name__) @app.route('/api/hello', methods=['GET']) def start(): r.....
  • flask实现异步任务

    万次阅读 2018-09-29 16:44:28
    flask实现异步任务 最近在开发同步mysql数据到redis的接口,因为数据同步涉及各种增删查改,如果用同步实现,可能回造成连接超时、堵塞,所以,使用python实现异步任务。 代码实现 from flask import Flask ...
  • 导入flask项目 File-Open选择需要导入flask项目(如项目名为flasky)。  二.设置解释器 File-Settings-Project Interpreter,如下图所示 三.添加Flask server 1.点击pycharm右上角的Add Configuration ...
  • 地址:http://www.jb51.net/article/63181.htm
  • 改变flask监听的主机地址和端口号

    万次阅读 2017-06-15 23:57:39
    If you use command: flask run to start http server, it's not OK to add host and port information under run function, like below: if __name__== '__main__':  app.run(  host = '0.0.0.0',  
1 2 3 4 5 ... 20
收藏数 72,311
精华内容 28,924
关键字:

flask