13.19 连接池 Connection pool
该pool模块包含许多Database为 PostgreSQL、MySQL 和 SQLite 数据库提供连接池的类。池通过覆盖Database类上打开和关闭与后端连接的方法来工作。池可以指定连接被回收的超时时间,以及打开连接数的上限。
在多线程应用程序中,最多将打开max_connections 。每个线程(或者,如果使用 gevent,greenlet)将有自己的连接。
在单线程应用程序中,只会创建一个连接。它将不断被回收,直到它超过过时的超时时间或被明确关闭(使用.manual_close())。
默认情况下,您的应用程序需要做的就是确保在完成连接后关闭连接,并将它们返回到池中。对于 Web 应用程序,这通常意味着在请求开始时,您将打开一个连接,当您返回响应时,您将关闭该连接。
简单的 Postgres 池示例代码:
# Use the special postgresql extensions.
from playhouse.pool import PooledPostgresqlExtDatabase
db = PooledPostgresqlExtDatabase(
'my_app',
max_connections=32,
stale_timeout=300, # 5 minutes.
user='postgres')
class BaseModel(Model):
class Meta:
database = db
就是这样!如果您想对连接池进行更细粒度的控制,请查看连接管理部分。
13.19.1 Pool APIs
class PooledDatabase
class PooledDatabase(database[, max_connections=20[, stale_timeout=None[, timeout=None[, **kwargs]]]])
参数:
- database ( str ) – 数据库或数据库文件的名称。
- max_connections ( int ) – 最大连接数。提供None无限。
- stale_timeout ( int ) – 允许使用连接的秒数。
- timeout ( int ) – 池已满时阻塞的秒数。默认情况下,当池已满时,peewee 不会阻塞,而只是抛出异常。要无限期阻止,请将此值设置为0。
- kwargs – 传递给数据库类的任意关键字参数。
Mixin 类旨在与Database.
笔记
当连接超过stale_timeout时,连接不会完全关闭。相反,只有在请求新连接时才会关闭陈旧连接。
笔记
如果打开的连接数超过max_connections,则会引发ValueError 。
manual_close()
关闭当前打开的连接而不将其返回到池中。
close_idle()
关闭所有空闲连接。这不包括任何当前正在使用的连接——只包括那些以前创建但后来又返回到池中的连接。
close_stale([age=600])
参数: 年龄( int ) – 连接应被视为陈旧的年龄。
返回: 关闭的连接数。
关闭正在使用但超过给定年龄的连接。调用此方法时要小心!
close_all()
关闭所有连接。这包括当时可能正在使用的任何连接。调用此方法时要小心!
class PooledPostgresqlDatabase
class PooledPostgresqlDatabase
该子类PostgresqlDatabase混入PooledDatabase助手中。
class PooledPostgresqlExtDatabase
class PooledPostgresqlExtDatabase
该子类PostgresqlExtDatabase混入PooledDatabase助手中。它PostgresqlExtDatabase是 Postgresql 扩展模块的一部分,提供对许多 Postgres 特定功能的支持。
class PooledMySQLDatabase
class PooledMySQLDatabase
该子类MySQLDatabase混入PooledDatabase助手中。
class PooledSqliteDatabase
class PooledSqliteDatabase
SQLite 应用程序的持久连接。
class PooledSqliteExtDatabase
class PooledSqliteExtDatabase
SQLite 应用程序的持久连接,使用SQLite Extensions高级数据库驱动程序SqliteExtDatabase。
13.20 测试工具 Test Utils
包含在测试 peewee 项目时有用的实用程序。
class count_queries
class count_queries([only_select=False])
上下文管理器,它将计算在上下文中执行的查询数。
参数: only_select( bool ) – 只计算SELECT查询。
with count_queries() as counter:
huey = User.get(User.username == 'huey')
huey_tweets = [tweet.message for tweet in huey.tweets]
assert counter.count == 2
count
执行的查询数。
get_queries()
返回由 SQL 查询和参数列表组成的 2 元组列表。
assert_query_count(expected[, only_select=False])
AssertionError如果在装饰函数中执行的查询数量不等于预期数量,将引发的函数或方法装饰器。
class TestMyApp(unittest.TestCase):
@assert_query_count(1)
def test_get_popular_blogs(self):
popular_blogs = Blog.get_popular()
self.assertEqual(
[blog.title for blog in popular_blogs],
["Peewee's Playhouse!", "All About Huey", "Mickey's Adventures"])
这个函数也可以用作上下文管理器:
class TestMyApp(unittest.TestCase):
def test_expensive_operation(self):
with assert_query_count(1):
perform_expensive_operation()
13.21 Flask实用程序
该playhouse.flask_utils模块包含几个用于将 peewee 与Flask Web 框架集成的助手。
13.21.1 数据库包装器
该类FlaskDB是一个包装器,用于从 Flask 应用程序中配置和引用 Peewee 数据库。不要让它的名字欺骗你:它与 peewee 数据库不同。FlaskDB旨在从您的Flask应用程序中删除以下样板:
- 根据应用配置数据动态创建 Peewee 数据库实例。
- 创建一个基类,所有应用程序的模型都将从该基类继承。
- 在请求的开始和结束处注册钩子以处理打开和关闭数据库连接。
基本用法:
import datetime
from flask import Flask
from peewee import *
from playhouse.flask_utils import FlaskDB
DATABASE = 'postgresql://postgres:password@localhost:5432/my_database'
# If we want to exclude particular views from the automatic connection
# management, we list them this way:
FLASKDB_EXCLUDED_ROUTES = ('logout',)
app = Flask(__name__)
app.config.from_object(__name__)
db_wrapper = FlaskDB(app)
class User(db_wrapper.Model):
username = CharField(unique=True)
class Tweet(db_wrapper.Model):
user = ForeignKeyField(User, backref='tweets')
content = TextField()
timestamp = DateTimeField(default=datetime.datetime.now)
上面的代码示例将创建并实例化 PostgresqlDatabase由给定数据库 URL 指定的 peewee。请求钩子将被配置为在收到请求时建立连接,并在发送响应时自动关闭连接。最后, FlaskDB该类公开了一个FlaskDB.Model属性,该属性可用作应用程序模型的基础。
以下是访问包装器为您配置的包装 Peewee 数据库实例的方法FlaskDB:
# Obtain a reference to the Peewee database instance.
peewee_db = db_wrapper.database
@app.route('/transfer-funds/', methods=['POST'])
def transfer_funds():
with peewee_db.atomic():
# ...
return jsonify({'transfer-id': xid})
笔记
可以使用该FlaskDB.database属性访问实际的 peewee 数据库。
这是另一种配置 Peewee 数据库的方法FlaskDB:
app = Flask(__name__)
db_wrapper = FlaskDB(app, 'sqlite:///my_app.db')
虽然上面的示例显示了使用数据库 URL,但对于更高级的用法,您可以指定配置选项字典,或者简单地传入 peeweeDatabase实例:
DATABASE = {
'name': 'my_app_db',
'engine': 'playhouse.pool.PooledPostgresqlDatabase',
'user': 'postgres',
'max_connections': 32,
'stale_timeout': 600,
}
app = Flask(__name__)
app.config.from_object(__name__)
wrapper = FlaskDB(app)
pooled_postgres_db = wrapper.database
使用 peeweeDatabase对象:
peewee_db = PostgresqlExtDatabase('my_app')
app = Flask(__name__)
db_wrapper = FlaskDB(app, peewee_db)
13.21.2 带有应用程序工厂的数据库 Database with Application Factory
如果您更喜欢使用应用程序工厂模式,则FlaskDB该类实现一个init_app()方法。
作为工厂使用:
db_wrapper = FlaskDB()
# Even though the database is not yet initialized, you can still use the
# `Model` property to create model classes.
class User(db_wrapper.Model):
username = CharField(unique=True)
def create_app():
app = Flask(__name__)
app.config['DATABASE'] = 'sqlite:home/code/apps/my-database.db'
db_wrapper.init_app(app)
return app
13.21.3 查询实用程序 Query utilities
该flask_utils模块提供了几个帮助程序来管理 Web 应用程序中的查询。一些常见的模式包括:
get_object_or_404
get_object_or_404(query_or_model, *query)
参数:
- query_or_model –Model类或预过滤的SelectQuery.
- query- 任意复杂的 peewee 表达式。
检索与给定查询匹配的对象,或返回 404 not found 响应。一个常见的用例可能是博客的详细信息页面。您想要检索与给定 URL 匹配的帖子,或者返回 404。
例子:
@app.route('/blog/<slug>/')
def post_detail(slug):
public_posts = Post.select().where(Post.published == True)
post = get_object_or_404(public_posts, (Post.slug == slug))
return render_template('post_detail.html', post=post)
object_list
object_list(template_name, query[, context_variable='object_list'[, paginate_by=20[, page_var='page'[, check_bounds=True[, **kwargs]]]]])
参数:
- template_name – 要呈现的模板的名称。
- query --SelectQuery要分页的实例。
- context_variable – 用于分页对象列表的上下文变量名称。
- paginate_by – 每页的对象数。
- page_var –GET包含页面的参数的名称。
- check_bounds – 是否检查给定页面是否为有效页面。如果check_bounds是True并且指定了无效页面,则将返回 404。
- kwargs – 要传递到模板上下文的任意键/值对。
检索给定查询指定的对象的分页列表。分页对象列表将使用给定的上下文 context_variable,以及有关当前页面和页面总数的元数据,最后是作为关键字参数传递的任意上下文数据。
使用page GET参数指定页面,例如 /my-object-list/?page=3将返回对象的第三页。
例子:
@app.route('/blog/')
def post_index():
public_posts = (Post
.select()
.where(Post.published == True)
.order_by(Post.timestamp.desc()))
return object_list(
'post_index.html',
query=public_posts,
context_variable='post_list',
paginate_by=10)
该模板将具有以下上下文:
- post_list,其中包含最多 10 个帖子的列表。
- page,其中包含基于page GET参数值的当前页面。
- pagination, 一个PaginatedQuery实例。
class PaginatedQuery
class PaginatedQuery(query_or_model, paginate_by[, page_var='page'[, check_bounds=False]])
参数:
- query_or_model –包含您希望分页的记录集合的一个Model或一个实例。SelectQuery
- paginate_by – 每页的对象数。
- page_var –GET包含页面的参数的名称。
- check_bounds – 是否检查给定页面是否为有效页面。如果check_bounds是True并且指定了无效页面,则将返回 404。
辅助类根据GET参数执行分页。
get_page()
返回当前选择的页面,由 page_var GET参数值指示。如果没有显式选择页面,则此方法将返回 1,表示第一页。
get_page_count()
返回可能的页面总数。
get_object_list()
使用 的值get_page(),返回用户请求的对象页面。返回值是 SelectQuery带有适当LIMITandOFFSET 子句的 a。
如果check_bounds设置为True并且请求的页面不包含任何对象,则会引发 404。