day052 Flask基础,模板

今天了解Flask框架的: 获取的请求数据,异常捕获,请求钩子,状态保持,上下文,Flast-script,以及模板的使用

获取请求数据

  • 通过使用request.<属性>的方式,获取request对象传过来的数据
  • 常用的属性如下:

    • method ,记录请求使用的HTTP方法,类型为:GET/POST
    • heafers,记录请求中的报文头,类型为:EnvironHeaders
    • url,记录请求的URL地址,类型为:string
    • cookies,记录请求中的cookies信息,类型为:Dict
    • args,记录请求中的查询参数,类型为:MultiDict
    • form,记录请求中的表单数据,类型为:MultiDict
    • data,记录请求的数据,并且转换为字符串
    • files,记录请求上传的文件
    • 调用方法:直接使用request.<属性>即可
    print 'method: %s' % request.method
    print 'headers: %s' % request.headers
    print 'url: %s' % request.url
    print 'cookies: %s' % request.cookies
    
    
    # args: name=XXX  form:name=XXX data=XXX file=图片
    
    
    print 'args: %s' % request.args.get('name')
    print 'form: %s' % request.form.get('name')
    print 'data: %s' % request.data
    print 'files: %s' % request.files
    
    
    # 保存图片到当前目录
    
    
    # 需要设置当前项目的工作目录, 这样传入文件路径时, '.'就可以表示当前项目根目录
    
    image_file = request.files.get('image')
    image_file.save('./image.jpg') 

异常获取(理解)

  • 使用 errorhandler装饰器捕获希望捕获的异常信息。
  • 捕获到异常时,可对异常进行进一步的处理,自定义渲染过的404异常或者500异常页面并且返还给浏览器

abort方法

  • 可以抛出一个给定状态代码的异常
  • 如:
    abort(404) # 抛出一个404异常

errorhandler装饰器

  • 注册一个指定异常,当该异常发生时,执行errorhandler装饰器装饰的函数
  • 如:
# 如果有404异常抛出,捕获到,并且执行关联的函数
@app.errorhandler(404)
def internal_server_error(error) # 参数是捕获到的异常
    return '网页找不到了'404

请求钩子(理解)

  • 类似django中的中间键,在请求的前后执行额外的任务
  • Flask支持如下4种请求钩子
    1. befor_first_request:在处理第一个请求之前运行,再次发起相同的的请求就不在执行
    2. before_request:在每次请求之前运行
    3. after_request:在每次请求后运行,前提是没有未处理的异常就抛出
    4. teardown_request:在每次请求后运行
@app.route('/')
def hello_world():
    print '这里是视图主逻辑代码'
    return 'hello world'


# before_first_request:在处理第一个请求之前运行,如:连接数据库操作
@app.before_first_request
def before_first_request():
    print 'before_first_request'


# before_request:在每次请求前运行,如:对数据进行校验的操作,如果有问题,直接返回,不用再执行匹配的视图函数
@app.before_request
def before_request():
    print 'before_request'


# after_request:在每次请求结束后执行(没有抛出异常),如:去拼接响应头信息,让所有的json数据,统一增加Content-Type:application/json属性
@app.after_request
def after_request(response):
    print 'after_request'
    response.headers['Content-Type'] = 'application/json'
    return response


#  teardown_request:在每次请求结束后执行(即使抛出异常),可以捕获到相应的异常
@app.teardown_request
def teardown_request(error):  # 参数为捕获到的异常信息
    print 'tearddown_request %s'% error

执行结果:

这里写图片描述

状态保持

  • 让浏览器的请求状态可以保持,不用每次都重新输入数据
  • 为什么需要状态保持,因为http是一种无状态协议,不会保持某一次请求所产生的信息,即没有记忆能力,每一次请求都是独立的。但是需要保持请求的状态信息。
  • 状态保持的方式:
    1. cookie:数据存储在客户端,节省服务器空间,缺点是不安全
    2. session:会话,数据存储在服务器上

设置cookie

  • 需要导入make_response方法,将return返回的数据当做参数传给这个方法并且使用set_cookie方法进行cookie的设置
from flask imoprt Flask,make_response
@app.route('/cookie')
def set_cookie():
    resp = make_response('this is to set cookie')
    resp.set_cookie('username', 'itcast')
    return resp

这里写图片描述

获取cookie

  • 导入request对象,使用request.cookies.get()方法进行获取
