我刚刚开始使用Proto Datastore创建API。我想知道是否可以为我的请求和响应设置不同的类,前提是它们都是从EndpointsModel继承的。例如,
class my_request(EndpointsModel):
#attributes
class my_response(EndpointsModel):
#different set of attributes
@endpoints.api(name='Blah', version='v1')
class testAPI(remote.Service):
@my_request.method(name='myAPImethod', path='blah',
http_method='POST')
def myAPImethod(self, req):
#do something
resp = my_response()
return resp
这看起来不会起作用。那么有人可以告诉我如何创建这样的方法。我能想到的唯一另一种方法是恢复原始的protorpc方法,并指定请求和响应类型作为装饰器的一部分。有没有办法使用proto-datastore实现这一目标? 提前谢谢。
答案 0 :(得分:0)
您可以应用一种输入/输出字段filtering using request_fields=
and response_fields=
in your .method()
装饰器,但不能指定不同的输入和输出消息类。
如果需要使用不同的对象,则必须使用标准端点和protoRPC类和装饰器。
答案 1 :(得分:0)
在你的例子中,你有一个非常简单的布局,并没有真正满足于问题,或者恕我直言,我认为你实际上想要问的是什么。
我自己正在开发一个更复杂的端点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非常棘手,需要您从protorpc导入的messages.Message
分类。有关ResourceContainers的说明,请参阅this 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
项以返回被调用者,就像{{1}一样} model可能包含多个MyUser
模型。这不需要向api方法添加任何内容,因为它已经是MySubscription
中的嵌套项。此外,如果不需要将这些项目返回给被调用者,则无需在响应消息中复制模型中的项目。