A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© hongsandao 中级黑马   /  2018-6-27 22:38  /  1730 人查看  /  1 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

一、初始化

每个Flask程序都必须创建一个实例,即Flask类的对象,一般采用如下形式:

    1 from flask import Flask
    2
    3 app = Flask(__name__)

其中, name用于指定程序主模块或包的名字,Flask利用name的值决定程序所在根目录,以方便相对于根目录来查找资源文件。



二、路由

1、静态路由

当客户端发出请求给web服务器后,web服务器会将该请求转发给Flask实例进行处理,因此Flask实例必须知道每个接收到的请求该对应运行那个接口,这就通过保存URL地址到接口的映射关系来实现路由。 Flask通过app.route装饰器将函数注册为路由,如:

    1 @app.route('/')
    2 def hello_world():
    3     return 'Hello World!'

代码中,将hello_world()函数注册为程序根地址的处理接口,假设当前程序的域名为"www.example.com",则在浏览器中输入"http://www.example.com/"后,将输出"Hello World!",此即Flask程序给出的响应。

2、动态路由

上面的路由称为静态路由,即路由地址是固定的,但有些应用场景下,我们可能会发现路由地址中有些部分是可变的,比如获取用户信息的URL地址"http://www.example.com/pattywgm"中pattywgm就是可变的部分,对不同的用户对应不同的用户编号。Flask为我们提供了参数式的可变路由方式,如:

    1 @app.route("/user/<user_name>")
    2 def hello_user(user_name):
    3     return "Hello %s" % user_name



三、启动服务器

Flask程序以run()方法启动web服务器,如:

    1 if __name__ == '__main__':
    2     app.run()

服务器启动后,会进入轮询,一直运行到程序停止为止。开发中,我们可以设置app.run(debug=True)来开启debug模式,方便调试。



四、运行示例

Flask默认启用5000端口





五、程序上下文

     Flask接收到来自客户端的请求后,路由到指定的接口进行响应处理并返回处理结果。响应接口需要知道客户端的请求体,即request对象才能进行正确的处理。如果给每个接口函数都传递一个request对象参数,太过冗余,且代码会很难看,因此Flask使用上下文临时将某些对象编程线程内的全局变量,即在同一个线程内,上下文信息可全局共享,且不会干扰到其他的线程环境。

     Flask有两种上下文,分别为程序上下文和请求上下文,各自对应的全局变量如下表:

  变量名                上下文          备注                     
  current_app        程序上下文        表示当前运行的程序实例            
  g                  程序上下文        处理请求时用作临时存储对象,每次请求都会重新设值
  request            请求上下文        客户端发来的request请求对象      
  session            请求上下文        请求携带的会话信息               



Flask在分发请求之前会激活上下文信息,在请求处理完成后再删除。上下文只有在激活后,才可使用对应的全局变量,否则会报异常。

app_ctx = app.app_context()    #可获取app对应的上下文

app_ctx.push()  #推送上下文信息,即激活上下文



六、请求钩子

有时候,在请求开始或结束后,我们可能想要执行一些代码,比如,在请求开始前,完成数据库的连接,用户信息的认证等等。Flask使用装饰器为我们提供了注册通用函数的功能,支持的钩子函数有以下四种:

  before_first_request        第一次请求之前调用            
  before_request              每次请求之前调用              
  after_request               每次请求之后调用,前提是没有未处理的异常抛出
  teardown_request            每次请求之后调用,即使有未处理的异常抛出  



在请求函数和钩子函数之间,一般通过全局变量g实现数据共享。



七、响应

Flask的响应一般可包含三个参数,第一个参数指明响应的字符串,第二个参数可指明响应状态码,如200表示响应成功,第三个参数是由header首部组成的字典。我们可以通过make_response()生成一个response对象并设置。

     1 #!/usr/bin/env python
     2 # encoding: utf-8
     3 from flask import Flask
     4 from flask import request
     5
     6 app = Flask(__name__)
     7
     8
     9 @app.route('/')
    10 def hello_world():
    11     return 'Hello World!'
    12
    13
    14 @app.route("/user/<user_name>")
    15 def hello_user(user_name):
    16     """
    17      动态路由示例
    18     :param user_name: 用户名
    19     :return:
    20     """
    21     1/0
    22     return "Hello %s" % user_name
    23
    24
    25 @app.route("/context")
    26 def get_context():
    27     """
    28      获取请求的上下文信息
    29     :return:
    30     """
    31     user_agent = request.headers.get("user-agent")
    32     return "your user-agent is %s" % user_agent
    33
    34
    35 @app.before_first_request
    36 def first_quest():
    37     print "run before first request"
    38
    39
    40 @app.before_request
    41 def every_request():
    42     print "One new request is comming"
    43
    44
    45 @app.after_request
    46 def after_r(exception):
    47     print "The request has done 1"
    48
    49 @app.teardown_request
    50 def teardown_r(exception):
    51     print "The request has done 2"
    52
    53
    54 if __name__ == '__main__':
    55     app.run()

上述代码运行后,在浏览器分别请求

http://127.0.0.1:5000/ 输出:

    run before first request

    One new request is comming   The request has done 1   The request has done 2

