云端点 - 从数据存储区检索单个实体(通过EndpointsModel提供的辅助方法以外的属性)

Tik*_*iki 4 google-cloud-endpoints endpoints-proto-datastore

这个问题完全遵循我在这里提出(并得到回答)的相关问题:尝试检索单个实体时出错

据我所知,要使用除已经提供的辅助方法之外的属性从数据存储中检索单个实体(例如'id')需要将简单的数据属性转换为EndpointsAliasProperty?如果是的话,我该怎么做呢?或者我们只能使用'id'(由提供的辅助方法EndpointsModel),我们不能使用我们定义的任何属性(在这种情况下'title')?

bos*_*ter 13

自定义EndpointsAliasPropertys和您定义的某个数据属性之间的区别在于它们的使用方式.它们都用于创建protorpc消息,然后将该消息转换为EndpointsModel包含自定义数据的消息.就是魔术发生的地方.

将其分解为几个步骤:

1.您指定数据

from google.appengine.ext import ndb
from endpoints_proto_datastore.ndb import EndpointsModel

class MyModel(EndpointsModel):
    my_attr = ndb.StringProperty()
Run Code Online (Sandbox Code Playgroud)

2.为您的方法选择字段

class MyApi(...):

    @MyModel.method(request_fields=('id', 'my_attr'), ...)
    def my_method(self, my_model_entity):
        ...
Run Code Online (Sandbox Code Playgroud)

3. protorpc从您的字段定义消息类

>>> request_message_class = MyModel.ProtoModel(fields=('id', 'my_attr'))
>>> request_message_class
<class '.MyModelProto_id_my_attr'>
>>> for field in request_message_class.all_fields():
...   print field.name, ':', field.variant
...
id : INT64
my_attr : STRING
Run Code Online (Sandbox Code Playgroud)

每次由装饰的方法处理请求时都会发生这种情况@MyModel.method.

4.您的应用程序中会出现一条请求,并会创建一条消息

使用protorpc消息类,将从JSON解析消息实例,该消息实例将传递给您的端点SPI(由其创建endpoints.api_server).

当请求进入您protorpc.remote.Service解码时:

>>> from protorpc import remote
>>> protocols = remote.Protocols.get_default()
>>> json_protocol = protocols.lookup_by_content_type('application/json')
>>> request_message = json_protocol.decode_message(
...      request_message_class,
...      '{"id": 123, "my_attr": "some-string"}'
... )
>>> request_message
<MyModelProto_id_my_attr
 id: 123
 my_attr: u'some-string'>
Run Code Online (Sandbox Code Playgroud)

5. protorpc消息被转换为数据存储模型

entity = MyModel.FromMessage(request_message)
Run Code Online (Sandbox Code Playgroud)

是你真正关心的一步.所述FromMessage类方法(还提供了作为其一部分EndpointsModel)遍历所有的字段

for field in sorted(request_message_class.all_fields(),
                    key=lambda field: field.number):
Run Code Online (Sandbox Code Playgroud)

对于具有值集的每个字段,将值转换为要添加到实体的内容,并根据属性是否为以下内容进行分隔EndpointsAliasProperty:

    if isinstance(value_property, EndpointsAliasProperty):
        alias_args.append((local_name, to_add))
    else:
        entity_kwargs[local_name] = to_add
Run Code Online (Sandbox Code Playgroud)

完成此循环后,我们有一个有序列表alias_args,其中包含所有键,值对以及entity_kwargs从消息中解析的数据属性的字典.

使用这些,首先创建一个简单的实体

entity = MyModel(**entity_kwargs)
Run Code Online (Sandbox Code Playgroud)

然后按顺序设置每个别名属性值:

for name, value in alias_args:
    setattr(entity, name, value)
Run Code Online (Sandbox Code Playgroud)

扩展行为发生在setattr(entity, name, value).由于EndpointsAliasProperty是的子类property,它是一个描述符,它有一个setter可以执行一些自定义的行为超出了简单的设置值.

例如,该id属性定义为:

@EndpointsAliasProperty(setter=IdSet, property_type=messages.IntegerField)
def id(self):
Run Code Online (Sandbox Code Playgroud)

setter执行除简单设置数据之外的操作:

def IdSet(self, value):
    self.UpdateFromKey(ndb.Key(self.__class__, value))
Run Code Online (Sandbox Code Playgroud)

此特定方法尝试使用id数据存储区中未包含在从请求解析的实体中的任何值中的和补丁来检索存储在数据存储区中的实体.


如果你想为类似的字段执行此操作my_attr,则需要构建一个自定义查询,该查询可以检索具有该唯一my_attr值的项目(如果不存在一个这样的实体则会失败).

这是有问题的,您最好使用唯一字段,例如用于在数据存储区中存储实体的密钥或ID.

具有祖先样本的提供了创建自己的自定义属性的一个很好的示例.

如果你真的坚持使用my_attr检索实体,你可以使用不同的属性名称(因为my_attr已经用于数据属性),例如fromMyAttr:

class MyModel(EndpointsModel):
    def MyAttrSet(self, value):
        ...
    @EndpointsAliasProperty(setter=MyAttrSet)
    def fromMyAttr(self):
        ...
Run Code Online (Sandbox Code Playgroud)

这里,MyAttrSet实例方法将形成查询:

    def MyAttrSet(self, value):
        query = MyModel.query(MyModel.my_attr == value)
        results = query.fetch(2)
Run Code Online (Sandbox Code Playgroud)

拒绝不是唯一的结果my_attr:

        if len(results) == 0:
            raise endpoints.NotFoundException('Not found.')
        if len(results) == 2:
            raise endpoints.BadRequestException('Colliding results.')
Run Code Online (Sandbox Code Playgroud)

如果我们找到一个唯一的实体,则复制已存储实体的值:

        matching_entity = results[0]
        self._CopyFromEntity(matching_entity)
        self._from_datastore = True
Run Code Online (Sandbox Code Playgroud)