oldboy:s9day114html
参考博客:https://www.cnblogs.com/wupeiqi/articles/7552008.htmlpython
pip install flask
Flask:django
- 短小精悍、可扩展性强的一个Web框架。编程
- 依赖wsgi:werkzurg(安装Flask时,这些依赖也会被自动安装)json
单独使用Werkzurg服务器:flask
from werkzeug.wrappers import Request, Response from werkzeug.serving import run_simple @Request.application def run(request): return Response('Hello World') if __name__ == '__main__': run_simple('localhost', 4000, run)
from flask import Flask # 建立一个Flask实例,参数是为这个实例定义一个名字,通常以__name__来指定 app = Flask(__name__) # 利用装饰器,绑定路由 @app.route('/index') def index(): return "HelloWorld" if __name__ == '__main__': # 开始运行 app.run()
Flask默认使用templates做为模板目录。当咱们要返回html页面时,默认去该文件夹下找模板文件:服务器
from flask import Flask, render_template # 建立一个Flask实例,参数是为这个实例定义一个名字,通常以__name__来指定 app = Flask(__name__) @app.route('/login') def login(): return render_template('login.html') if __name__ == '__main__': # 开始运行 app.run()
在Django中,咱们是在setting配置文件中去修改。而在Flask中,咱们能够以如下方式修改:cookie
# 建立一个Flask实例,参数是为这个实例定义一个名字,通常以__name__来指定 app = Flask(__name__, template_folder='mytempfile')
咱们查看一下Flask类的构造函数:session
def __init__( self, import_name, static_url_path=None, static_folder='static', static_host=None, host_matching=False, subdomain_matching=False, template_folder='templates', instance_path=None, instance_relative_config=False, root_path=None ):
能够看到,默认的模板目录参数就是"templates"。app
在Flask中,默认只容许接收GET请求,若是咱们先接收POST请求,则须要设置。
在视图函数中获取请求数据时,咱们须要导入request模块,而不是经过参数传递request。这是个Django不一样的地方。
from flask import Flask, render_template, request app = Flask(__name__) @app.route('/login', methods=['GET', 'POST']) def login(): # 这里的请求数据和Django中不同,Flask是经过上下文来管理的,request不是参数,而是导入的模块 if request.method == 'GET': pass if request.method == 'POST': pass if __name__ == '__main__': # 开始运行 app.run()
视图函数:
from flask import Flask, render_template, request, redirect app = Flask(__name__) @app.route('/login', methods=['GET', 'POST']) def login(): # 这里的请求数据和Django中不同,Flask是经过上下文来管理的,request不是参数,而是导入的模块 if request.method == 'GET': # request.args对应django中的request.GET # request.args.get('user) return render_template('login.html') if request.method == 'POST': user = request.form.get('user') pwd = request.form.get('pwd') if user == 'leokale' and pwd == '123': return redirect('/index') else: return render_template('login.html', error="用户名或密码错误") # 也能够传多个值(使用字典),但和Django不同的地方是要解包 # return render_template('login.html', **{"error": "用户名或密码错误"}) @app.route('/index') def index(): return '<h2>欢迎登陆</h2>' if __name__ == '__main__': # 开始运行 app.run()
注意几个和django框架不同的地方:
1)须要在装饰器中传入methods参数,用来容许接收哪些请求
2)request.args和request.form分别对应django中的request.GET和request.POST
3)redirect跳转和django基本一致
4)模板渲染回传给页面的参数,能够单独传一个,也可使用字典拆包的形式传入多个(django中参数为字典)
对应html代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Login</title> </head> <body> <h1>登陆页面</h1> <form method="POST"> <input type="text" name="user"/> <input type="password" name="pwd"/> <input type="submit" value="登陆"/>{{error}}
</form> </body> </html>
Flask中静态文件默认放在static目录下,若是要修改,也是修改Flask实例化时的参数。
app = Flask(__name__,static_folder='mystatic')
只须要将目录名和这个参数值对应上便可。
可是注意,咱们在html中写图片请求链接时使用的url默认是和static_folder一致的:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>INDEX</title> </head> <body> <h2>欢迎访问</h2> <img src="/mystatic/111.png"> </body> </html>
但实际上,咱们也能够经过一个参数来修改:
app = Flask(__name__, static_folder='mystatic', static_url_path='/vvvvv')
咱们将其修改成'/vvvvv',此时html中也要修改成'/vvvvv':
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>INDEX</title> </head> <body> <h2>欢迎访问</h2> <img src="/vvvvv/111.png"> </body> </html>
from flask import Flask, render_template, request, redirect, session app = Flask(__name__, static_folder='mystatic', static_url_path='/vvvvv') @app.route('/login', methods=['GET', 'POST']) def login(): # 这里的请求数据和Django中不同,Flask是经过上下文来管理的,request不是参数,而是导入的模块 if request.method == 'GET': # request.args对应django中的request.GET # request.args.get('user) return render_template('login.html') if request.method == 'POST': user = request.form.get('user') pwd = request.form.get('pwd') if user == 'leokale' and pwd == '123': session['user'] = user return redirect('/index') else: return render_template('login.html', error="用户名或密码错误") # 也能够传多个值(使用字典),但和Django不同的地方是要解包 # return render_template('login.html', **{"error": "用户名或密码错误"}) @app.route('/index') def index(): user = session.get('user') if not user: return redirect('/login') return render_template('index.html') if __name__ == '__main__': # 开始运行 app.run()
Flask中session的使用和django很相似,但Flask中的session是导入的模块。
注意,Flask的session默认是保存在加密的cookie中的,能够参照django相关的介绍:http://www.javashuo.com/article/p-vhnywloy-gy.html
这里要使用加密的cookie,又须要设置MD5加盐中的盐,不然会报错:
RuntimeError: The session is unavailable because no secret key was set. Set the secret_key on the application to something unique and secret. 127.0.0.1 - - [20/Jan/2020 00:27:46] "POST /login HTTP/1.1" 500 -
咱们须要在代码中加上加密设置:
app = Flask(__name__, static_folder='mystatic', static_url_path='/vvvvv') app.secret_key = "sndjkfn" # MD5加密的盐
此时就可使用加密cookie来保存session了。
在setting.py文件中有以下代码:
class Foo(object): DEBUG = True TEST = False
咱们若是在其余模块中经过importlib导入Foo类,获取其中的DEBUG和TEST的值:
import importlib # 给定类所处模块 path = 'setting.Foo' # 分割path,获取模块名和类名 (m, c) = path.rsplit('.', maxsplit=1) # 导入模块 m = importlib.import_module(m) # 获取类 cls = getattr(m, c) # 遍历类中的属性,咱们要获取的属性是大写的,因此使用isupper判断 for key in dir(cls): if key.isupper(): print(key, getattr(cls, key))
这样,咱们就使用importlib库动态的导入模块,并获取了其中的Foo类,以及其中的类属性。
Flask的配置文件也是经过这种方式来加载的(不少框架的配置文件都是经过这种方式加载)。
Flask默认的配置信息:
from flask import Flask, request, render_template app = Flask(__name__) # app.config获取全部配置信息 print(app.config) # 能够经过这种方式修改 app.config['DEBUG'] = True
app.config配置内容:
{ 'ENV': 'development', 'DEBUG': False, 'TESTING': False, 'PROPAGATE_EXCEPTIONS': None, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SECRET_KEY': None, 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(days = 31), 'USE_X_SENDFILE': False, 'SERVER_NAME': None, 'APPLICATION_ROOT': '/', 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, 'SESSION_COOKIE_SAMESITE': None, 'SESSION_REFRESH_EACH_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_MAX_AGE_DEFAULT': datetime.timedelta(seconds = 43200), 'TRAP_BAD_REQUEST_ERRORS': None, 'TRAP_HTTP_EXCEPTIONS': False, 'EXPLAIN_TEMPLATE_LOADING': False, 'PREFERRED_URL_SCHEME': 'http', 'JSON_AS_ASCII': True, 'JSON_SORT_KEYS': True, 'JSONIFY_PRETTYPRINT_REGULAR': False, 'JSONIFY_MIMETYPE': 'application/json', 'TEMPLATES_AUTO_RELOAD': None, 'MAX_COOKIE_SIZE': 4093 }
使用如下方式为其指定一个自定义配置文件:
from flask import Flask, request, render_template app = Flask(__name__) # app.config获取DEBUG,默认为False print(app.config['DEBUG']) # 经过from_object绑定一个类做为配置对象,其中DEBUG为True app.config.from_object("setting.Myconfig") # 再次获取DEBUG,为True print(app.config['DEBUG'])
这里使用setting模块中的Myconfig类做为其配置文件。from_object源码就是使用的1.中的方式对setting模块进行导入,而后获取Myconfig中的属性值。
setting模块中,咱们还能够根据需求,使用多个类来区分不一样场景下的配置:
class BaseConfig(object): # DEV和PROD场景下,相同的配置 XXX = False # 开发环境下的配置 class DevConfig(BaseConfig): DEBUG = True TEST = True # 生产环境下的配置 class ProdConfig(BaseConfig): DEBUG = False TEST = False
在使用的时候,咱们只须要根据场景切换便可:
# 开发环境使用DevConfig配置 app.config.from_object("setting.DevConfig") # 生产部署环境使用ProdConfig配置 app.config.from_object("setting.ProdConfig")
除了3.中所描述的,使用模块中的类来做为配置。还有如下几种方式:
1)from_pyfile
app.config.from_pyfile("setting.py")
例如:
# settings.py DEBUG = True TEST = False
2)from_envvar
app.config.from_envvar("FLASK_CONFIG_FILE")
例如在Linux下配置环境变量:
export FLASK_CONFIG_FILE='/path/to/config/file'
环境变量的值为python文件(不必定是.py结尾的文件,但内容要是 DEBUG = True这种Python代码格式)名称,内部调用from_pyfile方法。
3)from_json
app.config.from_json("json文件名称")
JSON文件名称,必须是json格式,由于内部会执行json.loads。
4)from_mapping
app.config.from_mapping({'DEBUG':True})
参数为一个字典。
在Flask中,路由是经过装饰器加到每一个视图函数的:
from flask import Flask, request, render_template app = Flask(__name__) @app.route('/index', methods=['GET', 'POST']) def hello_world(): if request.method == 'GET': return render_template('index.html') if __name__ == '__main__': app.run()
@app.route中的endpoint参数,就至关于django中的name参数,用来反向生成URL。
from flask import Flask, request, render_template, url_for app = Flask(__name__) @app.route('/index', methods=['GET', 'POST'], endpoint='n1') def hello_world(): print(url_for('n1')) # 打印/index if request.method == 'GET': return render_template('index.html') if __name__ == '__main__': app.run()
其中的url_for至关于django中的reverse函数,用来利用endpoint反向生成url。
注意:若是咱们不指定endpoint,则endpoint默认等于视图函数名,这里即"hello_world"。url_for("hello_world")即为"/index"。
Flask的路由中,咱们可使用动态参数。
from flask import Flask, request, render_template app = Flask(__name__) @app.route('/index/<int:nid>', methods=['GET', 'POST']) def hello_world(nid): print(nid) if request.method == 'GET': return render_template('index.html') if __name__ == '__main__': app.run()
Flask是django使用动态路由的方式不一样,在django中,动态路由是这样的:'/index/(?P<nid>\d+)'。
注意:<int:nid>中,int表示动态参数的类型,咱们的视图函数接收的参数nid为整形,若是不写,则默认为字符串。
动态参数的类型除了有int之外,还有如下几种类型:
@app.route('/user/<username>') # 字符串 @app.route('/post/<int:post_id>') # 整数 @app.route('/post/<float:post_id>') # 浮点数 @app.route('/post/<path:path>') # 路径类型 @app.route('/post/<uuid:uuid>') # UUID
在使用动态路由的时候,也可使用方向生成url:
url_for("hello_world",nid=777) # 生成/index/777
在Flask中,视图函数没有request参数。Flask中的request是直接导入使用的(上下文管理)。
from flask import Flask, request
在视图函数中,能够直接使用request:
#例如请求: http://127.0.0.1:5000/index/2019-03?name=leo #如下request的属性值为: request.method # 请求类型 request.args # GET请求的数据 request.args.get('user') request.form # POST请求的数据 request.form.get('user') request.values request.cookies # Cookie数据 request.headers """ Accept: text/html, application/xhtml+xml, image/jxr, */* ... Connection: Keep-Alive """ request.path # 请求的url "/index/2019-12" request.full_path # "/index/2019-12?name=leo" 包含query request.script_root # "" request.url # "http://127.0.0.1:5000/index/2019-12?name=leo" request.base_url # "http://127.0.0.1:5000/index/2019-12" request.url_root #"http://127.0.0.1:5000/" request.host_url # "http://127.0.0.1:5000/" request.host # "127.0.0.1:5000" request.files obj = request.files['file_name'] obj.save('/var/www/uploads/' + secure_filename(f.filename)) #上传文件
在Flask中,经常使用的响应方式有如下几种:
1)响应字符串
return "HelloWorld" # 至关于django中的HttpResponse
2)响应json数据
from flask import jsonify return jsonify({'k':'v'}) # 或 return json.dumps({'k':'v'})
3)响应渲染模板
from flask import render_template return render_template('login.html') # 至关于django中的render
4)响应重定向
from flask import redirect return redirect('/index')
在Flask中如何设置响应头,特别是响应字符串的时候,没法设置响应头。
from flask import make_response # 使用响应对象来封装要返回的字符串 obj=make_response("hello world") # obj=make_response(render_template('index.html')) # 设置响应头的字段xxx obj.headers['xxx']='123' # 设置cookie obj.set_cookie('key','value') # 返回obj return obj
Jinjia2模板语言和django自带的模板语言很类似,但功能更增强大。语法比较接近于Python。
视图函数代码:
from flask import Flask, request, render_template app = Flask(__name__) USER_INFO = { 1: {'name': 'Leo', 'age': 32, 'gender': 'male'}, 2: {'name': 'Jane', 'age': 33, 'gender': 'female'}, 3: {'name': 'Jake', 'age': 12, 'gender': 'male'}, 4: {'name': 'Alex', 'age': 45, 'gender': 'male'}, 5: {'name': 'Lilei', 'age': 77, 'gender': 'female'} } @app.route('/users', methods=['GET', 'POST']) def user_list(): if request.method == 'GET': return render_template('users.html', user_list=USER_INFO) if __name__ == '__main__': app.run()
咱们建立了一个字典,并返回给模板进行渲染。
html模板代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Users</title> </head> <body> <h2>用户列表</h2> <table> <thead> <tr> <th>ID</th> <th>名字</th> <th>岁数</th> <th>性别</th> </tr> </thead> <tbody> {% for k,v in user_list.items() %} <tr> <td>{{ k }}</td> <td>{{ v.name }}</td> <td>{{ v.age }}</td> <td>{{ v.gender }}</td> </tr> {% endfor %} </tbody> </table> </body> </html>
能够看到,使用for循环的方法和django模板语言是同样的。
注意,user_list.items()这个括号必定要有,这是和python不同的地方。
在取值的时候,使用{{ v.name }}这种形式能够获取字典中字段的值。在Jinjia2中,还可使用{{ v.get('name','默认') }}以及{{ v['name'] }}的形式来取值,和Python基本一致。
# 最简单的装饰器 def auth(func): def inner(*args, **kwargs): ret = func(*args, **kwargs) return ret return inner # index函数使用了auth装饰器 @auth def index(): print('index') # index2函数没有使用auth装饰器 def index2(): print('index2') if __name__ == '__main__': print(index.__name__) # 打印inner print(index2.__name__) # 打印index2
能够看到,当一个函数使用了简单装饰器后,打印其函数名,编程了装饰器中的inner。
在这种状况下,会影响咱们使用反向生成url的功能,即url_for(视图函数名)。
# 导入functools中的wraps from functools import wraps def auth(func): # 使用wraps装饰器 @wraps(func) def inner(*args, **kwargs): ret = func(*args, **kwargs) return ret return inner @auth def index(): print('index') if __name__ == '__main__': print(index.__name__) # 打印index
使用了functools的wraps装饰器,实际上运行index函数时,仍是调用的是inner,可是wraps帮咱们将index函数的元信息封装到了inner函数中。
from flask import Flask, request, render_template, redirect, session, url_for app = Flask(__name__) # 使用密码盐(session默认保存在加密cookie中,必须设置密码盐) app.secret_key = 'snjdkfnjsk' USER_INFO = { 1: {'name': 'Leo', 'age': 32, 'gender': 'male'}, 2: {'name': 'Jane', 'age': 33, 'gender': 'female'}, 3: {'name': 'Jake', 'age': 12, 'gender': 'male'}, 4: {'name': 'Alex', 'age': 45, 'gender': 'male'}, 5: {'name': 'Lilei', 'age': 77, 'gender': 'female'} } from functools import wraps # 认证用户是否登陆 def auth(func): @wraps(func) def inner(*args, **kwargs): if not session.get('user'): return redirect(url_for('login')) ret = func(*args, **kwargs) return ret return inner # 登陆页面 @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'GET': return render_template('login.html') user = request.form.get('username') pwd = request.form.get('password') # 用户名密码正确,则将用户名写到session中(默认保存在加密cookie中,默认cookie过时时间为31天) if user == 'leokale' and pwd == '123456': session['user'] = user # 登陆成功,跳转到users页面 return redirect('/users') # 登陆失败,返回login页面,并显示用户名或密码错误 return render_template('login.html', error='用户名或密码错误') # users页面,使用auth装饰器进行登陆验证 @app.route('/users', methods=['GET', 'POST']) @auth def user_list(): if request.method == 'GET': return render_template('users.html', user_list=USER_INFO) if __name__ == '__main__': app.run()
1)实现验证装饰器,必须使用functools.wraps装饰器,保证视图函数名不变
2)在auth装饰器中实现验证登陆功能(检查session中user是否存在)
3)将auth装饰器应用到须要验证的页面
♌