def log(level, *arg, **kvargs):
def inner(func):
"""
* 无名参数,用来传递任意个无名参数,这些参数会以一个Tuple的形式访问
** 有名参数 用来处理传递任意个有名字的参数,这些参数用dict来访问
"""
def wrapper(*args, **kvargs):
print(level, 'before calling ', func.__name__)
print(level, 'args',args,'kvargs', kvargs)
func(*args, **kvargs)
print(level, 'end calling ', func.__name__)
return wrapper
return inner
@log(level = 'INFO') # 直接去调用log装饰函数
def hello(name, age):
print('hello',name,age)
if __name__ == '__main__':
hello(name='toohoo',age=2) #= log(hello(name='toohoo',age=2))
print("---------------------------")
log(hello(name='toohoo',age=2))
测试结果
INFO before calling hello
INFO args () kvargs {'name': 'toohoo', 'age': 2}
hello toohoo 2
INFO end calling hello
---------------------------
INFO before calling hello
INFO args () kvargs {'name': 'toohoo', 'age': 2}
hello toohoo 2
INFO end calling hello
从上面的结果可以看出执行的顺序是从__main__开始调用Hello==>log装饰器==>inner函数==>wrapper函数==>func(就是hello函数)==>最后输出end…
因此可以知道,使用装饰器可以简化操作
使用命令import flask
验证是否安装了flask,没有安装时候可以使用命令安装
sudo pip3 install flask
参考文档:
验证简单app
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'hello'
if __name__ == '__main__':
app.run(debug=True) # 开发的时候打开debug模式
/
结尾自动补齐:@app.route('/')
@app.route('/index/')
@app.route('/profile//')
@app.route('/profile//')
1、/
结尾自动补齐和多映射:当使用浏览器访问的时候
使用127.0.0.1:5000和127.0.0.1:5000/都是一样的,/会自动补齐。
多映射就是可以设置多个映射路由,如下面的代码:
from flask import Flask
app = Flask(__name__)
@app.route('/index/')
@app.route('/')
def index():
return 'hello'
if __name__ == '__main__':
app.run(debug=True)
使用127.0.0.1:5000和127.0.0.1:5000/和127.0.0.1:5000/index都是可以访问的。
2、参数变量:@app.route('/profile/
@app.route('/profile/' )
def profile(uid):
return 'haha' + uid
浏览器中请求的url之后加上一个uid,页面会返回一个对应的uid,不限类型。
3、变量类型:@app.route('/profile/
@app.route('/profile/' )
def profile(uid):
return 'haha' + str(uid)
这个就必须是int类型,否则浏览器会显示Not Found
@app.route('/profile/' ,methods=['GET', 'post'])
def profile(uid):
return 'haha' + str(uid)
可以使用浏览器插件postman实现进行请求,当使用post请求Get的时候会返回405
<title>405 Method Not Allowedtitle>
<h1>Method Not Allowedh1>
<p>The method is not allowed for the requested URL.p>
静态:
模板文件:
jinjia2的模板语法
#
开头,行语法表达式(line statements),需要在后台文件里面加上对应的语法官方文档:
http://jinja.pocoo.org/docs/dev/templates/
http://docs.jinkan.org/docs/jinja2/
例如有后台的数据:
@app.route('/profile/' ,methods=['GET', 'post'])
def profile(uid):
colors = ('red','green','red','green')
infos = {'toohoo':'abc','zhihu':'def'}
return render_template('profile.html',uid=uid,colors=colors,infos=infos)
for 循环
以花括号和双百分号对称组合形成有效的表达逻辑
profile: {{ uid }} <br>
{# you can't see me ~ #}
{% for i in range(0,5):%}
profile: {{ i }}<br>
{% endfor %}
<ul>
# for color in colors:
<li>{{ color }}li>
# endfor
ul>
<br>
对于内嵌循环序号loop
{% for color in colors: %}
{{ loop.index }} {{ color }} <br>
{% endfor %}
loop.index
是从1开始的;
loop.index0
是从0开始的;
loop.first
判断是否是第一位,是就为True,其余的为False
loop.cycle
循环输出所有的值
filters
{% filter upper %}
{% for k,v in infos.items(): %}
{{ k }}, {{ v }} <br>
{% endfor %}
{% endfilter %}
将小写转成大写:
TOOHOO, ABC
ZHIHU, DEF
call/macro宏调用
{% macro render_color(color) %}
<div>This is color {{ color }}div>{{ caller() }}
{% endmacro %}
{% for color in colors: %}
{% call render_color(color) %}
render_color_demo
{% endcall %}
{% endfor %}
显示的结果为:
his is color red
render_color_demo
This is color green
render_color_demo
This is color red
render_color_demo
This is color green
render_color_demo
模板继承:
include,extend
例如孩子继承父亲,用base.html和child.html两个文件,则他们的继承关系可以使用incline和extend来达成:
base.html
base.html
<html lang="en">
<head>
{% block head %}
<link rel="stylesheet" href="style.css" />
<title>{% block title %}{% endblock %} - My Webpagetitle>
{% endblock %}
head>
<body>
<div id="content">{% block content %}{% endblock %}div>
<div id="footer">
{% block footer %}
© Copyright 2008 by <a href="http://domain.invalid/">youa>.
{% endblock %}
div>
body>
html>
child.html
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
{{ super() }}
<style type="text/css">
.important { color: #336699; }
style>
{% endblock %}
{% block content %}
<h1>Indexh1>
<p class="important">
Welcome to my awesome homepage.
p>
{% endblock %}
可以参考对应的项目:中base.html和index.html等文件写法:
https://github.com/too-hoo/myinstagram/tree/master/myinstagram/templates
request
response
@app.route('/request')
def request_demo():
key = request.args.get('key', 'defaultkey')
res = request.args.get('key', 'defaultkey') + '
'
res = res + request.url + '++' + request.path + '
'
for property in dir(request): # 遍历request里面的方法使用haha字符串连接起来
res = res + str(property) + '
haha' + str(eval('request.' + property)) + '
haha'
response = make_response(res)
response.set_cookie('toohooid', key) # 这是名字为toohooid的键为默认的值defaultkey的cookie
response.status = '404' # 使用F12 开发者工具可以查看状态
response.headers['toohoo'] = 'hello~~' # 在头部添加一个键值对
return response
重定向:状态码含义如下:
301:永久转移:Status Code: 301 MOVED PERMANENTLY (from disk cache)
302:临时转移:Status Code: 302 FOUND
@app.route('/newpath')
def newpath():
return 'newpath' # 解析路径并返回路径
@app.route('/re/' ) # 重定向例子
def redirect_demo(code):
return redirect('/newpath', code=code)
可以使用浏览器分别使用301和302进行验证
Error和异常处理
@app.errorhandler(400)
def exception_page(e):
response = make_response('出错啦~')
return response
@app.errorhandler(404)
def page_not_found(error):
return render_template('not_found.html', url=request.url), 404
@app.route('/admin')
def admin():
if request.args['key'] == 'admin':
return 'hello admin'
else: # 不带参数的时候会走这里,这里抛出的异常会被exception_page,400,捕捉
raise Exception()
not_found.html的内容为:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Not Foundtitle>
head>
<body>
<h2>你来到没有知识的荒原{{ url }}h2>
body>
html>
发送
目的是向用户反馈信息,先在文件头引入secretkey来保证一致性
# 在进行flash显示的时候要先设置一个secretkey保证统一
app.secret_key = 'toohoo'
@app.route('/index/')
@app.route('/')
def index():
res = ''
for msg, category in get_flashed_messages(with_categories=True):
res = res + category + msg + '
haha'
res += 'hello'
return res
#flash处理
@app.route('/login')
def login():
app.logger.info('login success')
flash('登录成功', 'info.txt')
# return 'ok'
return redirect('/') 跳转到首页
输出的内容为:
登录成功info.txt
haha登录成功info.txt
hahahello
debug的终极方案:按照不同的等级,将日志打印出来:(这里有个版本坑注意点,往后看)
import logging
from logging.handlers import RotatingFileHandler
@app.route('/log///' )
def log(level, msg):
dict = {'warn': logging.WARN, 'error': logging.ERROR, 'info': logging.INFO}
if level in dict.keys():
app.logger.log(dict[level], msg)
return 'logged:' + msg
def set_logger():
info_file_handler = RotatingFileHandler('./logs/info.txt')
info_file_handler.setLevel(logging.INFO)
app.logger.addHandler(info_file_handler)
warn_file_handler = RotatingFileHandler('./logs/warn.txt')
warn_file_handler.setLevel(logging.WARN)
app.logger.addHandler(warn_file_handler)
error_file_handler = RotatingFileHandler('./logs/error.txt')
error_file_handler.setLevel(logging.ERROR)
app.logger.addHandler(error_file_handler)
if __name__ == '__main__':
set_logger()
app.run(debug=True)
上面的是python2.7版本的日志输出的最佳简单实践了,但是到Python3.6和Python3.7的时候,当使用上面的配置文件的时候,使用浏览器访问路径http://127.0.0.1:5000/log/info/infomsg/
的时候,info.txt文件是死活不肯输出infomsg,原来是升级改版了,需要在文件中set_logger函数前面加上logging.basicConfig(level=logging.DEBUG)
,下面是整合的代码:
def set_logger():
logging.basicConfig(level=logging.DEBUG)
info_file_handler = RotatingFileHandler('./logs/info.txt')
info_file_handler.setLevel(logging.INFO)
app.logger.addHandler(info_file_handler)
warn_file_handler = RotatingFileHandler('./logs/warn.txt')
warn_file_handler.setLevel(logging.WARN)
app.logger.addHandler(warn_file_handler)
error_file_handler = RotatingFileHandler('./logs/error.txt')
error_file_handler.setLevel(logging.ERROR)
app.logger.addHandler(error_file_handler)
这样就可以在info.txt
中输出infomsg
,并且日志会安照不同的等级输出:
参考文档:
但是能不能让格式变得好看一些,文件大小能否限制一下,所以可以设置一下,将上面的代码改造一下:
def set_logger():
logging.basicConfig(level=logging.DEBUG)
formatter = logging.Formatter('%(asctime)s %(levelname)s %(filename)s %(lineno)d %(message)s')
info_log_handler = RotatingFileHandler(filename='./logs/info.txt', maxBytes=1024*1024, backupCount=10)
info_log_handler.setLevel(logging.INFO)
info_log_handler.setFormatter(formatter)
app.logger.addHandler(info_log_handler)
warn_log_handler = RotatingFileHandler(filename='./logs/warn.txt', maxBytes=1024 * 1024, backupCount=10)
warn_log_handler.setLevel(logging.WARN)
warn_log_handler.setFormatter(formatter)
app.logger.addHandler(warn_log_handler)
error_log_handler = RotatingFileHandler(filename='./logs/error.txt', maxBytes=1024 * 1024, backupCount=10)
error_log_handler.setLevel(logging.ERROR)
error_log_handler.setFormatter(formatter)
app.logger.addHandler(error_log_handler)
输出的结果如下:
2019-08-14 11:57:21,269 INFO hello.py 91 infomsg
2019-08-14 11:57:49,555 WARNING hello.py 91 warnmsg
2019-08-14 11:58:09,429 ERROR hello.py 91 errormsg
安装: pip3 install flask-script
官网地址为:https://flask-script.readthedocs.io/en/latest/
注: 维护者在新版本的Flask已经内置了一个CLI工具,此工具用的仅仅作为辅助,使用内置的或许更加适合。
通过脚本的方式将项目运行起来,类似于cli程序,辅助工程做一些周边的工作,使得大项目开发和运行变得更加简单,简单实践如下:
#!/usr/bin/env python3
# -*-encoding:UTF-8-*-
from flask_script import Manager
from hello import app
manager = Manager(app)
if __name__ == '__main__':
manager.run()
其在当在控制台运行python3 manger.py的时候会输出如下信息:
usage: manager.py [-?] {shell,runserver} ...
positional arguments:
{shell,runserver}
shell Runs a Python shell inside Flask application context.
runserver Runs the Flask development server i.e. app.run()
optional arguments:
-?, --help show this help message and exit
可以看出其初始可选择用法有:
可以选择的参数有:–help 查看帮助信息
功能添加:
#!/usr/bin/env python3
# -*-encoding:UTF-8-*-
from flask_script import Manager
from hello import app
manager = Manager(app)
@manager.option('-n', '--name', dest='name', default='toohoo')
def hello(name):
'say hello'
print('hello', name)
@manager.command
def init_database():
'初始化数据库'
print('init database...')
if __name__ == '__main__':
manager.run()
再次运行会出现如下信息:
usage: manager.py [-?] {hello,init_database,shell,runserver} ...
positional arguments:
{hello,init_database,shell,runserver}
hello say hello
init_database 初始化数据库
shell Runs a Python shell inside Flask application context.
runserver Runs the Flask development server i.e. app.run()
optional arguments:
-?, --help show this help message and exit
可见功能明显增加:
统一使用的命令为:python3 manager.py + 对应的命令参数
本总结参考代码地址为:https://github.com/too-hoo/PythonFoundation