0

我刚刚开始使用 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

这看起来行不通。所以有人可以告诉我如何创建这样的方法。我能想到的唯一另一种方法是恢复到原始的 protopc 方法,并将请求和响应类型都指定为装饰器的一部分。有没有办法使用 proto-datastore 来实现这一点?先感谢您。

4

2 回答 2

0

在您的示例中,您有一个非常简单的布局,与所提出的问题或恕我直言,我认为您实际上想要问的问题并不真正相符。

我自己正在开发一个更复杂的 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)
)

注意:注意附加属性cursorlimit,它们允许在您有数百个用户的情况下对返回的结果进行分页;这些在模型查询中经常用于此目的。

现在,要完成接受路径参数的更改,请从上面的示例中替换此行:

response_message=MyUserResponseMessage,

有了这个:

MY_REQUEST_RESOURCE_PAGE , MyUserResponseMessage,

最后一点,这个设置——无论是否使用路径参数——允许你MySubscriptionResponseMessage在你的内部嵌套一个或多个项目MyUserResponseMessage以返回给被调用者,就像MyUser模型可能包含多个MySubscription模型一样。这不需要向您的 api 方法添加任何内容,因为它已经是MyUserResponseMessage. 此外,如果不需要将这些项目返回给被调用者,则无需在响应消息中复制模型中的项目。

于 2015-10-09T21:59:00.800 回答
0

您可以在装饰器中使用and应用一种输入/输出字段过滤request_fields=response_fields=.method(),但您不能指定不同的输入和输出消息类。

如果您需要使用不同的对象,则必须使用标准端点和 protoRPC 类和装饰器。

于 2015-10-09T14:31:06.453 回答