Flask 基础知识总结

  • 装饰器
  • Flask安装
  • Routing
  • HTTP Method
  • 静态和模板
  • Request/Response
  • Error/重定向
  • Flash Message
  • Logger
  • Flask-Script

装饰器

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

参考文档:

  • 中文文档:http://dormousehole.readthedocs.io/en/latest/
  • github开源地址:https://github.com/pallets/flask
  • 最新版本官网:https://palletsprojects.com/p/flask/

验证简单app

from flask import Flask

app = Flask(__name__)


@app.route('/')
def index():
    return 'hello'

if __name__ == '__main__':
    app.run(debug=True)  # 开发的时候打开debug模式

路由 Routing

  • 1、/结尾自动补齐:@app.route('/')
  • 2、多映射:@app.route('/index/')
  • 3、参数变量:@app.route('/profile//')
  • 4、变量类型:@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

HTTP Method

  • GET:获取接口信息
  • HEAD:紧急查看HTTP的头
  • POST:提交数据到服务器
  • PUT:支持幂等性的POST(函数重发还是那一次,不改变)
  • DELETE:删除服务器上的资源
  • OPITIONS:查看支持的方法
    注意:一般的网站只用GET和POST,代表获取和更新,html的form仅仅支持GET和POST
@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>

静态和模板

静态:

  • 默认的目录是:static/templates
  • 文件:css/js/images

模板文件:

  • 默认目录:templates
  • 文件:x.html,x.htm 随意

jinjia2的模板语法

  • {{ 变量/表达式}}
  • {% 语法 %}
  • { # 注释 # }
  • #开头,行语法表达式(line statements),需要在后台文件里面加上对应的语法
  • app.jinja_env.line_statement_prefix = ‘#’

官方文档:
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

request

  • 参数解析
  • cookie
  • http请求字段
  • 文件上传

response

  • 页面内容返回
  • cookie下发
  • http字段设置,headers
@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

重定向和ERROR错误

重定向:状态码含义如下:
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>
  • 当使用http://127.0.0.1:5000/guest访问的时候,页面返回:你来到没有知识的荒原http://127.0.0.1:5000/guest
  • 当使用http://127.0.0.1:5000/admin访问的时候,页面返回:出错啦!
  • 当使用http://127.0.0.1:5000/admin?key=admin访问的时候,页面返回:hello admin

Flash Message

发送

  • 获取接口信息,flash(msg)
    获取
  • get_flashed_messages
    属性
  • 通过session传递消息

目的是向用户反馈信息,先在文件头引入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,并且日志会安照不同的等级输出:

  • http://127.0.0.1:5000/log/info/infomsg/ 访问的时候,infomsg只会在info.txt中出现
  • http://127.0.0.1:5000/log/warn/warnmsg/ 访问的时候,warnmsg会在info.txt和warn.txt中出现
  • http://127.0.0.1:5000/log/error/errormsg/ 访问的时候,errormsg会在info.txt、warn.txt和error.txt中出现

参考文档:

  • https://docs.python.org/3/howto/logging.html#logging-basic-tutorial

但是能不能让格式变得好看一些,文件大小能否限制一下,所以可以设置一下,将上面的代码改造一下:

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

Flask-Script

安装: 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

可以看出其初始可选择用法有:

  • 1 打开shell,在内置的Flask应用中运行Python;
  • 2 运行服务器,以运行Flask的开发服务器

可以选择的参数有:–help 查看帮助信息

功能添加:

  • 为其添加一些命令,并解析该命令:例如添加一个say hello和初始化数据库:
#!/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

你可能感兴趣的:(Python,Flask,Python)