>>> from  env_helper import info; info()
页面更新时间: 2024-04-07 23:42:19
运行环境:
    Linux发行版本: Debian GNU/Linux 12 (bookworm)
    操作系统内核: Linux-6.1.0-18-amd64-x86_64-with-glibc2.36
    Python版本: 3.11.2

2.8. 使用 Peewee 扩展 SQLite 的使用

默认值 SqliteDatabase 已经包含许多特定于sqlite的功能:

  • 有关使用SQLite的一般说明 .

  • 使用PRAGMA语句配置SQLite.

  • 用户定义的函数,聚合和排序规则 .

  • 交易锁定模式 .

这个 playhouse.sqlite_ext 包括更多的sqlite功能,包括:

  • 全文搜索.

  • JSON扩展集成.

  • 闭包表扩展支持.

  • LSM1扩展支持.

  • 用户定义表功能.

  • 使用备份API支持联机备份: backup_to_file()

  • BLOB API支持,用于高效的二进制数据存储. .

  • 其他帮助, 包括布卢姆过滤器,更多。

class JSONField(json_dumps=None, json_loads=None, ...) 适用于存储JSON数据的字段类,具有设计用于 json1 extension .

2.8.1. 介绍

添加了sqlite 3.9.0扩展库形式的JSON支持。 SQLite json1扩展提供了许多用于处理JSON数据的辅助函数。 这些API作为特殊字段类型JSONField的方法公开。

要访问或修改JSON结构中的特定对象键或数组索引,可以处理 JSONField 就像是一本字典、一张清单。

参数

json_dumps – (可选)用于将数据序列化为JSON字符串的函数。如果没有提供,将使用stdlib json.dumps .

json_loads – (可选)用于将JSON反序列化为Python对象的函数。如果没有提供,将使用stdlib json.loads .

注解

要自定义JSON序列化或反序列化,可以指定自定义 json_dumps 和 json_loads 召唤物。这些函数应该分别接受一个参数:要序列化的对象和JSON字符串。要修改stdlib json函数的参数,可以使用 functools.partial :

# 不要转义unicode代码点。
my_json_dumps = functools.partial(json.dumps, ensure_ascii=False)

class SomeModel(Model):
    # 指定我们的自定义序列化功能。
    json_data = JSONField(json_dumps=my_json_dumps)

2.8.2. 实例说明

让我们来看一些使用sqlite json1扩展和peewee的例子。在这里,我们将准备一个数据库和一个简单的模型来测试 json1 extension :

>>> from playhouse.sqlite_ext import *
>>> db = SqliteExtDatabase(':memory:')
>>> class KV(Model):
>>>     key = TextField()
>>>     value = JSONField()
>>>     class Meta:
>>>         database = db
>>> KV.create_table()

存储数据可以按预期工作。无需将字典或列表序列化为json,因为这是由peewee自动完成的:

>>> KV.create(key='a', value={'k1': 'v1'})
<KV: 1>
>>> KV.get(KV.key == 'a').value
{'k1': 'v1'}

我们可以使用字典查找访问JSON数据的特定部分:

>>> KV.get(KV.value['k1'] == 'v1').key
'a'

可以使用 update() 方法。注意“k1=v1”保留:

