当前位置: 首页 > news >正文

Python Web开发 # Flask框架教程

python三大框架:Flask、Django、Tornado,分别代表了轻量级(灵活)、大而全(完整)、c10k(高并发)。博主会一一介绍。此处先介绍Flask。
(ps:初学者快速出demo可以用flask,但是因为flask过于灵活,所以网上的flask工程结构千奇百怪,反而不利于初学者进一步学习规范化的工程项目,此时可以先去看django(工程结构固定),学习好后回过头来看flask才能更体现出flask的优点)
读者如果有比较好的flask工程项目的模板欢迎在评论区分享~感激不尽

基础

pip install Flask

测试代码:

from flask import Flask
app = Flask(__name__) # Flask类的一个对象是WSGI应用程序
@app.route('/')
def hello_world():
	return 'Hello World'
if __name__ == '__main__':
	app.run()

python xxx.py或者:

> set FLASK_APP=hello	# 设置入口点xxx.py,flask run 根据环境变量FLASK_APP来决定入口,默认app.py/wsgi.py则不用提供
> set FLASK_ENV=development # 调试模式,修改代码自动重启python脚本
> flask run # 默认本地可见,对网络暴露:flask run --host=0.0.0.0
  • Flask基于Werkzeug WSGI工具包和Jinja2模板引擎。被称为“microframework” ,simple core with extension来增加其他功能。无默认数据库、数据库抽象层、表单验证、窗体验证工具。

  • 安装Flask时会默认安装下面的:
    WSGIWeb Server Gateway Interface(Web服务器网关接口,WSGI),用作Python Web应用程序开发的标准。 是Python Web服务器和Web应用程序之间通用接口的规范。
    Werkzeug
    WSGI工具包,实现了请求,响应对象和实用函数。 能够在其上构建web框架。
    jinja2
    Jinja2 template引擎是Python的一个流行的模板引擎。渲染页面的模板语言。MarkupSafe 与 Jinja 共用,在渲染页面时用于避免不可信的输入,防止注入攻击。
    ItsDangerous 保证数据完整性的安全标志数据,用于保护 Flask 的 session cookie.
    Click 是一个命令行应用的框架。用于提供 flask 命令,并允许添加自定义 管理命令。

官方tutorial代码(flask《 教程 》一节的全部代码)地址:github link

  • 不默认安装但常用的库:
    • SQLAlchemy: ORM框架
    • logging 日志框架

Ref: CLI 文档。

app.run(host, port, debug, options)

Flask类的route()函数是一个装饰器,访问对应URL应该调用相关函数。
装饰器(Decorators) 是修改其他函数的功能的函数,让代码更简洁。封装一个函数,并且用这样或者那样的方式来修改它的行为。@是语法糖。

调试模式
将application对象的debug属性设置为True来启用Debug模式(好处是修改了python脚本后不用每次重新启动服务器,但是需要手动刷新网页)另一种是写在环境变量里。

app.run(debug = True)
app.debug = True; app.run()

路由

Flask中的route()装饰器用于将URL绑定到函数。

@app.route(rule, options)
  • rule 参数表示与该函数的URL绑定。
  • options 是要转发给基础Rule对象的参数列表。

add_url_rule()函数也可用于将URL与函数绑定

app.add_url_rule(rule,endpoint,view_func,methods)
app.add_url_rule(/<name>, ‘hello’, hello_world)
  • endpoint:理解为别名,more

变量规则(URL动态参数接收)

Flask的URL规则基于Werkzeug的路由模块。把 URL 的一部分标记为 <variable_name><converter:variable_name>来动态构建URL。

@app.route('/hello/<name>')
def hello_name(name): return 'Hello %s!' % name

converter有:string 不包含斜杠的文本/int 正整数/float 正浮点数/path 类似 string ,但可以包含斜杠/uuid UUID 字符串

坑点:关于尾部’/’

@app.route('/flask')
def hello_flask():   return 'Hello Flask'
@app.route('/python/')
def hello_python():   return 'Hello Python'

在第二个规则中,使用斜杠(/)。因此,它成为一个规范的URL。因此,使用 /python 或 /python/返回相同的输出。第一个规则,/flask/ URL会产生“404 Not Found”页面。

安全:html转义

防止注入攻击,所有用户提供的值在输出渲染前必须被转义

from markupsafe import escape

URL构建(用于URL跳转)

