【Python百日进阶-Web开发-Peewee】Day283 - Peewee 的扩展(二)apsw/sqlcipher/postgresql

13.4 apsw,一个高级的 sqlite 驱动程序

该apsw_ext模块包含一个适合与 apsw sqlite 驱动程序一起使用的数据库类。

APSW 项目页面:https 😕/github.com/rogerbinns/apsw

APSW 是一个非常简洁的库,它在 SQLite 的 C 接口之上提供了一个精简的包装器,从而可以使用 SQLite 的所有高级功能。

以下是使用 APSW 的几个原因,摘自文档:

  • APSW 提供了 SQLite 的所有功能,包括虚拟表、虚拟文件系统、blob i/o、备份和文件控制。
  • 可以跨线程共享连接,而无需任何额外的锁定。
  • 事务由您的代码显式管理。
  • APSW 可以处理嵌套事务。
  • Unicode 被正确处理。
  • APSW 更快。
    有关 apsw 和 pysqlite 之间差异的更多信息,请查看apsw 文档

13.4.1 如何使用 APSW 数据库

from apsw_ext import *

db = APSWDatabase(':memory:')

class BaseModel(Model):
    class Meta:
        database = db

class SomeModel(BaseModel):
    col1 = CharField()
    col2 = DateTimeField()

13.4.2 apsw_ext API 说明

APSWDatabase扩展SqliteExtDatabase并继承了其高级功能。

class APSWDatabase(database,**connect_kwargs )

参数:

  • database ( string ) – sqlite 数据库的文件名
  • connect_kwargs – 打开连接时传递给 apsw 的关键字参数
register_module( mod_name , mod_inst )

提供一种全局注册模块的方法。有关详细信息,请参阅有关虚拟表的文档。

参数:

  • mod_name ( string ) – 用于模块的名称
  • mod_inst ( object ) – 实现Virtual Table接口的对象
unregister_module( mod_name )

注销一个模块。

参数: 模组名称( string ) – 用于模块的名称

笔记
请务必使用模块中Field定义的子类apsw_ext ,因为它们将正确处理调整数据类型以进行存储。
例如,不要使用peewee.DateTimeField,请确保您正在导入和使用playhouse.apsw_ext.DateTimeField.

13.5 Sqlcipher 后端

笔记
尽管此扩展的代码很短,但尚未经过适当的同行评审,并且可能引入了漏洞。

另请注意,此代码依赖于sqlcipher3(python 绑定)和sqlcipher,其中的代码也可能存在漏洞,但由于这些是广泛使用的加密模块,我们可以预期那里的“短零日”。

13.5.1 sqlcipher_ext API 说明

class SqlCipherDatabase(database, passphrase, **kwargs)

SqliteDatabase存储加密数据库的子类。它没有使用标准sqlite3后端,而是使用sqlcipher3 : sqlcipher的python 包装器,而后者又是一个加密包装器sqlite3,因此 API与’s 相同,SqliteDatabase除了对象构造参数:

参数:

  • database – 要打开 [或创建] 的加密数据库文件名的路径。
  • passphrase – 数据库加密密码:至少应为 8 个字符长,但强烈建议在您的实现中强制执行更好的密码强度 标准。
    如果该database文件不存在,它将通过从passhprase.
    尝试打开现有数据库时,passhprase应与创建时使用的数据库相同。如果密码不正确,首次尝试访问数据库时将引发错误。

rekey(passphrase)

参数: passphrase( str ) – 数据库的新密码。
更改数据库的密码。

笔记
可以使用许多扩展 PRAGMA 来配置 SQLCipher。可以在SQLCipher 文档中找到 PRAGMA 列表及其描述。
例如,指定密钥派生的 PBKDF2 迭代次数(SQLCipher 3.x 中为 64K,SQLCipher 4.x 中默认为 256K):

# Use 1,000,000 iterations.
db = SqlCipherDatabase('my_app.db', pragmas={'kdf_iter': 1000000})

要使用 16KB 的密码页面大小和 10,000 个页面的缓存大小:

db = SqlCipherDatabase('my_app.db', passphrase='secret!!!', pragmas={
    'cipher_page_size': 1024 * 16,
    'cache_size': 10000})  # 10,000 16KB pages, or 160MB.

提示用户输入密码的示例:

db = SqlCipherDatabase(None)

class BaseModel(Model):
    """Parent for all app's models"""
    class Meta:
        # We won't have a valid db until user enters passhrase.
        database = db

# Derive our model subclasses
class Person(BaseModel):
    name = TextField(primary_key=True)

right_passphrase = False
while not right_passphrase:
    db.init(
        'testsqlcipher.db',
        passphrase=get_passphrase_from_user())

    try:  # Actually execute a query against the db to test passphrase.
        db.get_tables()
    except DatabaseError as exc:
        # This error indicates the password was wrong.
        if exc.args[0] == 'file is encrypted or is not a database':
            tell_user_the_passphrase_was_wrong()
            db.init(None)  # Reset the db.
        else:
            raise exc
    else:
        # The password was correct.
        right_passphrase = True

另见:一个稍微复杂的例子

13.6 Postgresql 扩展

postgresql 扩展模块提供了许多“postgres-only”功能,目前:

  • json 支持,包括Postgres 9.4 的jsonb。
  • hstore 支持
  • server-side cursors 服务器端游标
  • full-text search 全文检索
  • ArrayField字段类型,用于存储数组。
  • HStoreField字段类型,用于存储键/值对。
  • IntervalField字段类型,用于存储timedelta对象。
  • JSONField字段类型,用于存储 JSON 数据。
  • BinaryJSONField 数据类型的jsonbJSON字段类型。
  • TSVectorField字段类型,用于存储全文搜索数据。
  • DateTimeTZField字段类型,时区感知的日期时间字段。
    将来我想添加对更多 postgresql 功能的支持。如果您希望添加某个特定功能,请 打开 Github 问题。

警告
为了开始使用下面描述的功能,您需要使用扩展PostgresqlExtDatabase类而不是PostgresqlDatabase

下面的代码将假设您使用以下数据库和基本模型:

from playhouse.postgres_ext import *

ext_db = PostgresqlExtDatabase('peewee_test', user='postgres')

class BaseExtModel(Model):
    class Meta:
        database = ext_db

13.6.1 JSON 支持

peewee 对 Postgres 的原生 JSON 数据类型有基本的支持,格式为 JSONField. 从 2.4.7 版本开始,peewee 还支持 Postgres 9.4 二进制 jsonjsonb类型,通过BinaryJSONField.

警告
Postgres 从 9.2 开始原生支持 JSON 数据类型(在 9.3 中完全支持)。要使用此功能,您必须使用正确版本的 Postgres 和psycopg2 2.5 或更高版本。
要使用BinaryJSONField具有许多性能和查询优势的 ,您必须具有 Postgres 9.4 或更高版本。

笔记
您必须确保您的数据库是一个实例 PostgresqlExtDatabase才能使用JSONField。

下面是一个示例,说明如何使用 JSON 字段声明模型:

import json
import urllib2
from playhouse.postgres_ext import *

db = PostgresqlExtDatabase('my_database')

class APIResponse(Model):
    url = CharField()
    response = JSONField()

    class Meta:
        database = db

    @classmethod
    def request(cls, url):
        fh = urllib2.urlopen(url)
        return cls.create(url=url, response=json.loads(fh.read()))

APIResponse.create_table()

# Store a JSON response.
offense = APIResponse.request('http://crime-api.com/api/offense/')
booking = APIResponse.request('http://crime-api.com/api/booking/')

# Query a JSON data structure using a nested key lookup:
offense_responses = APIResponse.select().where(
    APIResponse.response['meta']['model'] == 'offense')

# Retrieve a sub-key for each APIResponse. By calling .as_json(), the
# data at the sub-key will be returned as Python objects (dicts, lists,
# etc) instead of serialized JSON.
q = (APIResponse
     .select(
       APIResponse.data['booking']['person'].as_json().alias('person'))
     .where(APIResponse.data['meta']['model'] == 'booking'))

