Flask 概述
- Flask基于Werkzeug WSGI工具包和Jinja2模板引擎。
- WSGI:Web Server Gateway Interface(Web服务器网关接口,WSGI)已被用作Python Web应用程序开发的标准。 WSGI是Web服务器和Web应用程序之间通用接口的规范。
- Werkzeug是一个WSGI工具包,它实现了请求,响应对象和实用函数。 这使得能够在其上构建web框架。 Flask框架使用Werkzeug作为其基础之一。
- jinja2是Python的一个流行的模板引擎。Web模板系统将模板与特定数据源组合以呈现动态网页。
安装: conda install Flask
flask 应用
import flask
app = flask.Flask(__name__)
# # http://127.0.0.1:5000/h
@app.route('/')
def hello_world():
return 'Hello World'
def hi():
return 'hi'
# view_func 一定要是函数名
# http://127.0.0.1:5000/hi
app.add_url_rule(rule='/hi/',view_func=hi)
# 动态URL
@app.route('/hello/<user_name>/')
def hello(user_name):
return 'hello:' + str(user_name)
# 整数
@app.route('/int/<int:user_id>/')
def theId(user_id):
return 'id:' + str(user_id)
# 浮点数
@app.route('/float/<float:pi>/')
def thePI(pi):
return 'float:' + str(pi)
# path
@app.route('/path/<path:p>/')
def thePath(p):
return 'float:' + str(p)
# for guest
@app.route('/guest/<user_name>')
def hi_guest(user_name):
return 'hi GUEST:' + user_name
# for admin
@app.route('/admin/<user_name>')
def hi_admin(user_name):
if user_name=='admin':
return 'hi ADMIN'
else:
return flask.redirect(flask.url_for('hi_guest',user_name=user_name))
if __name__ == '__main__':
app.run(host='127.0.0.1',port='5000',debug=True)
- Flask类的实例,这里的app是我们的WSGI应用程序
- Flask构造函数使用当前模块的名称
(__name __)
作为参数。 - Flask类的route()函数是一个装饰器,它告诉应用程序哪个URL应该调用相关的函数:
app.route(rule, options)
- rule 参数表示与该函数绑定的 URL,在上面的示例中,
/
URL与hello_world()函数绑定。因此,当在浏览器中打开web服务器的主页时,将呈现该函数的输出。 - options 是要转发给基础Rule对象的参数列表
- rule 参数表示与该函数绑定的 URL,在上面的示例中,
- Flask类的run()方法在本地开发服务器上运行应用程序:
app.run(host, port, debug, options)
- host:要监听的主机名。 默认为127.0.0.1(localhost)。设置为“0.0.0.0”以使服务器在外部可用
- port:默认值为5000
- debug:默认为False。 如果设置为True,则提供调试信息
- options:要转发到底层的Werkzeug服务器。
- 添加路由的两种方式
- route()装饰器
- application对象的add_url_rule()函数
- 路由定义要求:Flask的URL规则基于Werkzeug的路由模块。这确保形成的URL是唯一的,并且基于Apache规定的先例。一句话:URL路由最后加
/
,否则会导致404 - 动态URL:通过向规则参数添加变量部分,可以动态构建URL。此变量部分标记为
<variable-name>
。它作为关键字参数传递给与规则相关联的函数。- 默认参数为字符串
- 其他类型
- int:接受整数
- float:接受浮点值
- path:接受用作目录分隔符的斜杠
- 使用url_for函数重定向 url: url_for 函数接受函数的名称作为第一个参数(跳转到该参数对应的函数的URL),以及一个或多个关键字参数,每个参数对应于跳转的函数的 URL变量.
Flask 模板
简单来讲,模板就是渲染页面时,渲染函数的一个参数。
把业务逻辑和表现内容放在一起,会增加代码的复杂度和维护成本.
- 模板其实是一个包含响应文本的文件,其中用占位符(变量)表示动态部分,告诉模板引擎其具体的值需要从使用的数据中获取
- 使用真实值替换变量,再返回最终得到的字符串,这个过程称为’渲染’
- Flask 是使用 Jinja2 这个模板引擎来渲染模板
使用模板的好处
- 视图函数只负责业务逻辑和数据处理(业务逻辑方面)
- 而模板则取到视图函数的数据结果进行展示(视图展示方面)
- 代码结构清晰,耦合度低
模板基本使用:在项目下创建 templates 文件夹,用于存放所有模板文件。 在目录下创建一个模板文件 html 文件 hello.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
我的模板html内容
<!--模版中使用传入的变量 -->
<br />{{ my_str }}
<br />{{ my_int }}
<br />{{ my_array }}
<br />{{ my_dict }}
</body>
</html>
可以在应用中渲染模板文件返回:
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
# 可以向模板传入的数据
my_str = 'Hello Word'
my_int = 10
my_array = [3, 4, 2, 1, 7, 9]
my_dict = {
'name': 'xiaoming',
'age': 18
}
return render_template('hello.html',my_str=my_str,
my_int=my_int,
my_array=my_array,
my_dict=my_dict)
if __name__ == '__main__':
app.run(host='127.0.0.1',port='5000',debug=True
模板变量: 代码中传入到模板中的字符串,列表,字典
post方法
默认情况下,Flask路由响应GET请求。但是,可以通过为route()装饰器提供方法参数来更改此首选项。
为了演示在URL路由中使用POST方法,首先让我们创建一个HTML表单,并使用POST方法将表单数据发送到URL.
创建表单文件 login.html
,并放在 templates 目录下:
<html>
<body>
<form action = "http://127.0.0.1:5000/login/" method = "post">
<p>Enter Name:</p>
<p><input type = "text" name = "nm" /></p>
<p><input type = "submit" value = "submit" /></p>
</form>
</body>
</html>
应用:
import flask
app = flask.Flask(__name__)
@app.route('/success/<user_name>')
def success(user_name):
return 'welcome ' + user_name
@app.route('/login/',methods = ['POST', 'GET'])
def login():
if flask.request.method == 'POST':
user = flask.request.form['nm']
return flask.redirect(flask.url_for('success',user_name=user))
else:
return flask.render_template('login.html')
if __name__ == '__main__':
app.run(host='127.0.0.1',port='5000',debug=True)
Flask 静态文件
在开发过程中,静态文件是从包或模块旁边的static文件夹中提供,它将在应用程序的路由 /static
中被调用。
hello.js 文件内容:
function sayHello() {
alert("Hello World")
}
在如下的 index.html 中,点击按钮 Say Hello
会调用该js中的函数 sayHello()
:
<html>
<head>
<script type = "text/javascript"
src = "{{ url_for('static', filename = 'hello.js') }}" ></script>
</head>
<body>
<input type = "button" onclick = "sayHello()" value = "Say Hello" />
</body>
</html>
应用:
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run(host='127.0.0.1',port='5000',debug=True)
使用UI库: element-ui
参考:
- 代码: https://unpkg.com/browse/element-ui@2.14.0/
- 文档: https://element.eleme.cn/#/zh-CN/component/installation
从 https://unpkg.com/browse/element-ui@2.14.0/ 下载最新版本代码到本地文件夹 static。
参考 unpkg.com下载(pyhton)脚本分享下载文件:
- 下载脚本unpkg.com.py 放在 static 目录下
- 下载前指定组件版本, element UI 支持的是 vue版本2.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
unpkg.com.py
参考: https://www.jianshu.com/p/6b58bdc9fc6f
"""
import requests,re,os,time,shutil
url="https://unpkg.com/"
headers={ 'Accept-Language': 'zh-CN,zh;q=0.8',
'Content-Type': 'text/html;Charset=utf-8',
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36"
}
#获取HTML
def getHTML(url,encoding='utf-8'):
rd = requests.get(url, params=None, headers=headers)
rd.encoding = encoding
return rd.text
#获取版本
def getVsions(m):
h=getHTML(url+m+'/')
j=re.findall(r'<select name="version"(.*?)</select>', h,re.S)[0]
patt = re.compile(r'<option.+?>(.+?)</option>')
option = patt.findall(j)
return option
#扫描目录
def getPaths(v,p='/',files=[],folders=[]):
h=getHTML(url+v+p)
t=re.findall(r'<table(.*?)</table>', h,re.S)[0]
href = re.findall('href="(.*?)"',t)
for name in href:
path=p+name
#if name in ['../','LICENSE'] or not 'iconfont' in path:#material-design-icons
if name in ['../','LICENSE'] or path in ['/src/','/packages/','/types/','/dist/docs/','/docs/','/samples/',"/test/","/locale/"] :#跳过
continue
print(path)
if name[-1]=='/':
folders.append(path)
getPaths(v,path,files,folders)
else:
files.append(path)
return {"files":files,"folders":folders}
#创建目录
def makeDirs(dirs,p):
if p==None:
p='./'
for i in dirs:
path=p+i
if not os.path.exists(path):
print("创建目录",path)
os.makedirs(path)
#下载文件
def download(url,path=None):#dir=保存文件夹路径
if not os.path.exists(path):
print("下载:",url)
r=requests.get(url)
t=str(time.time())+'.'+str(pid)+'.tmp'
open(t, 'wb').write(r.content)
shutil.move(t, path)
else:
print("文件已存在")
if __name__=='__main__':
mod="element-ui"
pid=os.getpid()
print(url+mod+'/')
versions=getVsions(mod)
print("所有版本:",versions)
version=mod+'@'+versions[-1]
print("默认版本:",version)
version="element-ui@2.14.0"
paths=getPaths(version)
makeDirs(paths["folders"],version)
for i in paths["files"]:
u=url+version+i
download(u,version+'/'+i)
print("完成")
mod="vue"
pid=os.getpid()
print(url+mod+'/')
versions=getVsions(mod)
print("所有版本:",versions)
version=mod+'@'+versions[-1]
print("默认版本:",version)
version="vue@2.6.12"
paths=getPaths(version)
makeDirs(paths["folders"],version)
for i in paths["files"]:
u=url+version+i
download(u,version+'/'+i)
print("完成")
引入:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="/static/js/echarts.js"></script>
<!-- import CSS -->
<link rel="stylesheet" href="/static/element-ui@2.14.0/lib/theme-chalk/index.css">
</head>
<body>
<div id="app">
<el-button @click="visible = true">Button</el-button>
<el-dialog :visible.sync="visible" title="Hello world">
<p>Try Element</p>
</el-dialog>
</div>
</body>
<!-- import Vue before Element -->
<script src="/static/vue@2.6.12/dist/vue.js"></script>
<!-- import JavaScript -->
<script src="/static/element-ui@2.14.0/lib/index.js"></script>
<script>
new Vue({
el: '#app',
data: function() {
return { visible: false }
}
})
</script>
</html>
模版继承
父模版: base.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="/static/js/echarts.js"></script>
<!-- import CSS -->
<link rel="stylesheet" href="/static/element-ui@2.14.0/lib/theme-chalk/index.css">
</head>
<body>
<div id="app">
{% block content %}
{% endblock %}
</body>
<!-- import Vue before Element -->
<script src="/static/vue@2.6.12/dist/vue.js"></script>
<!-- import JavaScript -->
<script src="/static/element-ui@2.14.0/lib/index.js"></script>
<script>
new Vue().$mount('#app')
</script>
</html>
某个子模版:index.html
{% extends 'base.html' %}
{# 重写block #}
{% block content %}
<el-menu :default-active="activeIndex2" class="el-menu-demo" mode="horizontal" @select="handleSelect"
background-color="#545c64" text-color="#fff" active-text-color="#ffd04b">
<el-menu-item index="1">处理中心</el-menu-item>
<el-submenu index="2">
<template slot="title">我的工作台</template>
<el-menu-item index="2-1">选项1</el-menu-item>
<el-menu-item index="2-2">选项2</el-menu-item>
<el-menu-item index="2-3">选项3</el-menu-item>
<el-submenu index="2-4">
<template slot="title">选项4</template>
<el-menu-item index="2-4-1">选项1</el-menu-item>
<el-menu-item index="2-4-2">选项2</el-menu-item>
<el-menu-item index="2-4-3">选项3</el-menu-item>
</el-submenu>
</el-submenu>
<el-menu-item index="3" disabled>消息中心</el-menu-item>
<el-menu-item index="4"><a href="https://www.ele.me" target="_blank">订单管理</a></el-menu-item>
</el-menu>
{% endblock %}
windows系统chrome浏览器报错,css类型错误
报错信息:
Resource interpreted as Stylesheet but transferred with MIME type text/html
解决方案,假定已经设置静态文件夹:
from flask import send_from_directory
# Flask: Resource interpreted as Stylesheet but transferred with MIME type text/html
@app.route('/css/<path:path>')
def send_css(path):
return send_from_directory('css', path,mimetype='text/css')
正文完