反转函数url_for(func_name,**params)动态构建特定函数的URL
注意下面:

@app.route('/login')
def login():pass
@app.route('/user/<username>')
def profile(username): pass
url_for('login', next='/')
url_for('profile', username='John Doe')
对应的url分别是:
/login?next=/
/user/John%20Doe

HTTP方法

Http协议中定义了从指定URL检索数据的不同方法。如果使用了 GET,自动添加 HEAD、OPTIONS 方法支持,并且同时会按照 HTTP RFC 来处理 HEAD 请求。不写默认GET。

MethodDescription
GET以未加密的形式将数据发送到服务器。最常见的方法
HEAD和GET方法相同,但没有响应体
POST将HTML表单数据发送到服务器。POST方法接收的数据不由服务器缓存
PUT用上传的内容替换目标资源的所有当前表示
DELETE删除由URL给出的目标资源的所有当前表示

Demo

from flask import Flask, redirect, url_for, request
...
@app.route('/success/<name>')
def success(name):  return 'welcome %s' % name
@app.route('/login',methods = ['POST', 'GET'])
def login():
   if request.method == 'POST':
      user = request.form['nm']
      return redirect(url_for('success',name = user))
   else:
      user = request.args.get('nm')
      return redirect(url_for('success',name = user))

静态文件处理

flask project下静态文件默认是:static/,特定的 ‘static’ 端点用于访问该路径。
eg:

<script type = "text/javascript" src = "{{ url_for('static', filename = 'hello.js') }}" ></script>

模板

视图函数有两个作用: 处理业务逻辑、返回响应内容。在大型应用中,把业务逻辑表现内容放在一起,会增加代码的复杂度和维护成本。

  • 模板是包含响应文本的文件,用占位符(变量)表示动态部分

  • 使用真实值替换变量,再返回最终得到的字符串,这个过程称为’渲染’

  • 视图函数只负责业务逻辑数据处理(业务逻辑方面,简单地可以理解成根据业务要求对数据处理的逻辑)

  • 模板负责视图函数的数据结果进行展示(视图展示方面)

  • 代码结构清晰,耦合度低

默认 templates/ 文件夹用于存放模板文件 ,模板中可以访问 request 、 session 和 g 对象。模板继承有利于表现层代码复用。自动转义默认开启。

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

Ques:为什么render_template的路径没有给/templates/hello.html?
Ans:flask下的默认设置,Ref~

Request

用于处理请求数据。

  • Form - 它是一个字典对象,包含表单参数及其值的键和值对。(处理表单提交)
  • args - 解析查询字符串的内容,它是问号(?)之后的URL的一部分。
  • Cookies - 保存Cookie名称和值的字典对象。
  • files - 与上传文件有关的数据。
  • method - 当前请求方法。

扩展阅读:Flask:请求-响应循环与上下文

将表单数据发送到模板

在 URL 规则中指定 http 方法。触发函数接收的 Form 数据可以以字典对象的形式收集它并将其转发到模板以在相应的网页上呈现它。

demo

后端应用:

from flask import Flask, render_template, request
app = Flask(__name__)
@app.route('/')
def student():
   return render_template('student.html')


@app.route('/result',methods = ['POST', 'GET'])
def result():
   if request.method == 'POST':
      result = request.form
      return render_template("result.html",result = result)


if __name__ == '__main__':
   app.run(debug = True)

表单提交

 <form action="http://localhost:5000/result" method="POST">
     <p>Name <input type = "text" name = "Name" /></p>
     <p>Physics <input type = "text" name = "Physics" /></p>
     <p>Chemistry <input type = "text" name = "chemistry" /></p>
     <p>Maths <input type ="text" name = "Mathematics" /></p>
     <p><input type = "submit" value = "submit" /></p>
  </form>

模板

<!doctype html>
  <table border = 1>
     {% for key, value in result.items() %}
    <tr>
       <th> {{ key }} </th>
       <td> {{ value }}</td>
    </tr>
 {% endfor %}
</table>

Cookies

Cookie以文本文件的形式存储在客户端的计算机上。目的是记住跟踪客户使用相关的数据,以获得更好的访问者体验和网站统计信息。Request对象包含Cookie属性,包含cookie、网站的到期时间,路径和域名等信息。

Flask中对cookie的处理:

  1. 设置cookie:
    设置cookie,默认有效期是临时cookie,浏览器关闭就失效,可以通过 max_age 设置有效期, 单位是秒
