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)

实现效果

  1. 根据配置文件中的认证允许用户登录以及注销。仅仅支持一个用户。
  2. 当用户登录后,他们可以添加新的条目,这些条目是由纯文本的标题和 HTML 的正文构成。因为我们信任用户这里的 HTML 是安全的。
  3. 页面倒序显示所有条目(新的条目在前),并且用户登入后可以在此添加新条目。

首先还是目录结构

/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;
}
posted @ 2020-07-09 09:07  Lincso  阅读(411)  评论(0编辑  收藏  举报