http://127.0.0.1:5000/user/patty 输出:

   One new request is comming

   The request has done 2



变量共享:通过g实现

    1 @app.route('/')
    2 def hello_world():
    3     print g.user
    4     return 'Hello World!'
    5
    6 @app.before_first_request
    7 def first_quest():
    8     g.user = 'tmp'
    9     print "run before first request"



八、引入JinJa2模版

首先我们来对比两段代码,它们实现同样的功能, 都是现实用户信息列表:

     1 # 第一段
     2 @app.route("/users")
     3 def get_users():
           # 前面省略逻辑处理过程,得到users列表
     4     users = [{"name": "Jack", "age": 25, "city": "NewYork"},
     5              {"name": "Rose", "age": 26, "city": "Beijing"},
     6              {"name": "LiLi", "age": 23, "city": "ShangHai"}]
     7     html = """
     8         <table border="1">
     9            <tr>
    10                <th>姓名</th>
    11                <th>年龄</th>
    12                <th>城市</th>
    13            </tr>"""
    14     for user in users:
    15         h = """<tr>
    16              <td>%s</td>
    17              <td>%s</td>
    18              <td>%s</td>
    19         </tr>"""%(user['name'], user['age'], user['city'])
    20         html += h
    21     html += "</table>"
    22     return html
    23
    24 # 第二段
    25 @app.route("/users")
    26 def get_users_with_tempalte():
           # 逻辑处理
    27     users = [{"name": "Jack", "age": 25, "city": "NewYork"},
    28              {"name": "Rose", "age": 26, "city": "Beijing"},
    29              {"name": "LiLi", "age": 23, "city": "ShangHai"}]
    30     return render_template("users.html", users=users)

第一段代码中,将业务逻辑处理与视图展现的html都放在视图函数里,当业务处理逻辑或展现页面较复杂时,整个试图函数将会非常丑陋,难以理解且不易维护。



     1 <table border="1">
     2     <tr>
     3         <th>姓名</th>
     4         <th>年龄</th>
     5         <th>城市</th>
     6     </tr>
     7
     8     {% for user in users %}
     9     <tr>
    10         <td>{{ user.name }}</td>
    11         <td>{{ user.age }}</td>
    12         <td>{{ user.city }}</td>
    13     </tr>
    14     {%endfor%}
    15
    16 </table>

第二段代码中, 我们将业务逻辑处理与页面展现分开,将展现逻辑转移到模版中,使得代码目的清晰,层次分明,而且在模版中我们可以更好的进行页面渲染,这就是Jinja2模版引擎的魅力。



render_template()函数用于渲染模版,第一个参数表示模版文件名, 我们需要在当前项目路径下创建一个templates文件夹,用来放置html模版文件。随后的参数都是键值对,表示模版中的变量对应的实际值。



九、Jinja2模版中的控制结构

1、if-else语句   

    {% if name %}
        <h1>Hello, {{ name }}!</h1>
    {% else %}
        <h1>Hello, Stranger!</h1>
    {% endif %}



2、for循环(见(八)中示例)



3、宏定义

    Jinja2中的宏,类似Python中的函数,可供需要时调用,例如对于对于上文展现用户列表的例子,我们可以这样定义:

     1 {% macro render_user(user) %}
     2     <td>{{ user.name }}</td>
     3     <td>{{ user.age }}</td>
     4     <td>{{ user.city }}</td>
     5 {% endmacro %}
     6
     7 <table border="1">
     8     <tr>
     9         <th>姓名</th>
    10         <th>年龄</th>
    11         <th>城市</th>
    12     </tr>
    13
    14     {% for user in users %}
    15     <tr>
    16         {{ render_user(user) }}
    17     </tr>
    18     {%endfor%}
    19
    20 </table>

其中:macro表示定义宏, render_user相当于函数名, user即为参数。若是某些宏重复使用率较高, 我们可以将它们保存在一个单独的html文件中,然后通过 {% import 'macros.html  as macros %}引入,类似于Python的模块调用过程。



4、模版引用

和宏的引用类似, 我们可将多处重复使用的模版代码保存在单独的文件中,然后在需要的地方引用即可{% include 'common.html' %}



5、模版继承

模版继承类似于Python的继承,我们可以创建一个base.html模版,该模版包含基本的html块结构,通过继承该模版,可省去在每个模版中都书写相似内容的过程,而只突出当前模版的主要功能。

    <!--base.html-->
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Jinja2 Template</title>
    </head>
    <body>
     {% block body %}
     {% endblock %}
    </body>
    </html>
   
    <!-- users.html -->
    {% extends 'base.html' %}
   
    {% block body %}
    {% macro render_user(user) %}
        <td>{{ user.name }}</td>
        <td>{{ user.age }}</td>
        <td>{{ user.city }}</td>
    {% endmacro %}
   
    <table border="1">
        <tr>
            <th>姓名</th>
            <th>年龄</th>
            <th>城市</th>
        </tr>
   
        {% for user in users %}
        <tr>
            {{ render_user(user) }}
        </tr>
        {%endfor%}
   
    </table>
    {% endblock %}

extends关键字表示继承关系, {% block body %}...{% endblock %}实现了body部分内容的个性化展现。








1 个回复

倒序浏览
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马