from flask import Flask,request
#获取cookie
@app.route('/request')
def resp_cookie():
    resp = request.cookies.get('username')
    return resp

这里写图片描述

设置session

  • flask的session与其他框架不同的的一点是,它的session是完全保留在客户端浏览器的
  • 往flask的session中写入数据,最终会以json字符串的形式,经过base64编码写入到用户浏览器的cookieli里
  • 为什么要这样做?这样不是不安全吗?

    1. flask的最大特点就是轻巧灵活,这样做更符合作者和flask的气质
    2. session存储前是需要加盐混淆代码,并且进行加密处理的,保证了session在客户端的安全
    3. 考虑到负载均衡的问题,如使用Nginx进行负载均衡,将session存储在服务器端的架构,每次请求,nginx路由的服务器会不同,每次当需要使用session信息时,服务器还需要从统一的redis等数据库获取session信息。而flask处理的方式是直接将整个session的完整信息直接和cookies一起传送过来,不论路由到那一台服务器,都可以直接调用session数据,更为方便
    4. 其实,实际项目中,还是需要将session数据转移到redis中的,Flask-sesssion拓展中可以实现
  • 设置步骤

    1. 设置secret-key
    2. 导入session对象
    3. 使用 session[‘key’] = ‘value’的形式进行设置
  • 读取

    • 使用 session[‘key’]进行读取操作
 # 设置session前需要设置secret_key,session数据会加密保存在浏览器cookie中
app.secret_key = 'michael'# "michael"为加的盐值,是做内容混淆用的,不能告诉别人

# 设置session
@app.route('/set_session')
def set_session():
    session['user_name']= "michael"
    return redirect(url_for("read_session"))

# 读取session
@app.route('/read_session')
def read_session():
    user_name = session['user_name']
    return "user_name:%s"%user_name

上下文(理解)

  • 上下文相当于一个容器,保存了Flask程序运行过程中的一些信息
  • 有两种上下文:请求上下文和应用上下文

请求上下文

  • 能满足请求过程中的信息调用和设置的对象
  • 有request对象和session对象

request对象

  • 封装了Http请求的内容,针对的时http请求,如,user = request.args.get(‘user’),获取的是get请求时的参数
  • 当 app = Flask(name)之后,创建了应用对象app,在每次http请求发生时,WSGI-server调用Flask.call(),在Flask的内部创建了request对象
  • app对象的生命周期大于request对象,一个app存活期间,可能发生多次request请求
  • 最终通过return,redirect,或者render_template生成response对象。返回给客户端

session对象

  • 记录了请求会话中的信息,针对的时用户信息。如,session[‘name’]=user.id,可以记录用户信息。还可以通过session.get(‘name’)来获取用户信息

应用上下文

  • 是请求上下文中的一个对app的代理(local proxy)
  • 主要作用是帮助request获取当前的应用
  • 它是与request同生共灭的
  • 包括 current_app 和 g 变量

current_app

  • 一种应用程序上下文,用来存储应用程序中的变量
  • current_app.name可以当存储在current_app中的name值
  • 可以存储的信息有:
    1. 应用的启动脚本是哪个文件,启动时指定了那些参数
    2. 加载了哪些配置信息,导入了哪些配置
    3. 连接了哪个数据库
    4. 有哪些public的工具类,常量
    5. 应用跑在哪个机器上,IP多少,内存大

g变量

  • 是应用上下文中一个临时的全局变量
  • 可以在一下请求中,在不同的函数(如钩子函数和url匹配的视图函数)之间传递值

@app.route('/')
def hello_world():
    print "这是主逻辑代码 %s"%g
    return '{"code":0,"msg":"执行ok"}'

@app.before_first_request
def before_first_request():
    # g变量可以在一次请求里的多个函数之间传递值
    g.name = "xiaoming"
    print "before_first_request"

@app.before_request
def before_request():
    # request.args.get('xxx'),可以获取request里的数据
    print "before_request %s" %g.name

执行结果

这里写图片描述

请求上下文和应用上下文的区别

  • 请求上下文:保存了客户端和服务器交互的数据
  • 应用上下文:在应用程序运行的过程中,保存的一些配置信息

Flask-script

  • 能够让Flask服务器通过命令行的方式启动的拓展模块
  • 实现步骤
    1. 从flask_script中导入Manager类
    2. 将app作为参数传给Manage方法,完成Manager类与app的关联
    3. 运行的时候使用:manager.run()命令
