文章目录
6.5 使用 MySQL
要连接到 MySQL 数据库,我们将使用MySQLDatabase. 在数据库名称之后,您可以指定将传递回驱动程序(MySQLdb 或 pymysql)的任意连接参数。
mysql_db = MySQLDatabase('my_database')
class BaseModel(Model):
"""A base model that will use our MySQL database"""
class Meta:
database = mysql_db
class User(BaseModel):
username = CharField()
# etc, etc
6.5.0 错误 2006:MySQL 服务器已消失
当 MySQL 终止空闲的数据库连接时,可能会发生此特定错误。这通常发生在不明确管理数据库连接的 Web 应用程序中。发生的情况是您的应用程序启动,打开一个连接以处理执行的第一个查询,并且由于该连接从未关闭,它保持打开状态,等待更多查询。
要解决此问题,请确保在需要执行查询时显式连接到数据库,并在完成后关闭连接。在 Web 应用程序中,这通常意味着您将在请求进入时打开连接,并在返回响应时关闭连接。
有关配置通用 Web 框架以管理数据库连接的示例,请参阅框架集成部分。
6.5.1 使用数据库 URL 连接
playhouse 模块Database URL提供了一个辅助connect() 函数,它接受一个数据库 URL 并返回一个Database 实例。
示例代码:
import os
from peewee import *
from playhouse.db_url import connect
# Connect to the database URL defined in the environment, falling
# back to a local Sqlite database if no database URL is specified.
db = connect(os.environ.get('DATABASE') or 'sqlite:///default.db')
class BaseModel(Model):
class Meta:
database = db
示例数据库 URL:
sqlite:///my_database.db
将为当前目录中的文件my_database.db
创建一个SqliteDatabase实例。sqlite:///:memory
:将创建一个内存SqliteDatabase实例。postgresql://postgres:my_password@localhost:5432/my_database
将创建一个PostgresqlDatabase实例。提供了用户名和密码,以及要连接的主机和端口。mysql://user:passwd@ip:port/my_dbMySQLDatabase
将为本地数据库my_db创建一个 MySQL 实例。- db_url 文档中的更多示例。
6.5.2 运行时数据库配置
有时数据库连接设置直到运行时才知道,当这些值可能从配置文件或环境中加载时。在这些情况下,您可以通过指定 为 database_name 来推迟None数据库的初始化。
database = PostgresqlDatabase(None) # Un-initialized database.
class SomeModel(Model):
class Meta:
database = database
如果您在数据库未初始化时尝试连接或发出任何查询,您将收到异常:
>>> database.connect()
Exception: Error, database not properly initialized before opening connection
要初始化数据库,请init()使用数据库名称和任何其他关键字参数调用该方法:
database_name = input('What is the name of the db? ')
database.init(database_name, host='localhost', user='postgres')
如需对初始化数据库的更多控制,请参阅下一节, 动态定义数据库。
6.5.3 动态定义数据库
为了更好地控制数据库的定义/初始化方式,您可以使用DatabaseProxy帮助程序。DatabaseProxy对象充当占位符,然后在运行时您可以将其换成不同的对象。在下面的示例中,我们将根据应用程序的配置方式换出数据库:
database_proxy = DatabaseProxy() # Create a proxy for our db.
class BaseModel(Model):
class Meta:
database = database_proxy # Use proxy for our DB.
class User(BaseModel):
username = CharField()
# Based on configuration, use a different database.
if app.config['DEBUG']:
database = SqliteDatabase('local.db')
elif app.config['TESTING']:
database = SqliteDatabase(':memory:')
else:
database = PostgresqlDatabase('mega_production_db')
# Configure our proxy to use the db we specified in config.
database_proxy.initialize(database)
警告
仅当您的实际数据库驱动程序在运行时发生变化时才使用此方法。例如,如果您的测试和本地开发环境在 SQLite 上运行,但您部署的应用程序使用
PostgreSQL,您可以使用 PostgreSQLDatabaseProxy在运行时换出引擎。但是,如果只有连接值在运行时发生变化,例如数据库文件的路径或数据库主机,则应改为使用 Database.init().
有关更多详细信息,请参阅运行时数据库配置。笔记
DatabaseProxy避免使用和替代使用Database.bind()相关方法来设置或更改数据库可能更容易。有关详细信息,请参阅在运行时设置数据库。
6.5.4 在运行时设置数据库
我们已经看到了使用 Peewee 配置数据库的三种方式:
# The usual way:
db = SqliteDatabase('my_app.db', pragmas={'journal_mode': 'wal'})
# Specify the details at run-time:
db = SqliteDatabase(None)
...
db.init(db_filename, pragmas={'journal_mode': 'wal'})
# Or use a placeholder:
db = DatabaseProxy()
...
db.initialize(SqliteDatabase('my_app.db', pragmas={'journal_mode': 'wal'}))
Peewee 还可以设置或更改模型类的数据库。Peewee 测试套件使用此技术在运行测试时将测试模型类绑定到各种数据库实例。
有两组互补的方法:
- Database.bind()-Model.bind()将一个或多个模型绑定到数据库。
- Database.bind_ctx()和Model.bind_ctx()- 与它们的bind()对应项相同,但返回一个上下文管理器,并且在仅应临时更改数据库时很有用。
例如,我们将声明两个模型而不指定任何数据库:
class User(Model):
username = TextField()
class Tweet(Model):
user = ForeignKeyField(User, backref='tweets')
content = TextField()
timestamp = TimestampField()
在运行时将模型绑定到数据库:
postgres_db = PostgresqlDatabase('my_app', user='postgres')
sqlite_db = SqliteDatabase('my_app.db')
# At this point, the User and Tweet models are NOT bound to any database.
# Let's bind them to the Postgres database:
postgres_db.bind([User, Tweet])
# Now we will temporarily bind them to the sqlite database:
with sqlite_db.bind_ctx([User, Tweet]):
# User and Tweet are now bound to the sqlite database.
assert User._meta.database is sqlite_db
# User and Tweet are once again bound to the Postgres database.
assert User._meta.database is postgres_db
Model.bind()和Model.bind_ctx()方法对于绑定给定的模型类的工作方式相同:
# Bind the user model to the sqlite db. By default, Peewee will also
# bind any models that are related to User via foreign-key as well.
User.bind(sqlite_db)
assert User._meta.database is sqlite_db
assert Tweet._meta.database is sqlite_db # Related models bound too.
# Here we will temporarily bind *just* the User model to the postgres db.
with User.bind_ctx(postgres_db, bind_backrefs=False):
assert User._meta.database is postgres_db
assert Tweet._meta.database is sqlite_db # Has not changed.
# And now User is back to being bound to the sqlite_db.
assert User._meta.database is sqlite_db
本文档的测试 Peewee 应用程序部分还包含一些使用这些bind()方法的示例。
6.5.5 线程安全和多数据库
如果您计划在多线程应用程序中在运行时更改数据库,将模型的数据库存储在线程本地将防止竞争条件。这可以通过自定义模型Metadata类来完成(请参阅 ThreadSafeDatabaseMetadata,包含在 中playhouse.shortcuts):
from peewee import *
from playhouse.shortcuts import ThreadSafeDatabaseMetadata
class BaseModel(Model):
class Meta:
# Instruct peewee to use our thread-safe metadata implementation.
model_metadata_class = ThreadSafeDatabaseMetadata
Database.bind()现在可以使用熟悉的或 Database.bind_ctx()方法在多线程环境中运行时安全地交换数据库。