Flask web框架
Flask简介
Flask 是一个轻量级的 Web 应用框架, 使用 Python 编写。基于 WerkzeugWSGI 工具箱和 Jinja2 模板引擎。使用 BSD 授权。Flask 也被称为 microframework ,因为它使用简单的核心,用 extension 增加其它功能。Flask 没有默认使用的数据库、窗体验证工具。然而,Flask 保留了扩增的弹性,可以用 Flask-extension 加入这些功能:ORM、窗体验证工具、文件上传、各种开放式身份验证技术。
安装virtualenv
pip install virtualenv 安装virtualenv库
pip install -U pip virutalenv 更新virtualenv最新版本
pip3 freezr | grep virtualenv 查看版本信息
创建虚拟环境
cd /root/virtual
virtualenv -p python3 venv
-p 选择python3为解释器 venv为虚拟环境的名字
source /root/virtual/venv/bin/activate 激活虚拟环境
pip install flask=1.0.2 安装flask,不指定版本号即默认安装最新版
最小web应用启动
新建hello.py文件
from flask import Flask
app = Flask(__name__) flask模板包名或应用模块,第一个参数
@app.route('/') 用装饰器route()告诉flask在哪个url下才触发我们的函数
def hello_world():
return 'Hello, World!'
cd /root/virtual/
export FLASK_APP=hello.py
flask run | python3 -m flask run 运行虚拟环境
flask run -h 127.0.0.1 -p 8080 可自己指定端口
调试模式
启用调试模式可以在代码修改后能自动重载,并如果发生错误它会提供一个有用的调试器
export FLASK_ENV=development
export FLASK_DEBUG=1
flask run
flask路由
上面说了route是一个装饰器是用来把函数绑定到url上。修改py文件举例子理解
from flask import Flask
app = Flask(__name__)
# 如果访问根目录 '/' ,返回 Index Page
@app.route('/')
def index():
return 'Index Page'
# 如果访问 '/hello' ,返回 Hello, World!
@app.route('/hello')
def hello():
return 'Hello, World!'
可以看出当访问/目录返回index page,而访问/hello返回hello,world
给 URL 增加变量的部分,你需要把一些特定的字段标记成 <variable_name> 。这些特定的字段将作为参数传入到你的函数中。当然也可以指定一个可选的转换器通过规则 <converter:variable_name> 将变量值转换为特定的数据类型
添加py代码
@app.route('/user/<username>')
def show_user_profile(username):
# 显示用户名
return 'User {}'.format(username)
@app.route('/post/<int:post_id>')
def show_post(post_id):
# 显示提交整型的用户"id"的结果,注意"int"是将输入的字符串形式转换为整型数据
return 'Post {}'.format(post_id)
@app.route('/path/<path:subpath>')
def show_subpath(subpath):
# 显示 /path/ 之后的路径名
return 'Subpath {}'.format(subpath)
分析代码访问user下根据用户输入的用户名网页会回显用户的输入,访问http://127.0.0.1:5000/user/lincso
,页面会显示User lincso
。访问 http://127.0.0.1:5000/post/3
时,页面显示为 Post 3
。这里跟上面用户名不同的是浏览器输入字符串传递给show_post处理时会被转换为整型
类型 | 含义 |
---|---|
string | 默认的数据类型,接受没有任何斜杠“/”的字符串 |
int | 接受整型 |
float | 接受浮点类型 |
path | 和 string 类似,但是接受斜杠“/” |
uuid | 只接受 uuid 字符串 |
静态文件
创建一个static的文件夹,在应用中就可以访问/static。
生成静态url
url_for('static',filename="style.css") style.css文件是在static文件夹下的
渲染模板
在python中配置html是非常繁琐的,因为你需要自己做好html转移保持应用程序安全,而flask自动配置了jinja2模板解决这个问题。
render_template()渲染模板
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None): # 默认 name 为 None
return render_template('hello.html', name=name) # 将 name 参数传递到模板变量中
分析代码可以知道访问hello下fllask会在程序templates来寻找hello.html这个模板。如果应用是模块化的,那么templates会在旁边,如果是包那么在应用根下应该就能找到
新建hello.html
cd /root/virtual/
mkdir templates && cd templates
vi hello.html
-----代码-----
<!DOCTYPE html>
<title>Hello From Flask</title>
{% if name %}
<h1>Hello {{ name }}!</h1>
{% else %}
<h1>Hello World!</h1>
{% endif %}
结合这个模板清晰的看出我们可以访问两个页面,在name没有传值的时候返回hello world;有值的时候(lincso)返回hello lincso
练习项目
创建模板与css文件,在模板中引用css,访问网页会显示一个绿色的hello world
模块结构
/hello.py
/templates
/hello.html
/static
/hello.css
hello.py
-----代码-----
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def get_hello():
return render_template('hello.html')
hello.html
-----代码-----
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<link
rel="stylesheet"
type="text/css"
href="{{ url_for('static', filename='hello.css') }}"
/>
</head>
<body>
<h1>hello world</h1>
</body>
</html>
hello.css
-----代码-----
h1 {
color: green;
text-align: center;
}
图片上传实现
使用test_request_context()
或者request_context()
,从request
对象的form
中可以获取表单的数据,args
中可以获取 URL 中的参数,files
可以获取上传的文件,cookies
可以操作 cookie
读取cookies
from flask import request
@app.route('/')
def index():
username = request.cookies.get('username')
upload.py
-----代码-----
import os
from flask import Flask, request
from werkzeug import secure_filename
UPLOAD_FOLDER = '/home/shiyanlou/Code'
ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'])
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS
@app.route('/', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
file = request.files['file']
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return '{} upload successed!'.format(filename)
return '''
<!doctype html>
<title>Upload new File</title>
<h1>Upload new File</h1>
<form action="" method=post enctype=multipart/form-data>
<p><input type=file name=file>
<input type=submit value=Upload>
</form>
'''
登录功能实现
重定向:redirect 错误处理:errorhander 对象保存会话信息:session 使用会话需要设置secret_key,make_responese生成响应,flash实现消息闪现,flask用来整合WSGI中间件
模块结构
/app.py
/templates
/index.html
/login.html
log.py
-----代码-----
from flask import Flask, request
from flask import flash, redirect, url_for, render_template, session
app = Flask(__name__)
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'
@app.route('/<name>')
def index(name):
return render_template('index.html',name=name)
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
if request.form['username'] != 'shiyanlou' or request.form['password'] != 'shiyanlou':
flash('username or password invalid')
return redirect(url_for('index', name='world'))
else:
session['username'] = request.form['username']
name = request.form['username']
flash('you were logged in')
return redirect(url_for('index', name=name))
return render_template('login.html')
index.html
-----代码-----
<!DOCTYPE html>
<title>Index</title>
<div>
{% for message in get_flashed_messages() %}
<div class="flash">{{ message }}</div>
{% endfor %}
<h1>hello {{name}}</h1>
</div>
login.html
-----代码-----
<!DOCTYPE html>
<title>Login</title>
<div>
<form action="{{ url_for('login') }}" method="post">
<dl>
<dt>Username:</dt>
<dd><input type="text" name="username" /></dd>
<dt>Password:</dt>
<dd><input type="password" name="password" /></dd>
<dd><input type="submit" value="Login" /></dd>
</dl>
</form>
</div>
Flask实战----博客开发(Flog)
实现效果
- 根据配置文件中的认证允许用户登录以及注销。仅仅支持一个用户。
- 当用户登录后,他们可以添加新的条目,这些条目是由纯文本的标题和 HTML 的正文构成。因为我们信任用户这里的 HTML 是安全的。
- 页面倒序显示所有条目(新的条目在前),并且用户登入后可以在此添加新条目。
首先还是目录结构
/flog-------------flog.py+schema.sql
/static--------css+javascript
/templates-----html模板
创建sqlite数据库
schema.sql
------code------
DORP TABLE IF EXISTS ftable;
CREATE TABLE ftable(
id INTEGER PRIMARY KEY AUTOINCREMENT,
title STRING NOT NULL,
text STRING NOT NULL
)
主文件基本信息
flog.py
-------code----------
import sqlite3
from flask import (Flask,render_template,g,flash,request,session,abort,redirect,url_for)
#配置
DATABASE='/tmp/ftable.db'
ENV='development'
DEBUG=true
SECRET_KEY='development key'
USERNAME='admin'
PASSWORD='password'
#创建应用
app=Flask(__name__)
app.config.from_object(__name__)
#from_object 通常用于获取对象的属性以增加配置项
#读取当前所在文件中的所有变量,选择其中全大写的变量作为配置项
#连接数据库
def db_conn():
return sqlite3.connect(app.config['DATABASE'])
#运行文件
if __name__=='__main__':
app.run()
虚拟环境中运行
export FLASK_DEBUG=1 FLASK_APP=flog.py
flask run -h 127.0.0.1 -p 8080
在py的配置信息中DATABASE指向的文件还需要我们去创建,下面有两种方法
1.创建数据库模式
sqlite3 /tmp/ftable.db<schema.sql
2.添加一个函数对数据库进行初始化(在python中增加一个init_db)
from flog import init_db
def init_db():
with db_conn() as conn:
with app.open_resource('schema.sql') as f:
conn.cursor().executescript(f.read().decode())
conn.commit()
#代码分析:读取schema.sql中的数据以二进制格式返回,然后用executescript一次执行多个sql语句,使用commit提交执行sql语句的结果(因为有些语句执行后不会立即改动数据库)
请求数据库连接
@app.before_request
def before():
g.conn=db_conn()
@app.teardown_request
def teardown(exception):
g.conn.close()
#代码分析:这里很简单通过before_request(),teardown_request()装饰器实现请求之前初始化以及请求之后自动关闭
主页视图
@app.route('/')
def show_ftable():
cursor=g.conn.execute('select title,text from ftable order by id desc')
ftable=[dict(title=row[0],text=row[1]) for row in cursor.fetchall()]
return render_template('show_table.html',ftable=ftable)
添加视图
@app.route('/add',methods=['POST'])
def add_ftable():
if not session.get('login'):
abort(401)
g.conn.execute('insert into ftable(title,text) values (?,?)',[request.form.get('title'),request.form.get('text')])
g.conn.commit()
flash('new page is created successful')
return redirect(url_for('show_ftable'))
登录功能
@app.route('/login',methods=['GET','POST'])
def login():
error=none
if request.method=='POST':
if request.form.get('username')!=app.config.get('USERNAME'):
error='invalid username'
elif request.form.get('password')!=app.config.get('PASSWORD'):
error='invalid password'
else:
session['login']=true
flash('you are loginned successfully')
return redirect(url_for('show_ftable'))
return render_template('login.html',error=error)
def logout():
session.pop('login',none)
flash('you have logouted successfully')
return redirect(url_for('show_ftable'))
到这里所有功能以基本实现,现在就要做模板界面用于交互啦
layout.html
-------code---------
<!doctype html>
<html>
<head>
<title>Flaskr</title>
<link rel='stylesheet' type='text/css' href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<div class='page'>
<h1>Flaskr</h1>
<div class='metanav'>
{% if not session.login %}
<a href="{{ url_for('login') }}">log in</a>
{% else %}
<a href="{{ url_for('logout') }}">log out</a>
{% endif %}
</div>
{% for message in get_flashed_messages() %}
<div class='flash'>{{ message }}</div>
{% endfor %}
{% block body %}{% endblock %}
</div>
</body>
</html>
show_ftable.html
---------code---------
{% extends "layout.html" %}
{% block body %}
{% if session.login %}
<form action="{{ url_for('add_ftable') }}" method=post class=add-ftable>
<dl>
<dt>Title:
<dd><input type=text size=30 name=title>
<dt>Text:
<dd><textarea name=text rows=5 cols=40></textarea>
<dd><input type=submit value=Share>
</dl>
</form>
{% endif %}
<ul class=ftables>
{% for ftable in ftables %}
<li><h2>{{ ftable.title }}</h2>{{ ftable.text|safe }}</li>
{% else %}
<li><em>Unbelievable. No ftables here so far.</em><li>
{% endfor %}
</ul>
{% endblock %}
login.html
---------code----------
{% extends "layout.html" %}
{% block body %}
<h2>Login</h2>
{% if error %}<p class=error><strong>Error:</strong> {{ error }}{% endif %}
<form action="{{ url_for('login') }}" method=post>
<dl>
<dt>Username:
<dd><input type=text name=username>
<dt>Password:
<dd><input type=password name=password>
<dd><input type=submit value=Login>
</dl>
</form>
{% endblock %}
静态文件
style.css
----------code--------------
body {
font-family: sans-serif;
background: #eee;
}
a,
h1,
h2 {
color: #377ba8;
}
h1,
h2 {
font-family: "Georgia", serif;
margin: 0;
}
h1 {
border-bottom: 2px solid #eee;
}
h2 {
font-size: 1.2em;
}
.page {
margin: 2em auto;
width: 35em;
border: 5px solid #ccc;
padding: 0.8em;
background: white;
}
.entries {
list-style: none;
margin: 0;
padding: 0;
}
.entries li {
margin: 0.8em 1.2em;
}
.entries li h2 {
margin-left: -1em;
}
.add-entry {
font-size: 0.9em;
border-bottom: 1px solid #ccc;
}
.add-entry dl {
font-weight: bold;
}
.metanav {
text-align: right;
font-size: 0.8em;
padding: 0.3em;
margin-bottom: 1em;
background: #fafafa;
}
.flash {
background: #cee5f5;
padding: 0.5em;
border: 1px solid #aacbe2;
}
.error {
background: #f0d6d6;
padding: 0.5em;
}