from flask import Flask
from flask_script import Manager

app = Flask(__name__)
# 把 Manager 类和应用程序实例进行关联
manager = Manager(app)

@app.route('/')
def index():
return '床前明月光'

if __name__ == "__main__":
manager.run()

命令行传参

  • 可以在命令行中给Flask应用传参,通过python hello.py runserver --help来查看可供设置的参数

这里写图片描述

模板使用

  • 模板是一个包含响应文本的文件。
  • Flask是使用jinja2引擎来渲染模板,使用模板语言,动态的设置模板文件中的内容
    1. jinja2:是python下被广泛应用的模板引擎,是python实现的模板语言,是Flask内置的模板语言。
    2. 模板语言:是一种被设计来自动生成文档的简单文本格式。在模板语言中,一般都会把一些变量传给模板,替换模板的特定位置上预先定义好的占位比变量名。如变量代码块:{{‘这里是占位的变量名’}} ;控制代码块:{%‘这里是一些控制语句’%}

渲染模板

  • 使用 render_template 函数进行渲染
  • render_template函数的第一个参数是模板的文件名,后面的参数都是键值对,表示模板中变量对应的真实值
from flask import Flask, render_template
app = Flask(__name__)

@app.route('/')
def hello_world():
    my_list = [1,2,3,4,5,6]
    my_dict = {
        'name':"michael",
        'age':18
    }
    my_dict_list = [
        {'name':'michael',"age":18},
        {'name':'jackson',"age":10}

    return render_template("demo_template.html", my_dict=my_dict,
                           my_list=my_list, my_dict_list=my_dict_list)

过滤器

  • 过滤器本质就是函数
  • 需求:有时候不仅仅是想输出变量的值,还想改变量的显示,甚至是格式化,运算等等。此时需要用到过滤器。

格式:变量名|过滤器

{{variable | filter_name(*args)}}

  • 没有参数可以写成:

{{variable | filter_name}}

过滤器支持链式调用

{{ "hello world" | reverse | upper }}

常见过滤器

字符串操作
  • safe:禁用转义

    <p>{{ '<em>hello</em>' | safe }}</p>

  • capitalize:把变量值的首字母转成大写,其余字母转小写

    <p>{{ 'hello' | capitalize }}</p>

  • lower:把值转成小写

    <p>{{ 'HELLO' | lower }}</p>

  • upper:把值转成大写

<p>{{ 'hello' | upper }}</p>

  • title:把值中的每个单词的首字母都转成大写

<p>{{ 'hello' | title }}</p>

  • reverse:字符串反转

<p>{{ 'olleh' | reverse }}</p>

  • format:格式化输出

<p>{{ '%s is %d' | format('name',17) }}</p>

  • striptags:渲染之前把值中所有的HTML标签都删掉

<p>{{ '<em>hello</em>' | striptags }}</p>

  • truncate: 字符串截断
    <p>{{ 'hello every one' | truncate(9)}}</p>
列表操作
  • first:取第一个元素

<p>{{ [1,2,3,4,5,6] | first }}</p>

  • last:取最后一个元素

<p>{{ [1,2,3,4,5,6] | last }}</p>

  • length:获取列表长度

<p>{{ [1,2,3,4,5,6] | length }}</p>

  • sum:列表求和

<p>{{ [1,2,3,4,5,6] | sum }}</p>

  • sort:列表排序

<p>{{ [6,2,3,1,5,4] | sort }}</p>

语句块过滤
{% filter upper %}
    一大堆文字
{% endfilter %}

自定义过滤器

  • 有时候,系统内置的过滤器不能满足需求,就需要自定义过滤器
  • 方法:

    • 通过add_template_filter方法
    • 通过add_template_filter装饰器(推荐使用)
  • 方式一
    通过调用应用程序实例的add_template_filter方法实现自定义过滤器。该方法第一个参数是函数名,第二个参数是自定义的过滤器名称:

def do_list_reverse(list):
    list.reverse()
    return list
app.add_template_filter(do_list_reverse, 'lsreverse')
  • 方式二
    用装饰器来实现自定义过滤器。装饰器传入的参数是自定义的过滤器名称。
@app.template_filter('lsreverse')
def do_list_reverse(list):
    list.reverse()
    return list 

控制代码块

  • 模板语言的一种用法,用来执行一些控制功能。如遍历,判断等
  • 常用的有{% for x in xx %}{%endfor%}和{%if x ==2 %}{% endif %},也可以配合使用:
{% for num in my_list if num > 3 %}
    {{ num }} <br>
{% endfor %}
  • 在for 循环中可以访问一些特殊变量,如:loop.index(当前迭代的的次数);loop.cycle(循环某几个参数值)

比如:要是我们想知道当前被迭代的元素序号,则可以使用loop变量的index属性,例如

{% for num in my_list %}
    {{ loop.index }} -
    {% if num > 3 %}
        {{ num }}
    {% endif %} <br>
{% endfor %}

假设my_list=[1, 3, 5, 7, 9]会输出这样的结果

1-
2-
3-5
4-7
5-9

cycle函数会在每次循环的时候,返回其参数中的下一个元素,可以拿上面的例子来说明:

{% for num in my_list %}
    {{ loop.cycle('a', 'b') }} -
    {{ num }} <br>
{% endfor %}

会输出这样的结果:

a-
b-
a-5
b-7
a-9
  • for循环中其他的特殊变量
    • loop.index0 当前循环迭代的次数(从 0 开始)
    • loop.revindex 到循环结束需要迭代的次数(从 1 开始)
    • loop.revindex0 到循环结束需要迭代的次数(从 0 开始)
    • loop.first 如果是第一次迭代,为 True 。
    • loop.last 如果是最后一次迭代,为 True 。
    • loop.length 序列中的项目数。

模板代码的复用

  • 有时候模板中有许多重复代码,为了精简代码,提高代码的重用性,就需要对多次重复使用的模板代码进行复用设置。
  • 复用模板的方式有三种:1.继承 2.包含 3.宏

继承

  • 在父模板里挖好坑,子模板继承,填坑

  • 父模板:base.html


{% block top %}
    <h1>这是头部内容</h1>
{% endblock %}


{% block center %}
    这是父类的中间的内容
{% endblock %}


{% block bottom %}
    <h1>这是底部内容</h1>
{% endblock %}
  • 子模板:extends 指令声明继承自拿个父模板
{% extends 'base.html' %}

{% block content %}
    {{ super() }} <br>
    需要填充的内容 <br>
{% endblock content %}
  • 注意点:
    • 不支持多继承
    • 为了便于阅读,在子模板中使用extends时,尽量写在模板的第一行。
    • 不能在一个模板文件中定义多个相同名字的block标签。
    • 当在页面中使用多个block标签时,建议给结束标签起个名字,当多个block嵌套时,阅读性更好。

包含

  • 将另一个模板整个加载到当前模板中
  • 使用include指令

{% include 'hello.html' %}

  • 注意点:
    • 如果包含的模板文件不存在时,程序会抛出TemplateNotFound异常,可以加上 ignore missing 关键字。如果包含的模板文件不存在,会忽略这条include语句。

{% include 'hello.html' ignore missing %}

  • 可以理解成给模板代码抽出函数,需要时调用传参
  • 使用 macro 指令
  • 一个表单的书写,比较麻烦
<form>
    <label>用户名:</label><input type="text" name="username"><br>
    <label>昵称:</label><input type="text" name="nickname"><br>
    <label>身份证:</label><input type="text" name="idcard"><br>
    <label>密码:</label><input type="password" name="password"><br>
    <label>确认密码:</label><input type="password" name="password2"><br>
    <input type="submit" value="提交">
</form>
  • 简单点,定义宏
{% macro input(label="", type="text", name="", value="") %}
    <label>{{ label }}</label><input type="{{ type }}" name="{{ name }}" value="{{ value }}"><br>
{% endmacro %}
  • 将定义的多个宏单独放在一个文件里,如macro.html方便复用
  • 调用宏:
{% import 'macro.html' as func%}

<form>
    {{ func.input("用户名:", name="username") }}
    {{ func.input("昵称::", name="nickname") }}
    {{ func.input("身份证:", name="idcard") }}
    {{ func.input("密码:", type="password", name="password") }}
    {{ func.input("确认密码:", type="password", name="password2") }}
    {{ func.input(type="submit", value="提交") }}
</form>

总结

  • 继承(Block)、包含(include)、宏(Macro)均能实现代码的复用。

    • 继承(Block)的本质是代码替换,一般用来实现多个页面中重复不变的区域。
    • 包含(include)是直接将目标模板文件整个渲染出来。
    • 宏(Macro)的功能类似函数,可以传入参数,需要定义、调用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值