resp = make_response("success")   # 设置响应体
resp.set_cookie("skysys", "skysys", max_age=3600)
  1. 获取cookie
    获取cookie,通过request.cookies的方式, 返回的是一个字典
cookie_1 = request.cookies.get("skysys")
  1. 删除cookie(实际上对浏览器端是设置为过期,和直接在浏览器上删除不是一个含义)
resp = make_response("del success")  # 设置响应体
resp.delete_cookie("skysys")

demo:

from flask import Flask, make_response, request

app = Flask(__name__)

@app.route("/set_cookies")
def set_cookie():
    resp = make_response("success")
    resp.set_cookie("skysys", "skysys",max_age=3600)
    return resp

@app.route("/get_cookies")
def get_cookie():
    cookie_1 = request.cookies.get("skysys")  # 获取名字为Itcast_1对应cookie的值
    return cookie_1

@app.route("/delete_cookies")
def delete_cookie():
    resp = make_response("del success")
    resp.delete_cookie("skysys")

    return resp

if __name__ == '__main__':
    app.run(debug=True)

Sessions

Session数据存储在服务器上。会话是客户端登录到服务器并注销服务器的时间间隔。需要在该会话中保存的数据会存储在服务器上的临时目录中。
每个客户端的会话分配会话ID,会话数据存储在cookie的顶部,服务器以加密方式对其进行签名。对于此加密,Flask应用程序需要一个定义的密钥。
demo:

from flask import Flask
from flask import render_template
from flask import request
from flask import make_response
from flask import Flask, session, redirect, url_for, escape, request
app = Flask(__name__)
app.secret_key = 'fkdjsafjdkfdlkjfadskjfadskljdsfklj'
@app.route('/')
def index():
    if 'username' in session:
        username = session['username']
        return '登录用户名是:' + username + '<br>' + \
                 "<b><a href = '/logout'>点击这里注销</a></b>"
    return "您暂未登录, <br><a href = '/login'></b>" + \

         "点击这里登录</b></a>"

@app.route('/login', methods = ['GET', 'POST'])

def login():

    if request.method == 'POST':

        session['username'] = request.form['username']

        return redirect(url_for('index'))

    return '''

   <form action = "" method = "post">

      <p><input type ="text" name ="username"/></p>

      <p><input type ="submit" value ="登录"/></p>

   </form>

   '''

@app.route('/logout')

def logout():

   # remove the username from the session if it is there

   session.pop('username', None)

   return redirect(url_for('index'))

if __name__ == '__main__':

    app.run(debug = True)

重定向和错误

redirect()函数返回一个响应对象,并将用户重定向到具有指定状态代码的另一个目标位置。

Flask.redirect(location, statuscode, response)
  • location参数是应该重定向响应的URL。
  • statuscode发送到浏览器标头,默认为302。
  • response参数用于实例化响应。

以下状态代码已标准化:(默认为302)
HTTP_300_MULTIPLE_CHOICES
HTTP_301_MOVED_PERMANENTLY
HTTP_302_FOUND
HTTP_303_SEE_OTHER
HTTP_304_NOT_MODIFIED
HTTP_305_USE_PROXY
HTTP_306_RESERVED
HTTP_307_TEMPORARY_REDIRECT
带有错误代码的abort()函数Flask.abort(code)
400 - 用于错误请求
401 - 用于未身份验证的
403 - Forbidden
404 - 未找到
406 - 表示不接受
415 - 用于不支持的媒体类型
429 - 请求过多

from flask import Flask, redirect, url_for, render_template, request, abort
app = Flask(__name__)

@app.route('/')
def index():
   return render_template('log_in.html')

@app.route('/login',methods = ['POST', 'GET'])
def login():
   if request.method == 'POST':
      if request.form['username'] == 'admin' :
         return redirect(url_for('success'))
      else:
         abort(401)
   else:
      return redirect(url_for('index'))

@app.route('/success')
def success():
   return 'logged in successfully'

if __name__ == '__main__':
   app.run(debug = True)

消息闪现

一个好的基于 GUI 的应用程序会向用户提供有关交互的反馈。例如,桌面应用程序使用对话框或消息框,JavaScript 使用警报用于类似目的。
Flask 框架的闪现系统可以在一个视图中创建消息,并在名为 next 的视图函数中呈现它。
flash(message, category)方法将消息传递给下一个请求,该请求通常是一个模板。

  • message 参数是要闪现的实际消息。
  • category 参数是可选的。它可以是“error”,“info”或“warning”

