我正在使用 Endpoints 和 Endpoints Proto Datastore 构建 API,并使用 App Engine Launcher 进行测试。下面的代码是我用来构建两层深的嵌套实体(祖父母/父母/孩子)的代码。
该变量看似随机_child
返回“无”(在第 98 行设置),这ndb.Key
在第 139 和 143 行中断并导致BadArgumentError: Incomplete Key entry must be last
.
import endpoints
from google.appengine.ext import ndb
from protorpc import remote
from endpoints_proto_datastore.ndb import EndpointsAliasProperty
from endpoints_proto_datastore.ndb import EndpointsModel
import logging
project_api = endpoints.api(name='project', version='v1', description='Project API')
""" Grandparent """
class GrandparentModel(EndpointsModel):
_message_fields_schema = ('grandparent',)
def set_grandparent(self, value):
if not isinstance(value, basestring):
raise TypeError('Grandparent name must be a string.')
self.UpdateFromKey(ndb.Key(GrandparentModel, value))
@EndpointsAliasProperty(setter=set_grandparent, required=True)
def grandparent(self):
if self.key is not None:
return self.key.string_id()
""" Parent """
class ParentModel(EndpointsModel):
_message_fields_schema = ('grandparent', 'parent',)
_grandparent = None
_parent = None
def set_key(self):
if self._grandparent is not None and self._parent is not None:
key = ndb.Key(GrandparentModel, self._grandparent, ParentModel, self._parent)
self.UpdateFromKey(key)
def set_parts(self):
if self.key is not None:
grandparent_pair, parent_pair = self.key.pairs()
self._grandparent = grandparent_pair[1]
self._parent = parent_pair[1]
# Grandparent
def set_grandparent(self, value):
if not isinstance(value, basestring):
raise TypeError('Grandparent name must be a string.')
self._grandparent = value
if ndb.Key(GrandparentModel, value).get() is None:
raise endpoints.NotFoundException('Grandparent %s does not exist.' % value)
self.set_key()
self._endpoints_query_info.ancestor = ndb.Key(GrandparentModel, value)
@EndpointsAliasProperty(setter=set_grandparent, required=True)
def grandparent(self):
if self._grandparent is None:
self.set_parts()
return self._grandparent
# Parent
def set_parent(self, value):
if not isinstance(value, basestring):
raise TypeError('Parent must be a string.')
self._parent = value
self.set_key()
@EndpointsAliasProperty(setter=set_parent, required=True)
def parent(self):
if self._parent is None:
self.set_parts()
return self._parent
""" Child """
class ChildModel(EndpointsModel):
_message_fields_schema = ('grandparent', 'parent', 'child',)
_grandparent = None
_parent = None
_child = None
def set_key(self):
if self._grandparent is not None and self._parent is not None and self._child is not None:
key = ndb.Key(GrandparentModel, self._grandparent, ParentModel, self._parent, ChildModel, self._child)
self.UpdateFromKey(key)
def set_parts(self):
if self.key is not None:
grandparent_pair, parent_pair, child_pair = self.key.pairs()
self._grandparent = grandparent_pair[1]
self._parent = parent_pair[1]
self._child = child_pair[1]
# Grandparent
def set_grandparent(self, value):
if not isinstance(value, basestring):
raise TypeError('Grandparent must be a string.')
self._grandparent = value
self.set_key()
@EndpointsAliasProperty(setter=set_grandparent, required=True)
def grandparent(self):
if self._grandparent is None:
self.set_parts()
return self._grandparent
# Parent
def set_parent(self, value):
if not isinstance(value, basestring):
raise TypeError('Parent name must be a string.')
self._parent = value
logging.warning('Check _grandparent value: %s' % self._grandparent)
logging.warning('Check _parent value: %s' % self._parent)
if ndb.Key(GrandparentModel, self._grandparent, ParentModel, value).get() is None:
raise endpoints.NotFoundException('Key %s does not exist.' % value)
self.set_key()
self._endpoints_query_info.ancestor = ndb.Key(GrandparentModel, self._grandparent, ParentModel, value)
@EndpointsAliasProperty(setter=set_parent, required=True)
def parent(self):
if self._parent is None:
self.set_parts()
return self._parent
# Child
def set_child(self, value):
if not isinstance(value, basestring):
raise TypeError('Child must be a string.')
self._child = value
self.set_key()
@EndpointsAliasProperty(setter=set_child, required=True)
def child(self):
if self._child is None:
self.set_parts()
return self._child
@project_api.api_class(resource_name='grandparent')
class Grandparent(remote.Service):
@GrandparentModel.method(name='insert', path='grandparent')
def grandparent_insert(self, grandparent):
grandparent.put()
return grandparent
@GrandparentModel.query_method(name='list', path='grandparent')
def grandparent_list(self, query):
return query
@project_api.api_class(resource_name='parent', path='grandparent')
class Parent(remote.Service):
@ParentModel.method(name='insert', path='{grandparent}/parent')
def parent_insert(self, parent):
parent.put()
return parent
@ParentModel.query_method(name='list', path='{grandparent}/parent', query_fields=('grandparent',))
def parent_list(self, query):
return query
@project_api.api_class(resource_name='child', path='grandparent')
class Child(remote.Service):
@ChildModel.method(name='insert', path='{grandparent}/parent/{parent}/child')
def child_insert(self, child):
child.put()
return child
@ChildModel.query_method(name='list', path='{grandparent}/parent/{parent}/child',
query_fields=('grandparent', 'parent',))
def child_list(self, query):
return query
application = endpoints.api_server([project_api], restricted=False)
这是它中断时的日志:
INFO 2015-08-16 10:26:50,503 module.py:809] 默认值:“POST /_ah/spi/BackendService.getApiConfigs HTTP/1.1”200 8577 INFO 2015-08-16 09:26:52,099 main.py:136] 检查 _grandparent 值:g INFO 2015-08-16 09:26:52,099 main.py:137] 检查 _parent 值:p 信息 2015-08-16 10:26:52,108 module.py:809] 默认值:“POST /_ah/spi/Child.child_list HTTP/1.1”200 2 INFO 2015-08-16 10:26:52,109 module.py:809] 默认值:“GET /_ah/api/project/v1/grandparent/g/parent/p/child HTTP/1.1”200 2 INFO 2015-08-16 10:26:59,165 module.py:809] 默认值:“POST /_ah/spi/BackendService.getApiConfigs HTTP/1.1”200 8577 INFO 2015-08-16 09:26:59,168 main.py:136] 检查 _grandparent 值:无 INFO 2015-08-16 09:26:59,168 main.py:137] 检查 _parent 值:p ERROR 2015-08-16 09:26:59,168 service.py:191] ProtoRPC 方法实现遇到意外错误:BadArgumentError(不完整的键输入必须在最后) 回溯(最近一次通话最后): 文件“C:\Development\Google\google_appengine\lib\protorpc-1.0\protorpc\wsgi\service.py”,第 181 行,在 protorpc_service_app 响应 = 方法(实例,请求) 文件“C:\Development\Google\google_appengine\lib\endpoints-1.0\endpoints\api_config.py”,第 1332 行,invoke_remote 返回远程方法(服务实例,请求) 文件“C:\Development\Google\google_appengine\lib\protorpc-1.0\protorpc\remote.py”,第 414 行,invoke_remote_method 响应 = 方法(服务实例,请求) 文件“C:\Development\_Projects\API\project\endpoints_proto_datastore\ndb\model.py”,第 1574 行,在 QueryFromRequestMethod request_entity = cls.FromMessage(request) FromMessage 中的文件“C:\Development\_Projects\API\project\endpoints_proto_datastore\ndb\model.py”,第 1245 行 setattr(实体,名称,值) 文件“C:\Development\_Projects\API\project\main.py”,第 139 行,在 set_parent 如果 ndb.Key(GrandparentModel, self._grandparent, ParentModel, value).get() 为无: 文件“C:\Development\Google\google_appengine\google\appengine\ext\ndb\key.py”,第 220 行,在 __new__ self.__namespace) = self._parse_from_args(**kwargs) _parse_from_args 中的文件“C:\Development\Google\google_appengine\google\appengine\ext\ndb\key.py”,第 245 行 '不完整的密钥输入必须在最后') BadArgumentError:不完整的键输入必须是最后一个 INFO 2015-08-16 10:26:59,174 module.py:809] 默认值:“POST /_ah/spi/Child.child_list HTTP/1.1”500 512 INFO 2015-08-16 10:26:59,174 module.py:809] 默认值:“GET /_ah/api/project/v1/grandparent/g/parent/p/child HTTP/1.1”503 196
到目前为止,我一直无法自己弄清楚,所以任何帮助将不胜感激。
谢谢!