1. HTTP通信与Web框架
1.1 流程
客户端将请求打包成HTTP的请求报文(HTTP协议格式的请求数据)
采用TCP传输发送给服务器端
服务器接收到请求报文后按照HTTP协议进行解析
服务器根据解析后获知的客户端请求进行逻辑执行
服务器将执行后的结果封装成HTTP的响应报文(HTTP协议格式的响应数据)
采用刚才的TCP连接将响应报文发送给客户端
客户端按照HTTP协议解析响应报文获取结果数据
1.2 细节
客户端不一定是浏览器,也可以是PC软件、手机APP、程序
根据服务器端的工作,将其分为两部分:
服务器:与客户端进行tcp通信,接收、解析、打包、发送http格式数据
业务程序:根据解析后的请求数据执行逻辑处理,形成要返回的数据交给服务器
服务器与Python业务程序的配合使用WSGI协议
1.3 Web框架
能够被服务器调用起来,根据客户端的不同请求执行不同的逻辑处理形成要返回的数据的 程序
核心:实现路由和视图(业务逻辑处理)
1.4 框架的轻重
重量级的框架:为方便业务程序的开发,提供了丰富的工具、组件,如Django
轻量级的框架:只提供Web框架的核心功能,自由、灵活、高度定制,如Flask、Tornado
1.5 明确Web开发的任务
视图开发:根据客户端请求实现业务逻辑(视图)编写
模板、数据库等其他的都是为了帮助视图开发,不是必备的
2. 认识Flask
2.1 简介
Flask诞生于2010年,是Armin ronacher(人名)用Python语言基于Werkzeug工具箱编写的轻量级Web开发框架。它主要面向需求简单的小应用。
Flask本身相当于一个内核,其他几乎所有的功能都要用到扩展(邮件扩展Flask-Mail,用户认证Flask-Login),都需要用第三方的扩展来实现。比如可以用Flask-extension加入ORM、窗体验证工具,文件上传、身份验证等。Flask没有默认使用的数据库,你可以选择MySQL,也可以用NoSQL。其 WSGI 工具箱采用 Werkzeug(路由模块) ,模板引擎则使用 Jinja2 。
可以说Flask框架的核心就是Werkzeug和Jinja2。
Python最出名的框架要数Django,此外还有Flask、Tornado等框架。虽然Flask不是最出名的框架,但是Flask应该算是最灵活的框架之一,这也是Flask受到广大开发者喜爱的原因。
2.2 与Django对比
django提供了:
django-admin快速创建项目工程目录
manage.py 管理项目工程
orm模型(数据库抽象层)
admin后台管理站点
缓存机制
文件存储系统
用户认证系统
而这些,flask都没有,都需要扩展包来提供
2.3 Flask扩展包:
Flask-SQLalchemy:操作数据库;
Flask-migrate:管理迁移数据库;
Flask-Mail:邮件;
Flask-WTF:表单;
Flask-script:插入脚本;
Flask-Login:认证用户状态;
Flask-RESTful:开发REST API的工具;
Flask-Bootstrap:集成前端Twitter Bootstrap框架;
Flask-Moment:本地化日期和时间;
3. 创建虚拟环境 见(Linux下python相关设置笔记)
https://blog.csdn.net/weixin_42569562/article/details/84203979
虚拟环境是一个互相隔离的目录
-
mkvirtualenv flask_py2
-
pip install flask==0.10.1
pip freeze > requirements.txt 把已安装的工具包搜集到txt文件
pip install –r requirements.txt 安装文件中包含的包
4. Flask的Hello world程序
# coding:utf-8 # 导入Flask类 from flask import Flask #Flask类接收一个参数__name__ app = Flask(__name__) # 装饰器的作用是将路由映射到视图函数index @app.route('/') def index(): return 'Hello World' # Flask应用程序实例的run方法启动WEB服务器 if __name__ == '__main__': app.run()
# coding:utf-8 from flask import Flask, current_app # import demo # 创建flask的应用对象 # __name__表示当前的模块名字 # 模块名,flask以这个模块所在的目录为总目录,默认这个目录中的static为静态目录,templates为模板目录 app = Flask(__name__, static_url_path="/python", # 访问静态资源的url前缀, 默认值是static static_folder="static", # 静态文件的目录,默认就是static template_folder="templates", # 模板文件的目录,默认是templates ) # app = Flask("__main__") # app = Flask("abcdefg") # 配置参数的使用方式 # 1. 使用配置文件 # app.config.from_pyfile("config.cfg") # 2. 使用对象配置参数 class Config(object): DEBUG = True ITCAST = "python" app.config.from_object(Config) # # 3. 直接操作config的字典对象 # app.config["DEBUG"] = True @app.route("/") def index(): """定义的视图函数""" # a = 1 / 0 # 读取配置参数 # 1. 直接从全局对象app的config字典中取值 # print(app.config.get("ITCAST")) # 2. 通过current_app获取参数 print(current_app.config.get("ITCAST")) return "hello flask" if __name__ == '__main__': # 启动flask程序 # app.run() app.run(host="0.0.0.0", port=5000, debug=True)
4.1 Flask创建app对象
4.1.1 初始化参数
import_name: static_url_path: static_folder: 默认‘static’ template_folder: 默认‘templates’
4.1.2 配置参数
app.config.from_pyfile(“yourconfig.cfg”) 或 app.config.from_object()
4.1.3 在视图读取配置参数
app.config.get() 或者 current_app.config.get()
4.1.4 app.run的参数`
app.run(host=”0.0.0.0”, port=5000)
4.2 路由
4.2.1 app.url_map 查看所有路由
4.2.2 同一路由装饰多个视图函数
4.2.3 同一视图多个路由装饰器
4.2.4 利用methods限制访问方式
@app.route('/sample', methods=['GET', 'POST'])
4.2.5 使用url_for进行反解析
# coding:utf-8 from flask import Flask, current_app, redirect, url_for # import demo # 创建flask的应用对象 # __name__表示当前的模块名字 # 模块名,flask以这个模块所在的目录为总目录,默认这个目录中的static为静态目录,templates为模板目录 app = Flask(__name__) @app.route("/index") def index(): """定义的视图函数""" return "hello flask" # 通过methods限定访问方式 @app.route("/post_only", methods=["GET", "POST"]) def post_only(): return "post only page" @app.route("/hello", methods=["POST"]) def hello(): return "hello 1" @app.route("/hello", methods=["GET"]) def hello2(): return "hello 2" @app.route("/hi1") @app.route("/hi2") def hi(): return "hi page" @app.route("/login") def login(): # url = "/" # 使用url_for的函数,通过视图函数的名字找到视图对应的url路径 url = url_for("index") return redirect(url) @app.route("/register") def register(): # url = "/" url = url_for("index") return redirect(url) if __name__ == '__main__': # 通过url_map可以查看整个flask中的路由信息 print(app.url_map) # 启动flask程序 app.run(host = "0.0.0.0",port = 5000,=True)
4.2.6 动态路由
# 路由传递的参数默认当做string处理,这里指定int,尖括号中冒号后面的内容是动态的 @app.route('/user/<int:id>') def hello_itcast(id): return 'hello itcast %d' %id
4.2.7自定义转换器
from flask import Flask from werkzeug.routing import BaseConverter class Regex_url(BaseConverter): def __init__(self,url_map,*args): super(Regex_url,self).__init__(url_map) self.regex = args[0] app = Flask(__name__) app.url_map.converters['re'] = Regex_url @app.route('/user/<re("[a-z]{3}"):id>') def hello_itcast(id): return 'hello %s' %id
# coding:utf-8 from flask import Flask, current_app, redirect, url_for from werkzeug.routing import BaseConverter # 创建flask的应用对象 # __name__表示当前的模块名字 # 模块名,flask以这个模块所在的目录为总目录,默认这个目录中的static为静态目录,templates为模板目录 app = Flask(__name__) # 转换器 # 127.0.0.1:5000/goods/123 # @app.route("/goods/<int:goods_id>") @app.route("/goods/<goods_id>") # 不加转换器类型, 默认是普通字符串规则(除了/的字符) def goods_detail(goods_id): """定义的视图函数""" return "goods detail page %s" % goods_id # 1. 定义自己的转换器 class MobileConverter(BaseConverter): def __init__(self, url_map): super(MobileConverter, self).__init__(url_map) self.regex = r'1[34578]\d{9}' class RegexConverter(BaseConverter): """""" def __init__(self, url_map, regex): # 调用父类的初始化方法 super(RegexConverter, self).__init__(url_map) # 将正则表达式的参数保存到对象的属性中,flask会去使用这个属性来进行路由的正则匹配 self.regex = regex def to_python(self, value): """""" print("to_python方法被调用") # return "abc" # value是在路径进行正则表达式匹配的时候提取的参数 return value def to_url(self, value): """使用url_for的方法的时候被调用""" print("to_url方法被调用") # return "15811111111" return value # 2. 将自定义的转换器添加到flask的应用中 app.url_map.converters["re"] = RegexConverter app.url_map.converters["mobile"] = MobileConverter # 127.0.0.1:5000/send/18612345678 # @app.route("/send/<mobile:mobile_num>") @app.route("/send/<re(r'1[34578]\d{9}'):mobile_num>") def send_sms(mobile_num): return "send sms to %s" % mobile_num @app.route("/index") def index(): url = url_for("send_sms", mobile_num="18922222222") # /send/18922222222 return redirect(url) @app.route("/call/<re(r''):tel>") def call_tel(tel): pass if __name__ == '__main__': # 通过url_map可以查看整个flask中的路由信息 print(app.url_map) # 启动flask程序 app.run(host = "0.0.0.0",port = 5000,debug=True)
4.3 获取请求参数
from flask import request
# coding:utf-8 from flask import Flask, request app = Flask(__name__) # 接口 api # 127.0.0.1:5000/index?city=shenzhen&country=china 查询字符串 QueryString @app.route("/index", methods=["GET", "POST"]) def index(): # request中包含了前端发送过来的所有请求数据 # form和data是用来提取请求体数据 # 通过requset.form可以直接提取请求体中的表单格式的数据, 是一个类字典的对象 # 通过get方法只能拿到多个同名参数的第一个 name = request.form.get("name") age = request.form.get("age") name_li = request.form.getlist("name") # 如果是请求体的数据不是表单格式的(如json格式),可以通过request.data获取 print("request.data: %s" % request.data) # args是用来提取url中的参数(查询字符串) city = request.args.get("city") return "hello name=%s, age=%s, city=%s, name_li=%s" % (name, age, city, name_li) # def register(): # if request.method == 'GET': # return render(request, "register.html") # else: # if __name__ == '__main__': app.run(debug=True)
4.3.1 上传文件
已上传的文件存储在内存或是文件系统中一个临时的位置。你可以通过请求对象的 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))
代码演示
# coding:utf-8 from flask import Flask, request app = Flask(__name__) @app.route("/upload", methods=["POST"]) def upload(): """接受前端传送过来的文件""" file_obj = request.files.get("pic") if file_obj is None: # 表示没有发送文件 return "未上传文件" # 将文件保存到本地 # # 1. 创建一个文件 # f = open("./demo.png", "wb") # # 2. 向文件写内容 # data = file_obj.read() # f.write(data) # # 3. 关闭文件 # f.close() # 直接使用上传的文件对象保存 file_obj.save("./demo1.png") return "上传成功" if __name__ == '__main__': app.run(debug=True)
上下文管理
# coding:utf-8 # f = open("./1.txt", "wb") # # 2. 向文件写内容 # try: # f.write("hello flask") # except Exception: # pass # finally: # # 3. 关闭文件 # f.close() # 上下文管理器 # with open("./1.txt", "wb") as f: # f.write("hello flask") # f.write("hello flask") # f.write("hello flask") # f.write("hello flask") class Foo(object): def __enter__(self): """进入with语句的时候被with调用""" print("enter called") def __exit__(self, exc_type, exc_val, exc_tb): """离开with语句的时候被with调用""" print("exit called") print("exc_type: %s" % exc_type) print("exc_val: %s" % exc_val) print("exc_tb: %s" % exc_tb) with Foo() as foo: print("hello python") a = 1 / 0 print("hello end") # 进入with语句的时候,with帮助我们调用对象的__enter__方法, # 离开with语句的时候,with帮助我们调用对象的__exit__方法
4.4 abort函数与自定义异常处理
4.4.1 abort函数
from flask import abort
4.4.2 自定义异常处理
@app.errorhandler(404) def error(e): return '您请求的页面不存在了,请确认后再次访问!%s'%e
代码演示
# coding:utf-8 from flask import Flask, request, abort, Response app = Flask(__name__) @app.route("/login", methods=["GET"]) def login(): # name = request.form.get() # pwd = request.form.get() name = "" pwd = "" if name != "zhangsan" or pwd != "admin": # 使用abort函数可以立即终止视图函数的执行 # 并可以返回给前端特定的信息 # 1 传递状态码信息, 必须是标准的http状态码 abort(404) # # 2. 传递响应体信息 # resp = Response("login failed") # abort(resp) return "login success" # 定义错误处理的方法 @app.errorhandler(404) def handle_404_error(err): """自定义的处理错误方法""" # 这个函数的返回值会是前端用户看到的最终结果 return u"出现了404错误, 错误信息:%s" % err if __name__ == '__main__': app.run(debug=True)
4.5 返回的响应数据
4.5.1 元组
可以返回一个元组,这样的元组必须是 (response, status, headers) 的形式,且至少包含一个元素。 status 值会覆盖状态代码, headers 可以是一个列表或字典,作为额外的消息标头值。
4.5.2 make_response
resp = make_response() resp.headers[“sample”] = “value” resp.status = “404 not found”
# coding:utf-8 from flask import Flask, request, abort, Response, make_response app = Flask(__name__) @app.route("/index") def index(): # 1 使用元祖,返回自定义的响应信息 # 响应体 状态码 响应头 # return "index page", 400, [("Itcast", "pyton"), ("City", "shenzhen")] # return "index page", 400, {"Itcast1": "python1", "City1": "sz1"} # return "index page", 666, {"Itcast1": "python1", "City1": "sz1"} # return "index page", "666 itcast status", {"Itcast1": "python1", "City1": "sz1"} # return "index page", "666 itcast status" # 2 使用make_response 来构造响应信息 resp = make_response("index page 2") resp.status = "999 itcast" # 设置状态码 resp.headers["city"] = "sz" # 设置响应头 return resp if __name__ == '__main__': app.run(debug=True)
4.5.3使用jsonify返回json数据
# coding:utf-8 from flask import Flask, jsonify import json app = Flask(__name__) @app.route("/index") def index(): # json就是字符串 data = { "name": "python", "age": 24 } # # json.dumps(字典) 将python的字典转换为json字符串 # # json.loads(字符串) 将字符串转换为python中的字典 # # json_str = json.dumps(data) # # return json_str, 200, {"Content-Type": "application/json"} # jsonify帮助转为json数据,并设置响应头 Content-Type 为application/json # return jsonify(data) return jsonify(city="sz", country="china") if __name__ == '__main__': app.run(debug=True)
4.5.4重定向
from flask import redirect
4.6 设置和读取cookie
make_response 模块名
set_cookie(key, value=’’, max_age=None) 设置cookie
delete_cookie(key) 删除cookie
# coding:utf-8 from flask import Flask, make_response, request app = Flask(__name__) @app.route("/set_cookie") def set_cookie(): resp = make_response("success") # 设置cookie, 默认有效期是临时cookie,浏览器关闭就失效 resp.set_cookie("Itcast", "Python") resp.set_cookie("Itcast1", "Python1") # max_age设置有效期,单位:秒 resp.set_cookie("Itcast2", "Python1", max_age=3600) resp.headers["Set-Cookie"] = "Itcast3=Python3; Expires=Sat, 18-Nov-2017 04:36:04 GMT; Max-Age=3600; Path=/" return resp @app.route("/get_cookie") def get_cookie(): c = request.cookies.get("Itcast") return c @app.route("/delete_cookie") def delete_cookie(): resp = make_response("del success") # 删除cookie resp.delete_cookie("Itcast1") return resp if __name__ == '__main__': app.run(debug=True)
4.7 session
from flask import session
需要设置secret_key
# coding:utf-8 from flask import Flask, session, current_app app = Flask(__name__) # flask的session需要用到的秘钥字符串 app.config["SECRET_KEY"] = "dhsodfhisfhosdhf29fy989" # flask默认把session保存到了cookie中 @app.route("/login") def login(): # 设置session数据 session["name"] = "python" session["mobile"] = "18611111111" return "login success" @app.route("/index") def index(): # 获取session数据 name = session.get("name") return "hello %s" % name if __name__ == '__main__': app.run(debug=True)
4.8 请求上下文与应用上下文
请求上下文(request context)
request和session都属于请求上下文对象。
应用上下文(application context)
current_app和g都属于应用上下文对象。
current_app:表示当前运行程序文件的程序实例。
g:处理请求时,用于临时存储的对象,每次请求都会重设这个变量。
4.9 请求钩子
请求钩子是通过装饰器的形式实现,Flask支持如下四种请求钩子:
before_first_request:在处理第一个请求前运行。
@app.before_first_request
before_request:在每次请求前运行。
after_request(response):如果没有未处理的异常抛出,在每次请求后运行。
teardown_request(response):在每次请求后运行,即使有未处理的异常抛出。
# coding:utf-8 from flask import Flask, session, current_app, request, url_for app = Flask(__name__) @app.route("/index") def index(): print("index 被执行") a = 1 / 0 return "index page" @app.route("/hello") def hello(): print("hello 被执行") return "hello page" @app.before_first_request def handle_before_first_request(): """在第一次请求处理之前先被执行""" print("handle_before_first_request 被执行") @app.before_request def handle_before_request(): """在每次请求之前都被执行""" print("handle_before_request 被执行") @app.after_request def handle_after_request(response): """在每次请求(视图函数处理)之后都被执行, 前提是视图函数没有出现异常""" print("handle_after_request 被执行") return response @app.teardown_request def handle_teardown_request(response): """在每次请求 (视图函数处理)之后都被执行, 无论视图函数是否出现异常,都被执行, 工作在非调试模式时 debug=False""" path = request.path if path == url_for("index"): print("在请求钩子中判断请求的视图逻辑: index") elif path == url_for("hello"): print("在请求钩子中判断请求的视图逻辑: hello") print("handle_teardown_request 被执行") return response if __name__ == '__main__': app.run()
5. Flask-Script扩展命令行
pip install Flask-Script
from flask import Flask from flask_script import Manager app = Flask(__name__) manager = Manager(app) @app.route('/') def index(): • return '床前明月光' if __name__ == "__main__": • manager.run()
# coding:utf-8 from flask import Flask from flask_script import Manager # 启动命令的管理类 app = Flask(__name__) # 创建Manager管理类的对象 manager = Manager(app) @app.route("/index") def index(): return "index page" if __name__ == '__main__': # app.run(debug=True) # 通过管理对象来启动flask manager.run()
6. Jinja2模板
6.1 基本流程
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Template</title> </head> <body> <h1>hello {{ name }}</h1> </body> </html>
@app.route("/") def index(): return render_template("index.html", name="python")
使用flask 中的render_template渲染模板
6.2 变量
<p>{{mydict['key']}}</p> <p>{{mydict.key}}</p> <p>{{mylist[1]}}</p> <p>{{mylist[myvariable]}}</p>
from flask import Flask,render_template app = Flask(__name__) @app.route('/') def index(): mydict = {'key':'silence is gold'} mylist = ['Speech', 'is','silver'] myintvar = 0 return render_template('vars.html', mydict=mydict, mylist=mylist, myintvar=myintvar ) if __name__ == '__main__': app.run(debug=True)
6.3 过滤器
6.3.1 字符串过滤器
**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> **trim****:把值的首尾空格去掉;** <p>{{ ' hello world ' | trim }}</p> **reverse:****字符串反转;** <p>{{ 'olleh' | reverse }}</p> **format:****格式化输出;** <p>{{ '%s is %d' | format('name',17) }}</p> **striptags****:渲染之前把值中所有的HTML****标签都删掉;** <p>{{ '<em>hello</em>' | striptags }}</p> ##### 6.3.2 支持链式使用过滤器 <p>{{ “ hello world “ | trim | upper }}</p>
6.3.3 列表过滤器
**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>
6.3.4 自定义过滤器
自定义的过滤器名称如果和内置的过滤器重名,会覆盖内置的过滤器。
方式一: • 通过 **add_template_filter (****过滤器函数****,** **模板中使用的过滤器名字****)** def filter_double_sort(ls): • return ls[::2] app.add_template_filter(filter_double_sort,'double_2')
方式二: 通过装饰器 **app.template_filter (****模板中使用的装饰器名字)** @app.template_filter('db3') def filter_double_sort(ls): return ls[::-3]
代码演示
1、templates.py
# coding:utf-8 from flask import Flask, render_template app = Flask(__name__) @app.route("/index") def index(): data = { "name": "python", "age": 18, "my_dict": {"city": "sz"}, "my_list": [1, 2, 3, 4, 5], "my_int": 0 } return render_template("index.html", **data) def list_step_2(li): """自定义的过滤器""" return li[::2] # 注册过滤器 app.add_template_filter(list_step_2, "li2") @app.template_filter("li3") def list_step_3(li): """自定义的过滤器""" return li[::3] if __name__ == '__main__': app.run(debug=True)
2、 index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <p>name = {{ name }}</p> <p>age = {{ age }}</p> <p>my_dict: city= {{ my_dict["city"] }}</p> <p>my_dict: city= {{ my_dict.city }}</p> <p>my_list: {{ my_list }}</p> <p>my_list[my_int]: {{ my_list[my_int] }}</p> <p>my_list[0] + my_list[1] = {{ my_list[0] + my_list[1] }}</p> <p>{{"hello" + " python" }}</p> <p>a{{" flask world " | trim | upper }}a</p> <hr/> <p>{{my_list | li2 }}</p> <p>{{my_list | li3 }}</p> </body> </html>
xss.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form method="post"> <textarea name="text"></textarea> <input type="submit" value="提交"> </form> {{ text |safe}} //加上|safe表示禁止转义,可形成xss攻击,在表单中填入脚本提交后执行·表单内脚本 </body> </html>
xss.py
# coding:utf-8 from flask import Flask, render_template, request app = Flask(__name__) @app.route("/xss", methods=["GET", "POST"]) def xss(): text = "" if request.method == "POST": text = request.form.get("text") return render_template("xss.html", text=text) if __name__ == '__main__': app.run(debug=True)
6.4 表单
使用Flask-WTF表单扩展,可以帮助进行CSRF验证,帮助我们快速定义表单模板,而且可以帮助我们在视图中验证表的数据
pip install Flask-WTF
6.4.1 不使用Flask-WTF扩展时,表单需要自己处理
#模板文件 <form method='post'> <input type="text" name="username" placeholder='Username'> <input type="password" name="password" placeholder='password'> <input type="submit"> </form>
from flask import Flask,render_template,request @app.route('/login',methods=['GET','POST']) def login(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] print username,password return “success” else: return render_template(“login.html”)
6.4.2 使用Flask-WTF扩展
需要设置 SECRET_KEY 的配置参数
模板页: <form method="post"> #设置csrf_token {{ form.csrf_token() }} {{ form.us.label }} <p>{{ form.us }}</p> {{ form.ps.label }} <p>{{ form.ps }}</p> {{ form.ps2.label }} <p>{{ form.ps2 }}</p> <p>{{ form.submit() }}</p> {% for x in get_flashed_messages() %} {{ x }} {% endfor %} </form>
rf#coding=utf-8 from flask import Flask,render_template, redirect,url_for,session,request,flash #导入wtf扩展的表单类 from flask_wtf import FlaskForm #导入自定义表单需要的字段 from wtforms import SubmitField,StringField,PasswordField #导入wtf扩展提供的表单验证器 from wtforms.validators import DataRequired,EqualTo app = Flask(__name__) app.config['SECRET_KEY']='1' #自定义表单类,文本字段、密码字段、提交按钮 class Login(Flask Form): us = StringField(label=u'用户:',validators=[DataRequired()]) ps = PasswordField(label=u'密码',validators=[DataRequired(),EqualTo('ps2','err')]) ps2 = PasswordField(label=u'确认密码',validators=[DataRequired()]) submit = SubmitField(u'提交') #定义根路由视图函数,生成表单对象,获取表单数据,进行表单数据验证 @app.route('/',methods=['GET','POST']) def index(): form = Login() if form.validate_on_submit(): name = form.us.data pswd = form.ps.data pswd2 = form.ps2.data print name,pswd,pswd2 return redirect(url_for('login')) else: if request.method=='POST': flash(u'信息有误,请重新输入!') return render_template('index.html',form=form) if __name__ == '__main__': app.run(debug=True)
6.5 控制语句
6.5.1 if语句
{% if %} {% endif %}
@app.route('/user') def user(): user = 'dongGe' return render_template('user.html',user=user)
<html> <head> {% if user %} <title> hello {{user}} </title> {% else %} <title> welcome to flask </title> {% endif %} </head> <body> <h1>hello world</h1> </body> </html>
6.5.2 for语句
{% for item in samples %} {% endfor %}
@app.route('/loop') def loop(): fruit = ['apple','orange','pear','grape'] return render_template('loop.html',fruit=fruit)
<html> <head> {% if user %} <title> hello {{user}} </title> {% else %} <title> welcome to flask </title> {% endif %} </head> <body> <h1>hello world</h1> <ul> {% for index in fruit %} <li>{{ index }}</li> {% endfor %} </ul> </body> </html>
6.6 宏
类似于python中的函数,宏的作用就是在模板中重复利用代码,避免代码冗余。
6.6.1 不带参数宏的定义与使用
定义:** {% macro input() %} <input type="text" • name="username" • value="" • size="30"/> {% endmacro %} 使用 {{ input() }}
6.6.2 带参数宏的定义与使用
定义 {% macro input(name,value='',type='text',size=20) %} <input type="{{ type }}" name="{{ name }}" value="{{ value }}" size="{{ size }}"/> {% endmacro %} 使用 {{ input(value='name',type='password',size=40)}} ### 6.6.3 将宏单独封装在html文件中 文件名可以自定义macro.html {% macro input() %} <input type="text" name="username" placeholde="Username"> <input type="password" name="password" placeholde="Password"> <input type="submit"> {% endmacro %} 在其它模板文件中先导入,再调用 {% import 'macro.html' as func %} {% func.input() %}
定义带参数的宏 {% macro input(name,value='',type='text',size=20) %} <input type="{{ type }}" name="{{ name }}" value="{{ value }}" size="{{ size }}"/> {% endmacro %} 调用宏,并传递参数 {{ input(value='name',type='password',size=40)}} 把宏单独抽取出来,封装成html文件,其它模板中导入使用 文件名可以自定义macro.html {% macro function() %} <input type="text" name="username" placeholde="Username"> <input type="password" name="password" placeholde="Password"> <input type="submit"> {% endmacro %} 在其它模板文件中先导入,再调用 {% import 'macro.html' as func %} {% func.function() %}
6.4 模板继承
extend
6.5 模板包含
includ
模板继承: 模板继承是为了重用模板中的公共内容。一般Web开发中,继承主要使用在网站的顶部菜单、底部。这些内容可以定义在父模板中,子模板直接继承,而不需要重复书写。 {% block top %}``{% endblock %}标签定义的内容,相当于在父模板中挖个坑,当子模板继承父模板时,可以进行填充。 子模板使用extends指令声明这个模板继承自哪?父模板中定义的块在子模板中被重新定义,在子模板中调用父模板的内容可以使用super()。 父模板:base.html {% block top %} 顶部菜单 {% endblock top %} {% block content %} {% endblock content %} {% block bottom %} 底部 {% endblock bottom %} 子模板: {% extends 'base.html' %} {% block content %} 需要填充的内容 {% endblock content %} 模板继承使用时注意点: 不支持多继承。 为了便于阅读,在子模板中使用extends时,尽量写在模板的第一行。 不能在一个模板文件中定义多个相同名字的block标签。 当在页面中使用多个block标签时,建议给结束标签起个名字,当多个block嵌套时,阅读性更好。 包含(Include) Jinja2模板中,除了宏和继承,还支持一种代码重用的功能,叫包含(Include)。它的功能是将另一个模板整个加载到当前模板中,并直接渲染。 示例: include的使用 {\% include 'hello.html' %} 包含在使用时,如果包含的模板文件不存在时,程序会抛出TemplateNotFound异常,可以加上ignore missing关键字。如果包含的模板文件不存在,会忽略这条include语句。 示例: include的使用加上关键字ignore missing {\% include 'hello.html' ignore missing %} 宏、继承、包含: 宏(Macro)、继承(Block)、包含(include)均能实现代码的复用。 继承(Block)的本质是代码替换,一般用来实现多个页面中重复不变的区域。 宏(Macro)的功能类似函数,可以传入参数,需要定义、调用。 包含(include)是直接将目标模板文件整个渲染出来。
6.6 flask在模板中使用特殊变量和方法
6.6.1 config
6.6.2 request
6.6.3 url_for
3.6 Flask中的特殊变量和方法: 在Flask中,有一些特殊的变量和方法是可以在模板文件中直接访问的。 config 对象: config 对象就是Flask的config对象,也就是 app.config 对象。 {{ config.SQLALCHEMY_DATABASE_URI }} request 对象: 就是 Flask 中表示当前请求的 request 对象,request对象中保存了一次HTTP请求的一切信息。 request常用的属性如下: 属性 说明 类型 data 记录请求的数据,并转换为字符串 * form 记录请求中的表单数据 MultiDict args 记录请求中的查询参数 MultiDict cookies 记录请求中的cookie信息 Dict headers 记录请求中的报文头 EnvironHeaders method 记录请求使用的HTTP方法 GET/POST url 记录请求的URL地址 string files 记录请求上传的文件 * {{ request.url }} url_for 方法: url_for() 会返回传入的路由函数对应的URL,所谓路由函数就是被 app.route() 路由装饰器装饰的函数。如果我们定义的路由函数是带有参数的,则可以将这些参数作为命名参数传入。 {{ url_for('index') }} {{ url_for('post', post_id=1024) }} get_flashed_messages方法: 返回之前在Flask中通过 flash() 传入的信息列表。把字符串对象表示的消息加入到一个消息队列中,然后通过调用 get_flashed_messages() 方法取出。 {% for message in get_flashed_messages() %} {{ message }} {% endfor %}