将任意Protobuf消息对象转换为可用于Google Datastore的实体Protobuf对象的库。
项目描述
Protobuf消息到Google Datastore实体Protobuf消息翻译器
此库允许您将任意Protobuf消息对象存储在Google Datastore中。
它公开了将任意Protobuf消息对象转换为实体Protobuf对象(由Google Datastore使用)以及相反的方法。
它支持Google Datastore支持的所有原生类型。
原因,动机
如果您仅使用一种编程语言与Google Datastore一起工作,您可以利用该编程语言的多个Datastore ORM之一。这些ORM允许您为数据库模型定义模式,并使用本地编程语言类型与它们一起工作。
当您想要从多种编程语言中使用相同的集合数据存储实体时,这种方法就会崩溃。
针对该问题有多个解决方案,但一种方法是为编程语言无关的某种模型模式定义。
而这个库试图做到这一点。它利用原生protobuf消息定义作为数据库模型的模式。这样,这些定义可以被多种编程语言共享,每种语言只需要一个轻量级的翻译库(如这个库),它知道如何将任意Protobuf对象转换为实体Protobuf对象,反之亦然。
功能
目前,该库支持以下Protobuf字段类型和功能
-
所有简单类型(字符串、int32、int64、double、float、bytes、bool、枚举)
-
标量/容器类型(map、repeated)
-
来自 Protobuf 标准库的复杂类型(
google.protobuf.Timestamp
、google.protobuf.Struct
、google.types.LatLng
) -
使用导入和引用来自不同 Protobuf 定义文件中的类型。例如,你可以在文件
model1.proto
中有一个名为Model1DB
的 Protobuf 消息定义,该定义有一个字段引用了来自model2.proto
文件的Model2DB
。为了使其工作,你需要确保包含所有生成的 Protobuf Python 文件的根目录在
PYTHONPATH
中可用。例如,如果生成的文件写入到
my_app/generated/
,则my_app/generated/
需要包含在PYTHONPATH
中,并且此目录需要是一个 Python 包(它需要包含__init__.py
文件)。
有关 Google Datastore 支持的实际类型的更多信息,请参阅https://cloud.google.com/datastore/docs/concepts/entities#properties_and_value_types。
支持的 Python 版本
- Python 2.7
- Python 3.6
- Python 3.7
它也可能与 Python 3.4 和 3.5 一起工作,但我们没有对这些版本进行测试。
用法
此库公开了三个主要公共方法。
model_pb_to_entity_pb(model_pb, exclude_falsy_values=False, exclude_from_index=None)
此方法将任意 Protobuf 消息对象转换为可由 Google Datastore 使用的 Entity Protobuf 对象。
例如
from google.cloud import datastore
from google.protobuf.timestamp_pb2 import Timestamp
from protobuf_cloud_datastore_translator import model_pb_to_entity_pb
from generated.protobuf.models import my_model_pb2
# 1. Store your database model object which is represented using a custom Protobuf message class
# instance inside Google Datastore
# Create database model Protobuf instance
model_pb = my_model_pb2.MyModelDB()
# Other entity attributes
model_pb.key1 = 'value1'
model_pb.key2 = 200
model_pb.parameters['foo'] = 'bar'
model_pb.parameters['bar'] = 'baz'
start_time_timestamp = Timestamp()
start_time_timestamp.GetCurrentTime()
model_pb.start_time = start_time_timestamp
# Convert it to Entity Protobuf object which can be used with Google Datastore
entity_pb = model_pb_to_entity_pb(model_pb)
# Store it in the datastore
client = Client(...)
key = self.client.key('MyModelDB', 'some_primary_key')
entity_pb_translated.key.CopyFrom(key.to_protobuf())
entity = datastore.helpers.entity_from_protobuf(entity_pb)
client.put(entity)
model_pb_with_key_to_entity_pb(client, model_pb, exclude_falsy_values=False, exclude_from_index=None)
作为便利,此库还公开了 model_pb_to_entity_pb
方法。此方法假设你的 Protobuf 消息中有一个特殊的 key
字符串字段,该字段将作为 Entity 的主键。
在底层,此方法从传递给此方法的 client
对象中推断 Entity 组合主键的 project_id
和 namespace_id
部分。Entity kind
从 Protobuf 消息模型名称中推断出来。例如,如果 Protobuf 消息模型名称为 UserInfoDB
,则实体类型设置为 UserInfoDB
。
例如
from google.cloud import datastore
from protobuf_cloud_datastore_translator import model_pb_to_entity_pb
model_pb = my_model_pb2.MyModelDB()
model_pb.key = 'key-1234'
# set model fields
# ...
client = Client(project='my-project', namespace='my-namespace')
entity_pb = model_pb_to_entity_pb(model_pb)
# Store it in the datastore
entity = datastore.helpers.entity_from_protobuf(entity_pb)
client.put(entity)
# In this scenario, actual key would look the same if you manually constructed it like this:
key = client.key('MyModelDB', 'key-1234', project='my-project', namespace='my-namespace')
entity_pb_to_model_pb(model_pb_class, entity_pb, strict=False)
此方法将 Google Datastore 返回的原始 Entity Protobuf 对象转换为提供的 Protobuf 消息类。
默认情况下,Datastore Entity Protobuf 对象上找到但不在 Protobuf 消息类上的字段将被忽略。如果在这种情况下你想抛出异常,可以将 strict=True
参数传递给方法。
例如
key = client.key('MyModelDB', 'some_primary_key')
entity = client.get(key)
entity_pb = datastore.helpers.entity_to_protobuf(entity)
model_pb = entity_pb_to_model_pb(my_model_pb2.MyModelPB, entity_pb)
print(model_pb)
从索引中排除 Protobuf 模型字段
默认情况下,Google Cloud Datastore 会自动索引每个实体(模型)属性。
通常不希望或需要索引每个字段(实体属性)。它还有一些限制(例如,要索引的简单字段的长度限制为 1500
字节等)。此外,不必要的索引会导致存储空间消耗增加。
此库允许你使用 Protobuf 字段选项扩展在字段级别定义要排除从索引中的模型字段。
例如
syntax = "proto3";
import "google/protobuf/descriptor.proto";
// Custom Protobuf option which specifies which model fields should be excluded
// from index
// NOTE: Keep in mind that it's important not to change the option name
// ("exclude_from_index") since this library uses that special option name to
// determine if a field should be excluded from index.
extend google.protobuf.FieldOptions {
bool exclude_from_index = 50000;
}
message ExampleDBModelWithOptions1 {
string string_key_one = 1 [(exclude_from_index) = true];
string string_key_two = 2;
string string_key_three = 3 [(exclude_from_index) = true];
string string_key_four = 4;
int32 int32_field_one = 5;
int32 int32_field_two = 6 [(exclude_from_index) = true];
}
在此示例中,字段 string_key_one
、string_key_three
和 int32_field_two
不会被索引(https://cloud.google.com/datastore/docs/concepts/indexes#unindexed_properties)。
在此示例中,字段选项扩展定义在定义模型的同一文件中,但实际上你可能会在自定义 protobuf 文件(例如 field_options.proto
)中定义该扩展,并将该文件包含在其他包含你的数据库模型定义的文件中。
请注意,如果你在包中定义选项扩展,则该包需要与存储模型的包匹配。
例如
protobuf/models/field_options.proto
:
syntax = "proto3";
package models;
import "google/protobuf/descriptor.proto";
// Custom Protobuf option which specifies which model fields should be excluded
// from index
// NOTE: Keep in mind that it's important not to change the option name
// ("exclude_from_index") since this library uses that special option name to
// determine if a field should be excluded from index.
extend google.protobuf.FieldOptions {
bool exclude_from_index = 50000;
}
protobuf/models/my_model.proto
:
syntax = "proto3";
package models;
import "models/field_options.proto";
message ExampleDBModelWithOptions1 {
string string_key_one = 1 [(exclude_from_index) = true];
string string_key_two = 2;
string string_key_three = 3 [(exclude_from_index) = true];
string string_key_four = 4;
int32 int32_field_one = 5;
int32 int32_field_two = 6 [(exclude_from_index) = true];
}
示例
例如,关于 Protobuf 消息定义,请参阅 protobuf/
目录。
示例用法
from google.cloud import datastore
from protobuf_cloud_datastore_translator import model_pb_to_entity_pb
from protobuf_cloud_datastore_translator import entity_pb_to_model_pb
from generated.protobuf.models import my_model_pb2
# 1. Store your database model object which is represented using a custom Protobuf message class
# instance inside Google Datastore
# Create database model Protobuf instance
model_pb = my_model_pb2.MyModelDB()
model_pb.key1 = 'value1'
model_pb.key2 = 200
# Convert it to Entity Protobuf object which can be used with Google Datastore
entity_pb = model_pb_to_entity_pb(model_pb)
# Store it in the datastore
# To avoid conversion back and forth you can also use lower level client methods which
# work directly with the Entity Protobuf objects
# For information on the low level client usage, see
# https://github.com/GoogleCloudPlatform/google-cloud-datastore/blob/master/python/demos/trivial/adams.py#L66
client = Client(...)
key = self.client.key('MyModelDB', 'some_primary_key')
entity_pb_translated.key.CopyFrom(key.to_protobuf())
entity = datastore.helpers.entity_from_protobuf(entity_pb)
client.put(entity)
# 2. Retrieve entity from the datastore and convert it to your Protobuf DB model instance class
# Same here - you can also use low level client to retrieve Entity protobuf object directly and
# avoid unnecessary conversion round trip
key = client.key('MyModelDB', 'some_primary_key')
entity = client.get(key)
entity_pb = datastore.helpers.entity_to_protobuf(entity)
model_pb = entity_pb_to_model_pb(my_model_pb2.MyModelPB, entity_pb)
print(model_pb)
注意事项
默认值
在Protobuf语法版本3中,已删除“字段被设置”的概念,并将其与默认值的概念合并。这意味着即使在字段未设置的情况下,也会返回特定于该字段类型的默认值。
就这个库而言,这意味着当您转换/翻译未设置值的Protobuf对象时,翻译后的对象仍然会包含未设置的字段的默认值。
例如,这两个调用输出的最终结果将相同
# Field values are explicitly provided, but they match default values
example_pb = example_pb2.ExampleDBModel()
example_pb.bool_key = False
example_pb.string_key = ''
example_pb.int32_key = 0
example_pb.int64_key = 0
example_pb.double_key = 0.0
example_pb.float_key = 0.0
example_pb.enum_key = example_pb2.ExampleEnumModel.ENUM0
example_pb.bool_key = False
example_pb.bytes_key = b''
example_pb.null_key = 1
entity_pb_translated = model_pb_to_entity_pb(example_pb)
print(entity_pb_translated)
# No field values are provided, implicit default values are used during serialization
example_pb = example_pb2.ExampleDBModel()
entity_pb_translated = model_pb_to_entity_pb(example_pb)
print(entity_pb_translated)
如果您不希望在翻译的实体Protobuf对象上设置默认值并将其存储在数据存储中,您可以将exclude_falsy_values=True
参数传递给model_pb_to_entity_pb
方法。
详细信息请参阅
- https://developers.google.com/protocol-buffers/docs/reference/python-generated
- https://github.com/protocolbuffers/protobuf/issues/1606
- https://github.com/googleapis/google-cloud-python/issues/1402
- https://github.com/googleapis/google-cloud-python/pull/1450
- https://github.com/googleapis/google-cloud-python/pull/1329
结构字段类型
该库直接支持google.protobuf.Struct
字段类型。结构字段值被序列化为嵌入式实体。
请注意,google.protobuf.Struct
字段类型模仿JSON类型,仅支持数值类型的number
(https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/struct.proto#L62)。这意味着所有数字(包括整数)都表示为双精度浮点值(在实体内部,这存储为value_pb.double_value
)。
其他编程语言的翻译库
本节包含其他编程语言的翻译库列表,它们提供相同的功能。
测试
单元测试和集成测试可以在tests/
目录中找到。
您可以使用tox运行单元测试、集成测试和其他代码检查。
# Run all tox targets
tox
# Run only lint checks
tox -e lint
# Run unit tests under Python 2.7
tox -e py2.7-unit-tests
# Run Integration tests under Python 3.7
tox -e py3.7-integration-tests
# Run unit and integration tests and generate and display code coverage report
tox -e coverage
注意1:集成测试依赖于Google Cloud Datastore模拟器正在运行(./scripts/run-datastore-emulator.sh
)。
注意2:集成测试还运行跨编程语言兼容性测试,以验证Python和Go翻译库产生的输出完全相同。因此,这些测试还需要在系统上安装Golang >= 1.12。
许可证
版权所有 2019 Tomaz Muraus
根据Apache License,版本2.0(“许可证”);除非遵守许可证,否则您不得使用此作品。您可以在LICENSE
文件中或通过https://apache.ac.cn/licenses/LICENSE-2.0获得许可证副本。
https://apache.ac.cn/licenses/LICENSE-2.0
通过贡献,您同意这些贡献是您的(或经您雇主批准的)并且您授予所有当前和未来的项目用户和开发者完整的、不可撤销的版权许可,根据项目的许可证。
项目详情
哈希值 for protobuf-cloud-datastore-translator-0.1.13.tar.gz
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 4d8614df4d57af4665c2e8c08fc1ebc703c8c606dd6f9edfc5e0f465bbfa7b95 |
|
MD5 | 88f0c7e501dc9420c91ef44caadc1606 |
|
BLAKE2b-256 | 25e5053863431f47ba48738318233d601c2774506765a0c451eba66e28083d70 |
哈希值 for protobuf_cloud_datastore_translator-0.1.13-py3-none-any.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | d5a46cb5545112f7c08d83eba38fdca2a9cecddad304b8db4b32bbac59963269 |
|
MD5 | b5af02520eb0324e123229f3d22cbc2b |
|
BLAKE2b-256 | d32adcd8162791e061253f9766f909d73849d97dc0f2fe41b69f865ec333066d |