删除消息:get_flashed_messages(with_categories, category_filter)
两个参数都是可选的。如果接收到的消息具有类别,则第一个参数是元组。第二个参数仅用于显示特定消息。

以下闪现在模板中接收消息:

{% with messages = get_flashed_messages() %}
   {% if messages %}
      {% for message in messages %}
         {{ message }}
      {% endfor %}
   {% endif %}
{% endwith %}

demo:

from flask import Flask, flash, redirect, render_template, request, url_for
app = Flask(name)
app.secret_key = 'random string'
@app.route('/')
def index():
    return render_template('index.html')
	

@app.route('/login', methods = ['GET', 'POST'])
def login():
    error = None


    if request.method == 'POST':
        if request.form['username'] != 'admin' or \
            request.form['password'] != 'admin':
            error = 'Invalid username or password. Please try again!'
        else:
            flash('You were successfully logged in')
            return redirect(url_for('index'))
	

    return render_template('login.html', error = error)


if name == "main":
    app.run(debug = True)

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Login</title>
</head>
<body>
    <form method = "post" action = "http://localhost:5000/login">
        <table>
            <tr>
                <td>Username</td>
                <td><input type = 'username' name = 'username'></td>
            </tr>
            <tr>
                <td>Password</td>
                <td><input type = 'password' name = 'password'></td>
            </tr>
            <tr>
                <td><input type = "submit" value = "Submit"></td>
            </tr>
        </table>
    </form>
    {% if error %}
        <p><strong>Error</strong>: {{ error }}</p>
    {% endif %}
</body>
</html>

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Index</title>
</head>
<body>
    {% with messages = get_flashed_messages() %}
         {% if messages %}
               {% for message in messages %}
                    <p>{{ message }}</p>
               {% endfor %}
         {% endif %}
    {% endwith %}
<h3>Welcome!</h3>
<a href = "{{ url_for('login') }}">login</a>
</body>
</html>

文件上传

在 Flask 中处理文件上传非常简单。它需要一个 HTML 表单,Enctype​ 属性设置为“​multipart / form-data”​,将文件发布到 URL。URL 处理程序从 ​request.files[]​ 对象中提取文件,并将其保存到所需的位置。
每个上传的文件首先会保存在服务器上的临时位置,然后将其实际保存到它的最终位置。目标文件的名称可以是硬编码的,也可以从 ​request.files[file] ​对象的​ filename ​属性中获取。但是,建议使用 ​secure_filename()​ 函数获取它的安全版本。
可以在 Flask 对象的配置设置中定义默认上传文件夹的路径和上传文件的最大大小。

app.config[‘UPLOAD_FOLDER’] 定义上传文件夹的路径 app.config[‘MAX_CONTENT_LENGTH’] 指定要上传的文件的最大大小(以字节为单位)

<html>
<head>
  <title>File Upload</title>
</head>
<body>
    <form action="http://localhost:5000/uploader" method="POST" enctype="multipart/form-data">
        <input type="file" name="file" accept=".jpg,.png" />
        <input type="submit" />
    </form>
</body>
</html>
from flask import Flask, render_template, request
from werkzeug.utils import secure_filename
import os

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = 'upload/'

@app.route('/upload')
def upload_file():
   return render_template('upload.html')

@app.route('/uploader', methods = ['GET', 'POST'])
def uploader():
   if request.method == 'POST':
      f = request.files['file']
      f.save(os.path.join(app.config['UPLOAD_FOLDER'],secure_filename(f.filename)))
      return 'file uploaded successfully'

if __name__ == '__main__':
   app.run()

注意:app.config[‘UPLOAD_FOLDER’] = 'upload/'upload 前面不能加“/”。

extensions 扩展

Flask扩展为Flask框架提供了可扩展性。Flask扩展是一个Python模块,它向Flask应用程序添加了特定类型的支持。Flask Extension Registry(Flask扩展注册表)是一个可用的扩展目录。可以通过pip实用程序下载所需的扩展名。
比较重要的扩展:

  • Flask Mail - 为Flask应用程序提供SMTP接口
  • Flask WTF - 添加WTForms的渲染和验证
  • Flask SQLAlchemy - 为Flask应用程序添加SQLAlchemy支持(ORM框架)
  • Flask Sijax - Sijax的接口 - Python/jQuery库,使AJAX易于在Web应用程序中使用