>>> KV.update(value=KV.value.update({'k2': 'v2', 'k3': 'v3'})).execute()
1
>>> KV.get(KV.key == 'a').value
{'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}

我们还可以自动更新现有数据,或者通过将键的值设置为来删除键。在下面的示例中,我们将更新“k1”的值并删除“k3”(“k2”不会被修改):

>>> KV.update(value=KV.value.update({'k1': 'v1-x', 'k3': None})).execute()
1
>>> KV.get(KV.key == 'a').value
{'k1': 'v1-x', 'k2': 'v2'}

我们还可以使用 set() 方法:

>>> KV.update(value=KV.value['k1'].set('v1')).execute()
1
>>> KV.get(KV.key == 'a').value
{'k1': 'v1', 'k2': 'v2'}

这个 set() 除了标量值之外,方法还可以用于对象:

>>> KV.update(value=KV.value['k2'].set({'x2': 'y2'})).execute()
1
>>> KV.get(KV.key == 'a').value
{'k1': 'v1', 'k2': {'x2': 'y2'}}

也可以使用原子方式删除JSON数据的各个部分。 remove()

>>> KV.update(value=KV.value['k2'].remove()).execute()
1
>>> KV.get(KV.key == 'a').value
{'k1': 'v1'}

我们还可以使用 json_type() 方法:

>>>  KV.select(KV.value.json_type(), KV.value['k1'].json_type()).tuples()[:]
[('object', 'text')]

让我们添加一个嵌套值,然后看看如何使用 tree() 方法:

>>> KV.create(key='b', value={'x1': {'y1': 'z1', 'y2': 'z2'}, 'x2': [1, 2]})
<KV: 2>
>>> tree = KV.value.tree().alias('tree')
>>> query = KV.select(KV.key, tree.c.fullkey, tree.c.value).from_(KV, tree)
>>> query.tuples()[:]
[('a', '$', {'k1': 'v1'}),
 ('a', '$.k1', 'v1'),
 ('b', '$', {'x1': {'y1': 'z1', 'y2': 'z2'}, 'x2': [1, 2]}),
 ('b', '$.x1', {'y1': 'z1', 'y2': 'z2'}),
 ('b', '$.x1.y1', 'z1'),
 ('b', '$.x1.y2', 'z2'),
 ('b', '$.x2', [1, 2]),
 ('b', '$.x2[0]', 1),
 ('b', '$.x2[1]', 2)]

这个 tree()children() 方法是强大的。有关如何使用它们的详细信息,请参阅 json1 extension documentation .

还要注意的是 JSONField 查找可以链接:

>>> query = KV.select().where(KV.value['x1']['y1'] == 'z1')
>>> for obj in query:
>>>     print(obj.key, obj.value)
b {'x1': {'y1': 'z1', 'y2': 'z2'}, 'x2': [1, 2]}

有关详细信息,请参阅 sqlite json1 documentation .

2.8.3. 接口说明

__getitem__(item):参数item是访问JSON数据中的特定键或数组索引。函数返回公开对JSON数据访问的特殊对象。返回类型JSONPath.

访问JSON数据中的特定键或数组索引。返回A JSONPath 对象,它公开了读取或修改JSON对象特定部分的方便方法。

例子:

# If metadata contains {"tags": ["list", "of", "tags"]}, we can
# extract the first tag in this way:
Post.select(Post, Post.metadata['tags'][0].alias('first_tag'))

set(value[, as_json=None]): 参数 value, 标量值、列表或字典。as_json (bool) ,强制将该值视为JSON,在这种情况下,它将在python中预先序列化为JSON。默认情况下,列表和字典被视为要序列化的JSON,而字符串和整数则按原样传递。

设置存储在 JSONField .使用 json_set() 来自JSON1扩展的函数。

update(data):参数data, 要与当前存储在 JSONField . 要删除特定的密钥,请将该密钥设置为 None 在更新的数据中。

使用RFC-7396 merge patch算法将新数据合并到json值中以应用补丁( data 参数)。MergePatch可以添加、修改或删除JSON对象的元素,这意味着 update()是两者的通用替换 set()remove() . MergePatch将JSON数组对象视为原子对象,因此 update() 不能追加到数组,也不能修改数组的单个元素。

RFC-7396:该规范定义了JSON合并补丁程序格式和处理规则。 合并修补程序格式主要用于HTTP PATCH方法,作为描述对目标资源内容的一组修改的一种方式。

remove():删除存储在 JSONField .使用 json_remove 来自JSON1扩展的函数。

json_type(): 返回一个标识列中存储的值类型的字符串。返回的类型将是以下类型之一:

对象;数组;整数;真实的;真;假;文本;null,字符串“空”表示实际的空值;None,实际空值表示找不到路径 。

使用 json_type 来自JSON1扩展的函数。

length(): 返回存储在列中的数组的长度。 使用 json_array_length 来自JSON1扩展的函数。

children(): 这个 children 函数对应于 json_each ,一个表值函数,用于遍历所提供的JSON值并返回顶级数组或对象的直接子级。如果指定了路径,则该路径将被视为最上面的元素。

调用返回的行 children() 具有以下属性:

  • key :当前元素相对于其父元素的键。

  • value :当前元素的值。

  • type :数据类型之一(请参见 json_type() )

  • atom :基元类型的标量值, NULL 用于数组和对象。

  • id :引用树中当前节点的唯一ID。

  • parent :包含节点的ID。

  • fullkey :描述当前元素的完整路径。

  • path :当前行的容器路径。

在内部,此方法使用 json_each (文档链接)json1扩展中的函数。

示例用法与 tree() 方法:

>>> from playhouse.sqlite_ext import *
>>> db = SqliteExtDatabase('keyd')
>>> class KeyData(Model):
>>>     key = TextField()
>>>     data = JSONField()
>>>     class Meta:
>>>         database = db
>>> KeyData.create_table()
>>> KeyData.create(key='a', data={'k1': 'v1', 'x1': {'y1': 'z1'}})
>>> KeyData.create(key='b', data={'x1': {'y1': 'z1', 'y2': 'z2'}})
<KeyData: 4>

我们将在KeyData模型中查询键以及其数据字段中的所有顶级键和值。

>>> kd = KeyData.data.children().alias('children')
>>> query = (KeyData
>>>          .select(kd.c.key, kd.c.value, kd.c.fullkey)
>>>          .from_(KeyData, kd)
>>>          .order_by(kd.c.key)
>>>          .tuples())
>>> print(query[:])
[('k1', 'v1', '$.k1'), ('k1', 'v1', '$.k1'), ('x1', '{"y1":"z1"}', '$.x1'), ('x1', '{"y1":"z1","y2":"z2"}', '$.x1'), ('x1', '{"y1":"z1"}', '$.x1'), ('x1', '{"y1":"z1","y2":"z2"}', '$.x1')]

这个 tree() 函数对应于 json_tree ,一个表值函数,它递归地遍历所提供的JSON值,并返回有关每个级别的键的信息。如果指定了路径,则该路径将被视为最上面的元素。

调用返回的行 tree() 具有与调用返回的行相同的属性 children()

  • key :当前元素相对于其父元素的键。

  • value :当前元素的值。

  • type :数据类型之一(请参见 json_type() )

  • atom :基元类型的标量值, NULL 用于数组和对象。

  • id :引用树中当前节点的唯一ID。

  • parent :包含节点的ID。

  • fullkey :描述当前元素的完整路径。

  • path :当前行的容器路径。

在内部,此方法使用 json_tree (文档链接)json1扩展中的函数。

示例用法:

>>> from playhouse.sqlite_ext import *
>>> db = SqliteExtDatabase('keyd2')
>>> class KeyData2(Model):
>>>         key = TextField()
>>>         data = JSONField()
>>>         class Meta:
>>>             database = db
>>> KeyData2.create_table()
>>> KeyData2.create(key='a', data={'k1': 'v1', 'x1': {'y1': 'z1'}})
>>> KeyData2.create(key='b', data={'x1': {'y1': 'z1', 'y2': 'z2'}})
<KeyData2: 4>

我们将递归地查询KeyData2模型中的键以及其数据字段中的所有键和值。

>>> kd = KeyData2.data.tree().alias('tree')
>>> query = (KeyData2.select(kd.c.key, kd.c.value, kd.c.fullkey)
>>>          .from_(KeyData2, kd)
>>>          .order_by(kd.c.key)
>>>          .tuples())
>>> print(query[:])
[(None, '{"k1":"v1","x1":{"y1":"z1"}}', '$'), (None, '{"x1":{"y1":"z1","y2":"z2"}}', '$'), (None, '{"k1":"v1","x1":{"y1":"z1"}}', '$'), (None, '{"x1":{"y1":"z1","y2":"z2"}}', '$'), ('k1', 'v1', '$.k1'), ('k1', 'v1', '$.k1'), ('x1', '{"y1":"z1"}', '$.x1'), ('x1', '{"y1":"z1","y2":"z2"}', '$.x1'), ('x1', '{"y1":"z1"}', '$.x1'), ('x1', '{"y1":"z1","y2":"z2"}', '$.x1'), ('y1', 'z1', '$.x1.y1'), ('y1', 'z1', '$.x1.y1'), ('y1', 'z1', '$.x1.y1'), ('y1', 'z1', '$.x1.y1'), ('y2', 'z2', '$.x1.y2'), ('y2', 'z2', '$.x1.y2')]