在您的示例中,您有一个非常简单的布局,与所提出的问题或恕我直言,我认为您实际上想要问的问题并不真正相符。
我自己正在开发一个更复杂的 Endpoints API,但使用两种不同的模型来为我的 API 调用制作请求和响应对象;然而,它们是嵌套在另一个中的一个(或多个)。我发现了提示我应该在 App Engine 日志中用作警告的方法:
Method csapi.user.XXXXX specifies path parameters but you are not using a ResourceContainer. This will fail in future releases; please switch to using ResourceContainer as soon as possible.
注意;ResourceContainers 可能非常棘手,需要您messages.Message
从 protorpc 中导入。有关 ResourceContainers 的说明,请参阅此StackOverflow 答案。
以您的示例为基础,我们需要构建 ResourceContainers 来复制我们希望返回给被调用者的模型中的内容。
import endpoints
from protorpc import messages
from protorpc import message_types
from protorpc import remote
from google.appengine.ext import ndb
from google.appengine.ext.ndb import msgprop
from endpoints_proto_datastore.ndb import EndpointsAliasProperty
from endpoints_proto_datastore.ndb import EndpointsModel
class MySubscriptionResponseMessage(messages.Message):
id = messages.IntegerField(1)
accountType = messages.EnumField(Account_Type, 2, required=True)
isActive = messages.BooleanField(3, required=True, default=False)
thisAcctEmail = messages.StringField(4)
subscriberSince = message_types.DateTimeField(5)
class MyUserResponseMessage(messages.Message):
id = messages.IntegerField(1)
subscriptions = messages.MessageField('SubscriptionReadResponseMessage', 2, repeated=True)
class MySubscription(EndpointsModel):
accountType = msgprop.EnumProperty(AccountType, choices=set([AccountType.POTENTIAL_LEAD, AccountType.BASIC, AccountType.ADVANCED, AccountType.PREMIUM]), required=True, default=AccountType.POTENTIAL_LEAD)
isActive = ndb.BooleanProperty(required=True, indexed=True)
thisAcctId = ndb.StringProperty(repeated=False, indexed=True, required=True)
subscriberSince = ndb.DateTimeProperty(auto_now_add=True)
class MyUser(EndpointsModel):
subscription_key = ndb.KeyProperty(kind="Subscription", repeated=True)
def IdSet(self, value):
# By default, the property "id" assumes the "id" will be an integer in a
# simple key -- e.g. ndb.Key(GSModel, 10) -- which is the default behavior
# if no key is set. Instead, we wish to use a string value as the "id" here,
# so first check if the value being set is a string.
if not isinstance(value, basestring):
raise TypeError('ID must be a string.')
# We call UpdateFromKey, which each of EndpointsModel.IdSet and
# EndpointsModel.EntityKeySet use, to update the current entity using a
# datastore key. This method sets the key on the current entity, attempts to
# retrieve a corresponding entity from the datastore and then patch in any
# missing values if an entity is found in the datastore.
self.UpdateFromKey(ndb.Key(Csuser, value))
@EndpointsAliasProperty(setter=IdSet, required=True)
def id(self):
# First check if the entity has a key.
if self.key is not None:
# If the entity has a key, return only the string_id. The method id()
# would return any value, string, integer or otherwise, but we have a
# specific type we wish to use for the entity "id" and that is string.
return self.key.string_id()
@EndpointsAliasProperty(repeated=True,property_type=Subscription.ProtoModel())
def subscriptions(self):
return ndb.get_multi(self.subscription_key)
@endpoints.api(name='Blah', version='v1')
class testAPI(remote.Service):
@my_request.method(
response_message=MyUserResourceContainer,
name='myAPImethod',
path='blah',
http_method='POST')
def myAPImethod(self, req):
#do something
this_sub = MySubscription()
subs = []
... how you manipulate this object is up to you ...
for sub in subs
sub_msg = MySubscriptionResponseMessage(
id=this_sub.id,
accountType=this_sub.accountType,
isActive=this_sub.isActive,
thisAcctEmail=this_sub.thisAcctEmail,
subscriberSince=this_sub.subscriberSince,
subs.append(sub_msg)
return MyUserResponseMessage(
id=user1.id,
subscriptions=subs)
如您所见,这比您的简单示例要深入得多。
加分:使用路径参数
如果您希望为您的方法接受路径参数,例如我们假设的用户的 id 属性(即,path='users/{id}/delete'
),我们将在方法本身之前添加以下块将使用它:
MY_REQUEST_RESOURCE_PAGE = endpoints.ResourceContainer(
message_types.VoidMessage,
id=messages.StringField(1, variant=messages.Variant.STRING),
accountType=messages.EnumField(Account_Type, 2, required=True)
isActive=messages.BooleanField(3, required=True),
thisAcctEmail=messages.StringField(4, variant=messages.Variant.STRING),
subscriberSince=messages.message_types.DateTimeField(5),
cursor=messages.StringField(6, variant=messages.Variant.STRING, required=False, default="1"),
limit=messages.IntegerField(7, variant=messages.Variant.INT32, required=False, default=10)
)
注意:注意附加属性cursor
和limit
,它们允许在您有数百个用户的情况下对返回的结果进行分页;这些在模型查询中经常用于此目的。
现在,要完成接受路径参数的更改,请从上面的示例中替换此行:
response_message=MyUserResponseMessage,
有了这个:
MY_REQUEST_RESOURCE_PAGE , MyUserResponseMessage,
最后一点,这个设置——无论是否使用路径参数——允许你MySubscriptionResponseMessage
在你的内部嵌套一个或多个项目MyUserResponseMessage
以返回给被调用者,就像MyUser
模型可能包含多个MySubscription
模型一样。这不需要向您的 api 方法添加任何内容,因为它已经是MyUserResponseMessage
. 此外,如果不需要将这些项目返回给被调用者,则无需在响应消息中复制模型中的项目。