每种类型的扩展通常提供有关其用法的大量文档。由于扩展是一个Python模块,因此需要导入它才能使用它。Flask扩展名通常命名为flask-foo。导入的操作如下:

from flask_foo import [class, function]
# flask 0.7以后
from flask.ext import foo

# 对于此用法,需要激活兼容性模块
import flaskext_compat
flaskext_compat.activate()
from flask.ext import foo

Mail 邮件

pip install Flask-Mail

设置以下应用程序参数的值来配置Flask-Mail
MAIL_SERVER 电子邮件服务器的名称/IP地址
MAIL_PORT 使用的服务器的端口号
MAIL_USE_TLS 启用/禁用传输安全层加密
MAIL_USE_SSL 启用/禁用安全套接字层加密
MAIL_DEBUG 调试支持。默认值是Flask应用程序的调试状态
MAIL_USERNAME 发件人的用户名
MAIL_PASSWORD 发件人的密码
MAIL_DEFAULT_SENDER 设置默认发件人
MAIL_MAX_EMAILS 设置要发送的最大邮件数
MAIL_SUPPRESS_SEND 如果app.testing设置为true,则发送被抑制
MAIL_ASCII_ATTACHMENTS 如果设置为true,则附加的文件名将转换为ASCII

from flask_mail import Mail, Message

app =Flask(__name__)
mail=Mail(app)

app.config['MAIL_SERVER']='smtp.gmail.com'
app.config['MAIL_PORT'] = 465
app.config['MAIL_USERNAME'] = 'yourId@gmail.com'
app.config['MAIL_PASSWORD'] = '*****'
app.config['MAIL_USE_TLS'] = False
app.config['MAIL_USE_SSL'] = True
mail = Mail(app)

@app.route("/")
def index():
   msg = Message('Hello', sender = 'yourId@gmail.com', recipients = ['id1@gmail.com'])
   msg.body = "Hello Flask message sent from Flask-Mail"
   mail.send(msg)
   return "Sent"

if __name__ == '__main__':
   app.run(debug = True)

Message类方法
attach() - 为邮件添加附件。此方法采用以下参数:

  • filename - 要附加的文件的名称
  • content_type - MIME类型的文件
  • data - 原始文件数据
  • 处置 - 内容处置(如果有的话)。

add_recipient() - 向邮件添加另一个收件人

WTForms:一个灵活的表单,渲染和验证库

用户输入的数据以Http请求消息的形式通过GET或POST方法提交给服务器端脚本

  • 服务器端脚本必须从http请求数据重新创建表单元素。因此,实际上,表单元素被定义了两次 - 一次在HTML中,另一次在服务器端脚本中。
  • 使用HTML表单的另一个缺点是很难动态呈现表单元素。HTML本身无法验证用户的输入。

使用Flask-WTF,可以在Python脚本中定义表单字段,并使用HTML模板进行渲染。还可以将验证应用于WTF字段。

pip install flask-WTF

已安装的软件包包含一个Form类,该类必须用作用户定义表单的父级。WTForms包中包含各种表单字段的定义。下面列出了一些标准表单字段。
TextField 表示 <input type ='text'>HTML表单元素
BooleanField 表示<input type ='checkbox'> HTML表单元素
DecimalField 用于显示带小数的数字的文本字段
IntegerField 用于显示整数的文本字段
RadioField 表示<input type = 'radio'>HTML表单元素
SelectField 表示选择表单元素
TextAreaField 表示<textarea> HTML表单元素
PasswordField 表示<input type = 'password'> HTML表单元素
SubmitField 表示<input type = 'submit'>表单元素

例如,包含文本字段的表单可以设计如下:

from flask_wtf import Form
from wtforms import TextField

class ContactForm(Form):
   name = TextField("Name Of Student")

除了’name’字段,还会自动创建CSRF令牌的隐藏字段。这是为了防止Cross Site Request Forgery(跨站请求伪造)攻击。
渲染时,这将导致等效的HTML脚本,如下所示:

<input id = "csrf_token" name = "csrf_token" type = "hidden" />
<label for = "name">Name Of Student</label><br>
<input id = "name" name = "name" type = "text" value = "" />

