Flask、Django、Tornado框架 区别
1 Django:重武器,内部包含了非常多组件:ORM、Form、ModelForm、缓存、Session、中间件、信号等...
2 Flask:短小精悍,内部没有太多组件。第三方组件非常丰富。 路由比较特殊:基于装饰器来实现,但是究其本质还是通过add_url_rule来实现。
3 Tornado:异步非阻塞框架 ,底层使用的是 IOLoop 使用协程实现的异步非阻塞
wsji
werkzeug示例
from werkzeug.wrappers import Request, Response @Request.application
def hello(request):
return Response('Hello World!') if __name__ == '__main__':
from werkzeug.serving import run_simple
run_simple('localhost', 4000, hello)
wsgiref示例
from wsgiref.simple_server import make_server def runserver(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return [bytes('<h1>Hello, web!</h1>', encoding='utf-8'), ] if __name__ == '__main__':
# obj = WSGIHandler()
httpd = make_server('', 8000, runserver)
httpd.serve_forever()
他们的本质都是基于sokect
import socket def handle_request(client):
buf = client.recv(1024)
client.send("HTTP/1.1 200 OK\r\n\r\n")
client.send("Hello, Seven") def main():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost',8000))
sock.listen(5) while True:
connection, address = sock.accept()
handle_request(connection)
connection.close() if __name__ == '__main__':
main()
安装 Flask
pip3 install flask
创建一个简单的Flask 程序
from flask import Flask # 实例化Flask对象
app = Flask(__name__) # 将 '/' 和 函数index的对应关系添加到路由中。
"""
{
‘/’:index
}
"""
@app.route('/')
def index():
return 'Hello World!' if __name__ == '__main__':
# 监听用户请求
# 如果有用户请求到来,则执行app的__call__方法
app.__call__
app.run()
通过写一个简单的登陆小案例来了解Flask的使用
from flask import Flask,render_template,request,redirect,session,url_for
app = Flask(__name__)
app.debug = True
app.secret_key = 'siuljskdjfs' USERS = {
1:{'name':'张桂坤','age':18,'gender':'男','text':"当眼泪掉下来的时候,是真的累了, 其实人生就是这样: 你有你的烦,我有我的难,人人都有无声的泪,人人都有难言的苦。 忘不了的昨天,忙不完的今天,想不到的明天,走不完的人生,过不完的坎坷,越不过的无奈,听不完的谎言,看不透的人心放不下的牵挂,经历不完的酸甜苦辣,这就是人生,这就是生活。"},
2:{'name':'主城','age':28,'gender':'男','text':"高中的时候有一个同学家里穷,每顿饭都是膜膜加点水,有时候吃点咸菜,我们六科老师每天下课都叫他去办公室回答问题背诵课文,然后说太晚啦一起吃个饭,后来他考上了人大,拿到通知书的时候给每个老师磕了一个头"},
3:{'name':'服城','age':18,'gender':'女','text':"高中的时候有一个同学家里穷,每顿饭都是膜膜加点水,有时候吃点咸菜,我们六科老师每天下课都叫他去办公室回答问题背诵课文,然后说太晚啦一起吃个饭,后来他考上了人大,拿到通知书的时候给每个老师磕了一个头"},
} def wrap(func):
def inner(*args, **kwargs):
user = session.get('user_info')
if not user:
return redirect('/login')
return func()
return inner @app.route('/detail/<int:nid>',methods=['GET'],endpoint='n1') # 登陆后才能访问
@wrap
def detail(nid):
info = USERS.get(nid)
return render_template('detail.html',info=info) @app.route('/index',methods=['GET'],endpoint='n2') # 登陆后才能访问
@wrap
def index():
user = session.get('user_info')
print(user)
if not user:
# return redirect('/login')
url = url_for('l1')
return redirect(url)
return render_template('index.html',user_dict=USERS) @app.route('/login',methods=['GET','POST'],endpoint='l1')
def login():
if request.method == "GET":
return render_template('login.html')
else:
# request.query_string
user = request.form.get('user')
pwd = request.form.get('pwd')
if user == 'zhang' and pwd == '':
session['user_info'] = user
return redirect('http://www.baidu.com')
return render_template('login.html',error='用户名或密码错误') if __name__ == '__main__':
app.run()
创建登陆模板 templates/login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>用户登录</h1>
<form method="post">
<input type="text" name="user">
<input type="text" name="pwd">
<input type="submit" value="登录">{{error}}
</form>
</body>
</html>
创建首页模板 templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>用户列表</h1>
<table>
{% for k,v in user_dict.items() %}
<tr>
<td>{{k}}</td>
<td>{{v.name}}</td>
<td>{{v['name']}}</td>
<td>{{v.get('name')}}</td>
<td><a href="/detail/{{k}}">查看详细</a></td>
</tr>
{% endfor %}
</table>
</body>
</html>
创建详情页模板 templates/detail.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>详细信息 {{info.name}}</h1>
<div> {{info.text}}
</div>
</body>
</html>
登陆设置session
http://127.0.0.1:5000/login
携带session 访问主页
http://127.0.0.1:5000/index
携带session 访问详情页
http://127.0.0.1:5000/detail/1
配置文件
flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为:
{
'DEBUG': get_debug_flag(default=False), 是否开启Debug模式
'TESTING': False, 是否开启测试模式
'PROPAGATE_EXCEPTIONS': None,
'PRESERVE_CONTEXT_ON_EXCEPTION': None,
'SECRET_KEY': None,
'PERMANENT_SESSION_LIFETIME': timedelta(days=31),
'USE_X_SENDFILE': False,
'LOGGER_NAME': None,
'LOGGER_HANDLER_POLICY': 'always',
'SERVER_NAME': None,
'APPLICATION_ROOT': None,
'SESSION_COOKIE_NAME': 'session',
'SESSION_COOKIE_DOMAIN': None,
'SESSION_COOKIE_PATH': None,
'SESSION_COOKIE_HTTPONLY': True,
'SESSION_COOKIE_SECURE': False,
'SESSION_REFRESH_EACH_REQUEST': True,
'MAX_CONTENT_LENGTH': None,
'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12),
'TRAP_BAD_REQUEST_ERRORS': False,
'TRAP_HTTP_EXCEPTIONS': False,
'EXPLAIN_TEMPLATE_LOADING': False,
'PREFERRED_URL_SCHEME': 'http',
'JSON_AS_ASCII': True,
'JSON_SORT_KEYS': True,
'JSONIFY_PRETTYPRINT_REGULAR': True,
'JSONIFY_MIMETYPE': 'application/json',
'TEMPLATES_AUTO_RELOAD': None,
} 方式一:
app.config['DEBUG'] = True PS: 由于Config对象本质上是字典,所以还可以使用app.config.update(...) 方式二:
app.config.from_pyfile("python文件名称")
如:
settings.py
DEBUG = True app.config.from_pyfile("settings.py") app.config.from_envvar("环境变量名称")
环境变量的值为python文件名称名称,内部调用from_pyfile方法 app.config.from_json("json文件名称")
JSON文件名称,必须是json格式,因为内部会执行json.loads app.config.from_mapping({'DEBUG':True})
字典格式 app.config.from_object("python类或类的路径") app.config.from_object('pro_flask.settings.TestingConfig') settings.py class Config(object):
DEBUG = False
TESTING = False
DATABASE_URI = 'sqlite://:memory:' class ProductionConfig(Config):
DATABASE_URI = 'mysql://user@localhost/foo' class DevelopmentConfig(Config):
DEBUG = True class TestingConfig(Config):
TESTING = True PS: 从sys.path中已经存在路径开始写 PS: settings.py文件默认路径要放在程序root_path目录,如果instance_relative_config为True,则就是instance_path目录
在根目录下创建config.py
class Config(object):
DEBUG = False
TESTING = False
secret_key = "asdfasdf"
DATABASE_URI = 'sqlite://:memory:' class ProductionConfig(Config):
DATABASE_URI = 'mysql://user@localhost/foo' class DevelopmentConfig(Config):
DEBUG = True
创建s3.py ,引入配置文件 config.py
from flask import Flask
from config import DevelopmentConfig app = Flask(__name__) app.config.from_object(DevelopmentConfig) @app.route('/')
def index():return 'Hello World!' if __name__ == '__main__':
app.run()
Flask 中路由的本质
1. decorator = app.route('/',methods=['GET','POST'],endpoint='n1')
def route(self, rule, **options):
# app对象
# rule= /
# options = {methods=['GET','POST'],endpoint='n1'}
def decorator(f):
endpoint = options.pop('endpoint', None)
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
2. @decorator ----> index = decorator(index)
decorator(index)
通过源码我们可以看到最终是通过 add_url_rule 添加路由的,基于这个我们可以模仿
from flask import Flask app = Flask(__name__) def login():
return '登录' app.add_url_rule('/login', 'n2', login, methods=['GET',"POST"]) if __name__ == '__main__':
app.run(
类视图
之前使用的视图都是函数,简称为视图函数,视图也可以基于类来实现,类视图的好处是支持继承,类视图需要通过app.add_url_role(url_rule,view_func(name="必传的参数"))来进行注册,类里面要加装饰器就用:detactors=[] ,里面可以添加多个装饰器
可以设置类属性来做一些限制 methods 可以限制访问的请求方法 , decorators 增加装饰器
from flask import Flask,views app = Flask(__name__)
app.debug = True
app.secret_key = "asdfasdf" def auth(func):
def inner(*args, **kwargs):
result = func(*args, **kwargs)
return result
return inner class IndexView(views.MethodView):
methods = ['GET'] # 只允许GET 请求访问
decorators = [auth, ] # 添加装饰器 def get(self):
return 'Index.GET' def post(self):
return 'Index.POST' app.add_url_rule('/index', view_func=IndexView.as_view(name='index')) # name=endpoint if __name__ == '__main__':
app.run()
add_url_rule中的参数介绍
一个简单的重定向的小实例
from flask import Flask app = Flask(__name__) @app.route('/index/',methods=['GET','POST'],endpoint='n1',redirect_to="/index2",)
def index():return '公司老首页' @app.route('/index2/',methods=['GET','POST'],endpoint='n2',defaults={'nid':888},strict_slashes=True)
def index2(nid):
print(nid)
return '公司新首页' if __name__ == '__main__':
app.run()
访问公司的内部网站有时需要修改hosts文件
小工具:脚本 py文件【为公司测试人员开发的修改host文件的小程序】
1 windows C:\Windows\System32\drivers\etc\hosts
2 linux /etc/hosts
添加以下的实例
127.0.0.1:5000 www.baidu.com
子域名访问
在hosts文件中添加一个域名
127.0.0.1 zhangbiao.com
127.0.0.1 admin.zhangbiao.com
127.0.0.1 buy.zhangbiao.com
子域名访问的代码如下
from flask import Flask, views, url_for app = Flask(import_name=__name__)
app.config['SERVER_NAME'] = 'zhangbiao.com:5000' @app.route("/", subdomain="admin") 返回静态的子域名
def static_index():
"""Flask supports static subdomains
This is available at static.your-domain.tld"""
return "xxxxxx.your-domain.tld" @app.route("/dynamic", subdomain="<username>") # 返回动态的子域名
def username_index(username):
"""Dynamic subdomains are also supported
Try going to user1.your-domain.tld/dynamic"""
print(username)
return username + ".your-domain.tld" if __name__ == '__main__':
app.run()
输入子域名
http://admin.zhangbiao.com:5000/
返回静态子域名如下
输入子域名
http://buy.zhangbiao.com:5000/dynamic
返回动态子域名如下
自定义正则表达式
基本的步奏
1 写一个类 BaseConverter
2 视情况而定,是否要重构 to_python 和 to_url方法
to_python 正则匹配成功之后,传入视图函数之前对先匹配的数据,进行二次处理
to_url 反向生成执行to_url ,在下面实例中在执行url_for的时候执行to_url
from flask import Flask, views, url_for
from werkzeug.routing import BaseConverter app = Flask(import_name=__name__) # 1. 写RegexConverter类
class RegexConverter(BaseConverter):
"""
自定义URL匹配正则表达式
""" def __init__(self, map, regex):
super(RegexConverter, self).__init__(map)
self.regex = regex def to_python(self, value):
"""
路由匹配时,匹配成功后传递给视图函数中参数的值
:param value:
:return:
"""
# "123"
return int(value) def to_url(self, value):
"""
使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数
:param value:
:return:
"""
val = super(RegexConverter, self).to_url(value)
return val # 2. 将RegexConverter添加到flask中
app.url_map.converters['regex'] = RegexConverter @app.route('/index/<regex("\d+"):nid>')
def index(nid):
print(nid,type(nid)) url_for('index',nid=89)
return 'Index' if __name__ == '__main__':
app.run()
模板
1 Flask 可以传递函数给模板 2 后端对html进行转义可以使用markup 前端可以用safe
2 减少前端代码的复用,前端可以定义宏,可以像一个函数一样调用
主程序
from flask import Flask,render_template,Markup,jsonify,make_response
app = Flask(__name__) def func1(arg):
return Markup("<input type='text' value='%s' />" %(arg,)) @app.route('/')
def index():
# return jsonify({'k1':'v1'})
return render_template('s10index.html',ff = func1) if __name__ == '__main__':
app.run()
s10index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body> {{ff('六五')}} {% macro xx(name, type='text', value='') %}
<input type="{{ type }}" name="{{ name }}1" value="{{ value }}">
<input type="{{ type }}" name="{{ name }}2" value="{{ value }}">
<input type="{{ type }}" name="{{ name }}3" value="{{ value }}">
<input type="{{ type }}" name="{{ name }}4" value="{{ value }}">
{% endmacro %} {{ xx('n') }} </body>
</html>
请求和响应
请求封装在 request中
from flask import Flask,request
响应
from flask import Flask,jsonify,make_response
from flask import Flask,jsonify,make_response,request
app = Flask(__name__) @app.route('/')
def index():
response = make_response("asdfasdf")
response.set_cookie("name", 'zhang')
return response if __name__ == '__main__':
app.run()
Session
简单的使用如下
from flask import Flask,session
app = Flask(__name__)
app.secret_key = 'sdfsdf' @app.route('/')
def set_session():
# flask内置的使用 加密cookie(签名cookie)来保存数据。
session['k1'] = 'v1'
session['k2'] = 'v2'
return 'xxx' @app.route('/get_session')
def get_session():
# flask内置的使用 加密cookie(签名cookie)来保存数据。
k1=session.get('k1','')
print(k1)
return 'xxx'
if __name__ == '__main__':
app.run()