for result in q:
    print(result.person['name'], result.person['dob'])

其BinaryJSONField工作原理与常规相同,支持相同的操作JSONField,但提供了几个额外的操作来测试遏制。使用二进制 json 字段,您可以测试您的 JSON 数据是否包含其他部分 JSON 结构 ( contains(), contains_any(), contains_all()),或者它是否是较大 JSON 文档的子集 ( contained_by())。

有关更多示例,请参阅下面的JSONField和 BinaryJSONFieldAPI 文档。

13.6.2 hstore 支持

Postgresql hstore 是一个嵌入式键/值存储。使用 hstore,您可以将任意键/值对与结构化关系数据一起存储在数据库中。

要使用hstore,您需要在实例化时指定一个附加参数PostgresqlExtDatabase:

# Specify "register_hstore=True":
db = PostgresqlExtDatabase('my_db', register_hstore=True)

目前该postgres_ext模块支持以下操作:

  • 存储和检索任意字典
  • 按键或部分字典过滤
  • 更新/添加一个或多个键到现有字典
  • 从现有字典中删除一个或多个键
  • 选择键、值或 zip 键和值
  • 检索一段键/值
  • 测试密钥是否存在
  • 测试一个键是否具有非 NULL 值
  • 49
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是使用 FastAPI 中 Tortoise-ORM、SQLAlchemy 和 peewee 进行 ORM 查询的示例。 ## Tortoise-ORM ```python from fastapi import FastAPI from tortoise import fields from tortoise.contrib.fastapi import register_tortoise, HTTPNotFoundError from tortoise.models import Model from tortoise import Tortoise class User(Model): id = fields.IntField(pk=True) name = fields.CharField(50) email = fields.CharField(50) class Meta: table = "users" app = FastAPI() @app.on_event("startup") async def startup(): await Tortoise.init( db_url="sqlite://db.sqlite3", modules={"models": ["main"]} ) await Tortoise.generate_schemas() @app.on_event("shutdown") async def shutdown(): await Tortoise.close_connections() @app.get("/users") async def get_users(): users = await User.all() return users @app.get("/users/{user_id}") async def get_user(user_id: int): user = await User.get_or_none(id=user_id) if user is None: raise HTTPNotFoundError return user register_tortoise( app, db_url="sqlite://db.sqlite3", modules={"models": ["main"]}, generate_schemas=True, add_exception_handlers=True ) ``` ## SQLAlchemy ```python from fastapi import FastAPI from sqlalchemy import create_engine, Column, Integer, String from sqlalchemy.orm import sessionmaker from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True, index=True) name = Column(String(50)) email = Column(String(50)) engine = create_engine("sqlite:///db.sqlite3") SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) app = FastAPI() @app.get("/users") async def get_users(): db = SessionLocal() users = db.query(User).all() db.close() return users @app.get("/users/{user_id}") async def get_user(user_id: int): db = SessionLocal() user = db.query(User).filter(User.id == user_id).first() db.close() if user is None: raise HTTPNotFoundError return user ``` ## peewee ```python from fastapi import FastAPI from peewee import SqliteDatabase, Model, CharField, IntegerField from playhouse.shortcuts import model_to_dict db = SqliteDatabase("db.sqlite3") class User(Model): id = IntegerField(primary_key=True) name = CharField() email = CharField() class Meta: database = db table_name = "users" app = FastAPI() @app.on_event("startup") def startup(): db.connect() db.create_tables([User]) @app.on_event("shutdown") def shutdown(): db.close() @app.get("/users") async def get_users(): users = [model_to_dict(user) for user in User.select()] return users @app.get("/users/{user_id}") async def get_user(user_id: int): user = User.get_or_none(User.id == user_id) if user is None: raise HTTPNotFoundError return model_to_dict(user) ``` 注意:以上示例中的代码仅用于演示 ORM 查询的基本用法,并且没有进行错误处理。在实际应用中,你应该根据需要添加适当的错误处理和安全性检查。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

岳涛@心馨电脑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值