在Flask应用程序中使用用户定义的表单类,并使用模板呈现表单。

from flask import Flask, render_template
from forms import ContactForm
app = Flask(__name__)
app.secret_key = 'development key'

@app.route('/contact')
def contact():
   form = ContactForm()
   return render_template('contact.html', form = form)

if __name__ == '__main__':
   app.run(debug = True)

WTForms包也包含验证器类。它对表单字段应用验证很有用。以下列表显示了常用的验证器。
DataRequired 检查输入字段是否为空
Email 检查字段中的文本是否遵循电子邮件ID约定
IPAddress 在输入字段中验证IP地址
Length 验证输入字段中的字符串的长度是否在给定范围内
NumberRange 验证给定范围内输入字段中的数字
URL 验证在输入字段中输入的URL

demo

forms.py - 表单的设计

from flask_wtf import Form
from wtforms import TextField, IntegerField, TextAreaField, SubmitField, RadioField,
   SelectField

from wtforms import validators, ValidationError

class ContactForm(Form):
   name = TextField("Name Of Student",[validators.Required("Please enter 
      your name.")])
   Gender = RadioField('Gender', choices = [('M','Male'),('F','Female')])
   Address = TextAreaField("Address")
   
   email = TextField("Email",[validators.Required("Please enter your email address."),
      validators.Email("Please enter your email address.")])
   
   Age = IntegerField("age")
   language = SelectField('Languages', choices = [('cpp', 'C&plus;&plus;'), 
      ('py', 'Python')])
   submit = SubmitField("Send")

应用脚本

from flask import Flask, render_template, request, flash
from forms import ContactForm
app = Flask(__name__)
app.secret_key = 'development key'

@app.route('/contact', methods = ['GET', 'POST'])
def contact():
   form = ContactForm()
   
   if request.method == 'POST':
      if form.validate() == False:
         flash('All fields are required.')
         return render_template('contact.html',form = form)
      else:
         return render_template('success.html')
      elif request.method == 'GET':
         return render_template('contact.html',form = form)

if __name__ == '__main__':
   app.run(debug = True)

模板(contact.html)

<!doctype html>
<html>
   <body>
   
      <h2 style = "text-align: center;">Contact Form</h2>
		
      {% for message in form.name.errors %}
         <div>{{ message }}</div>
      {% endfor %}
      
      {% for message in form.email.errors %}
         <div>{{ message }}</div>
      {% endfor %}
      
      <form action = "http://localhost:5000/contact" method = post>
         <fieldset>
            <legend>Contact Form</legend>
            {{ form.hidden_tag() }}
            
            <div style = font-size:20px; font-weight:bold; margin-left:150px;>
               {{ form.name.label }}<br>
               {{ form.name }}
               <br>
               
               {{ form.Gender.label }} {{ form.Gender }}
               {{ form.Address.label }}<br>
               {{ form.Address }}
               <br>
               
               {{ form.email.label }}<br>
               {{ form.email }}
               <br>
               
               {{ form.Age.label }}<br>
               {{ form.Age }}
               <br>
               
               {{ form.language.label }}<br>
               {{ form.language }}
               <br>
               {{ form.submit }}
            </div>
            
         </fieldset>
      </form>
      
   </body>
</html>

SQLite

在 Flask 中,通过使用特殊的 g 对象可以使用 before_request() 和 teardown_request() 在请求开始前打开数据库连接,在请求结束后关闭连接。

import sqlite3
from flask import g

DATABASE = '/path/to/database.db'

def connect_db():
    return sqlite3.connect(DATABASE)

@app.before_request
def before_request():
    g.db = connect_db()

@app.teardown_request
def teardown_request(exception):
    if hasattr(g, 'db'):
        g.db.close()

销毁函数是一定会被执行的。即使请求前处理器执行失败或根本没有执行, 销毁函数也会被执行。因此,我们必须保证在关闭数据库连接之前数据库连接是存在 的。

按需连接

上述方式的缺点是只有在 Flask 执行了请求前处理器时才有效。如果你尝试在脚本或者 Python 解释器中使用数据库,那么你必须这样来执行数据库连接代码:

with app.test_request_context():
    app.preprocess_request()
    # now you can use the g.db object

这样虽然不能排除对请求环境的依赖,但是可以按需连接数据库:

def get_connection():
    db = getattr(g, '_db', None)
    if db is None:
        db = g._db = connect_db()
    return db

这样做的缺点是必须使用 db = get_connection() 来代替直接使用 g.db 。

简化查询

在每个请求处理函数中可以通过访问 g.db 来得到当前打开的数据库连接。为了 简化 SQLite 的使用,这里有一个有用的辅助函数:

def query_db(query, args=(), one=False):
    cur = g.db.execute(query, args)
    rv = [dict((cur.description[idx][0], value)
               for idx, value in enumerate(row)) for row in cur.fetchall()]
    return (rv[0] if rv else None) if one else rv

参考文档

  1. flask教程 https://www.w3cschool.cn/flask/
  2. https://dormousehole.readthedocs.io/en/latest/

参考文章

  1. Flask 学着用模板 render_template
  2. flask 中的render_template 跳转到文件夹问题(疑惑)

附录 - Flask extensions recommendation

  • Flask-Babel 国际化、本地支持
  • Marshmallow 序列化/反序列化Python对象,在API中提供资源的不同表述
  • Celery 处理后台作业的任务队列
  • Frozen-Flask 把flask应用转化成静态网站
  • Flask-DebugToolbar flask的浏览器调试工具
  • Flask-Assets 合并、压缩、编译CSS、JS静态资源文件
  • Flask-Session
  • Flask-SocketIO flask实现SocketIO服务器,支持Websocket、长轮询

相关文章:

  • 项目上线流程
  • 刻意练习
  • 【成长经历】【钉钉前端】 高中毕业-如何用 15 年从小白到技术专家
  • 转正实习、春招、秋招、校招、社招的4个区别和陷阱
  • 前端工具方法
  • 【转载】8年工作的总结
  • 【转】计算机保研经验总结
  • IIS 500.19错误的解决思路
  • el-form内el-select与el-input纵向不对齐的问题
  • MVVM模式的理解
  • 吞吐量(TPS)、QPS、并发数、响应时间(RT)概念
  • C#学习笔记
  • 微信小程序帐号被系统冻结问题汇总
  • 微信小程序开发工具win10下编译非常慢解决方法
  • Nodejs热更新
  • Apache Pulsar 2.1 重磅发布
  • bearychat的java client
  • CentOS从零开始部署Nodejs项目
  • Docker下部署自己的LNMP工作环境
  • echarts的各种常用效果展示
  • Java多态
  • PHP 7 修改了什么呢 -- 2
  • python学习笔记 - ThreadLocal
  • Sequelize 中文文档 v4 - Getting started - 入门
  • vuex 学习笔记 01
  • vue中实现单选
  • webpack4 一点通
  • Zsh 开发指南(第十四篇 文件读写)
  • 基于Javascript, Springboot的管理系统报表查询页面代码设计
  • 力扣(LeetCode)21
  • 我的面试准备过程--容器(更新中)
  • 由插件封装引出的一丢丢思考
  • 蚂蚁金服CTO程立:真正的技术革命才刚刚开始
  • ​configparser --- 配置文件解析器​
  • # 计算机视觉入门
  • #FPGA(基础知识)
  • #我与Java虚拟机的故事#连载02:“小蓝”陪伴的日日夜夜
  • (13)Hive调优——动态分区导致的小文件问题
  • (51单片机)第五章-A/D和D/A工作原理-A/D
  • (bean配置类的注解开发)学习Spring的第十三天
  • (C语言版)链表(三)——实现双向链表创建、删除、插入、释放内存等简单操作...
  • (js)循环条件满足时终止循环
  • (博弈 sg入门)kiki's game -- hdu -- 2147
  • (超详细)2-YOLOV5改进-添加SimAM注意力机制
  • (翻译)Entity Framework技巧系列之七 - Tip 26 – 28
  • (附源码)ssm高校实验室 毕业设计 800008
  • (附源码)计算机毕业设计SSM疫情社区管理系统
  • (转)大型网站的系统架构
  • (转)负载均衡,回话保持,cookie
  • ****** 二 ******、软设笔记【数据结构】-KMP算法、树、二叉树
  • .NET CORE 3.1 集成JWT鉴权和授权2
  • .net core 依赖注入的基本用发
  • .net 桌面开发 运行一阵子就自动关闭_聊城旋转门家用价格大约是多少,全自动旋转门,期待合作...
  • .Net转Java自学之路—SpringMVC框架篇六(异常处理)
  • /var/log/cvslog 太大