あなたの例では、非常に単純なレイアウトがあり、質問された質問や、私見では、実際に質問したいと思っていることと実際には一致していません。
私は自分でエンドポイント API に取り組んでいますが、これははるかに複雑ですが、2 つの異なるモデルを使用して、API 呼び出しの要求オブジェクトと応答オブジェクトの両方を作成しています。ただし、それらは 1 つ (または複数) が他の中にネストされています。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,
最後に、このセットアップでは、パス パラメーターを使用するかどうかに関係なく、モデルに複数のモデルが含まれている場合と同様に、内部に1 つ以上のMySubscriptionResponseMessage
アイテムをネストMyUserResponseMessage
して、呼び出し先に返すことができます。のネスト項目であるため、API メソッドに何も追加する必要はありません。また、それらの項目を呼び出し先に返す必要がない場合は、応答メッセージでモデルから項目を複製する必要はありません。MyUser
MySubscription
MyUserResponseMessage