我希望避免使用样板代码为我的Google App Engine应用程序的不同模型创建Google Cloud Endpoints App。假设我有一个Post、User和Category模型。数据存储在数据存储区中。我想使用资源posts、users和categories创建REST API。我为posts资源编写了以下代码:
import endpoints
from protorpc import messages
from protorpc import message_types
from protorpc import remote
from blog.models import Post
from cloud_endpoints import WEB_CLIENT_ID, ANDROID_CLIENT_ID, IOS_CLIENT_ID, ANDROID_AUDIENCE
class PostMessage(messages.Message):
id = messages.StringField(1)
title = messages.StringField(2)
body = messages.StringField(3)
class PostMessageCollection(messages.Message):
post_messages = messages.MessageField(PostMessage, 1, repeated=True)
def post_to_message(post):
return PostMessage(
id=str(post.key()),
title=post.title,
body=post.body)
ID_RESOURCE = endpoints.ResourceContainer(
message_types.VoidMessage,
id=messages.StringField(1, variant=messages.Variant.STRING))
PUT_RESOURCE = endpoints.ResourceContainer(
PostMessage,
id=messages.StringField(1, variant=messages.Variant.STRING))
POST_RESOURCE = endpoints.ResourceContainer(Post)
@endpoints.api(name='posts',
version='v1',
allowed_client_ids=[WEB_CLIENT_ID, ANDROID_CLIENT_ID, IOS_CLIENT_ID],
audiences=[ANDROID_AUDIENCE])
class PostsApi(remote.Service):
"""List"""
@endpoints.method(message_types.VoidMessage,
PostMessageCollection,
path='/posts',
http_method='GET',
name='posts.listPosts')
def list(self, unused_request):
post_messages = []
for post in Post.all():
post_messages.append(post_to_message(post))
return PostCollection(post_messages=post_messages)
"""Get"""
@endpoints.method(ID_RESOURCE,
PostMessage,
path='/posts/{id}',
http_method='GET',
name='posts.getPost')
def get(self, request):
try:
return post_to_message(Post.get(request.id))
except (IndexError, TypeError):
raise endpoints.NotFoundException('Post %s not found.' % (request.id,))
"""Create"""
@endpoints.method(POST_RESOURCE,
message_types.VoidMessage,
path='/posts',
http_method='POST',
name='posts.createPost')
def create(self, request):
post = Post(title=request.title, body=request.body)\
post.put()
return message_types.VoidMessage()
"""Update"""
@endpoints.method(PUT_RESOURCE,
message_types.VoidMessage,
path='/posts/{id}',
http_method='POST',
name='posts.updatePost')
def update(self, request):
try:
post = Post.get(request.id)
post.title = request.title
post.body = request.body
return message_types.VoidMessage()
except (IndexError, TypeError):
raise endpoints.NotFoundException('Post %s not found.' % (request.id,))
"""Delete"""
@endpoints.method(ID_RESOURCE,
message_types.VoidMessage,
path='/posts/{id}',
http_method='DELETE',
name='posts.deletePost')
def delete(self, request):
try:
post = Post.get(request.id)
post.delete()
return message_types.VoidMessage()
except (IndexError, TypeError):
raise endpoints.NotFoundException('Post %s not found.' % (request.id,))我可以复制/粘贴这段代码,将“发布”更改为“分类”,还可以编辑PostMessage、PostMessageCollection和post_to_message,但这似乎不是什么好做法。我不想重复我的话。可以创建一个抽象API类并为PostAPI、CategoryAPI和UserAPI创建子类吗?或者,有没有更好的方法来参数化Post、PostMessage、PostMessageCollection、post_to_message和资源的路径("/posts“、"/categories”和"/users"),这样我就不必复制/粘贴每个资源的类了?这些类将具有相同的方法和相同的装饰器,我不想为每个资源重复这一点。我使用的是Python 2.7。
发布于 2014-02-05 03:11:37
我也偶然发现了同样的问题,不幸的是,这在google cloud endpoints上是不可能的。方法装饰器需要请求描述(这里是PostMessageCollection)。对message.Message进行子类化的请求描述不允许通过继承进行重用,因此所有消息类都必须在没有任何继承的情况下完全定义。
但是,您可以通过以下方式在某种程度上实现这一点(尽管我还没有测试过它,现在就想到了:):
# All the message and response definitions have to be here, complete.
class PostMessage(messages.Message):
id = messages.StringField(1)
title = messages.StringField(2)
body = messages.StringField(3)
class PostMessageCollection(messages.Message):
post_messages = messages.MessageField(PostMessage, 1, repeated=True)
def post_to_message(post):
return PostMessage(
id=str(post.key()),
title=post.title,
body=post.body)
ID_RESOURCE = endpoints.ResourceContainer(
message_types.VoidMessage,
id=messages.StringField(1, variant=messages.Variant.STRING))
PUT_RESOURCE = endpoints.ResourceContainer(
PostMessage,
id=messages.StringField(1, variant=messages.Variant.STRING))
POST_RESOURCE = endpoints.ResourceContainer(Post)
# Now define all the 'Category' related messages here.
@endpoints.api(name='posts_n_categories', # The name can be a common one.
version='v1',
allowed_client_ids=[WEB_CLIENT_ID, ANDROID_CLIENT_ID, IOS_CLIENT_ID],
audiences=[ANDROID_AUDIENCE])
class BaseAPI(remote.Service):
"""List"""
# Common defs go here.
MessageCollection = messages.Message
PATH = '/'
NAME = ''
@staticmethod
def converter(x):
raise NotImplemented
iterator = []
collection = messages.Message
@endpoints.method(message_types.VoidMessage,
MessageCollection,
path=PATH,
http_method='GET',
name=NAME)
def list(self, unused_request):
# Do the common work here. You can
_messages = []
for post in self.__class__.iterator.all():
_messages.append(self.__class__.converter(post))
return self.__class__.collection(post_messages=_messages)
@endpoints.api(name='posts', # The name can be different.
version='v1',
allowed_client_ids=[WEB_CLIENT_ID, ANDROID_CLIENT_ID, IOS_CLIENT_ID],
audiences=[ANDROID_AUDIENCE])
class PostAPI(Base):
# Post specific defs go here.
MessageCollection = PostMessageCollection
PATH = '/posts'
NAME = 'posts.listPosts'
converter = post_to_message
iterator = Post
collection = PostCollection
# Define the category class here.显然,这并不能节省太多时间。
发布于 2016-08-03 06:33:33
这个帖子已经有两年的历史了,但无论如何我都会提出一个想法。它有一个明显的缺陷;下面有更多关于这方面的内容。
我将假设您正在通过ndb API使用数据存储。
其想法是引入通用消息类(EntityContainer,EntityContainerList),这些类可以包含任何特定于应用程序的消息类的实例,并包括一个将路由映射到protorpc和ndb类的字典:
model.py
class Book(ndb.Model):
title = ndb.StringProperty()
author = ndb.StringProperty()
class Movie(ndb.Model):
title = ndb.StringProperty()
director = ndb.StringProperty()main.py
class Book(messages.Message):
title = messages.StringField(1)
author = messages.StringField(2)
class Movie(messages.Message):
title = messages.StringField(1)
director = messages.StringField(2)
class EntityContainer(messages.Message):
book = messages.MessageField(Book, 1)
movie = messages.MessageField(Movie, 2)
id_ = messages.StringField(3)
class EntityContainerList(messages.Message):
entities = messages.MessageField(EntityContainer, 1, repeated=True)
Map = {
'books': {
'message_class': Book,
'ndb_model_class': model.Book,
'entity_container_key': 'book'
},
'movies': {
'message_class': Movie,
'ndb_model_class': model.Movie,
'entity_container_key': 'movie'
}
}
@endpoints.api(name='testApi', version='v1')
class TestApi(remote.Service):
GET_RESOURCE = endpoints.ResourceContainer(
id_=messages.StringField(1),
entities=messages.StringField(2)
)
LIST_RESOURCE = endpoints.ResourceContainer(
entities=messages.StringField(2)
)
POST_RESOURCE = endpoints.ResourceContainer(
EntityContainer,
entities=messages.StringField(1),
)
@endpoints.method(
GET_RESOURCE,
EntityContainer,
path='{entities}/{id_}',
http_method="GET")
def get_entity(self, request):
# The path tells us what kind of entity we're fetching.
Entity = Map[request.entities]['message_class']
key = Map[request.entities]['entity_container_key']
# Pull from database.
ndb_entity = ndb.Key(urlsafe=request.id_).get()
# Formulate response.
entity_container = EntityContainer(**{key: Entity(**ndb_entity.to_dict())})
entity_container.id_ = request.id_
logging.info("\n\nResponse: %s\n" % str(entity_container))
return entity_container
@endpoints.method(
LIST_RESOURCE,
EntityContainerList,
path='{entities}',
http_method="GET")
def list_entities(self, request):
# The path tells us what kinds of entities we're fetching.
Entity = Map[request.entities]['message_class']
NdbModel = Map[request.entities]['ndb_model_class']
key = Map[request.entities]['entity_container_key']
# Pull from database
query = NdbModel.query()
# Formulate response.
entities = [
EntityContainer(**{'id_': q.key.urlsafe(), key: Entity(**q.to_dict())})
for q in query
]
entity_container_list = EntityContainerList(entities=entities)
logging.info("\n\nEntity list: %s\n" % str(entity_container_list))
return entity_container_list
@endpoints.method(
POST_RESOURCE,
EntityContainer,
path='{entities}',
http_method='POST'
)
def post_entity(self, request):
# The path tells us what kind of entity we're' creating.
Entity = Map[request.entities]['message_class']
NdbModel = Map[request.entities]['ndb_model_class']
key = Map[request.entities]['entity_container_key']
# Extract the body of the request
body_message = getattr(request, key)
body_dict = {f.name: getattr(body_message, f.name)
for f in body_message.all_fields()
if getattr(body_message, f.name)}
# Write to database
ndb_entity = NdbModel(**body_dict)
ndb_key = ndb_entity.put()
id_ = ndb_key.urlsafe()
# Reload entity. Maybe some model hooks treated the data.
ndb_entity = ndb_key.get()
entity_container = EntityContainer(**{key: Entity(**ndb_entity.to_dict())})
entity_container.id_ = id_
logging.info("\n\nResponse: %s\n" % str(entity_container))
return entity_container当然,您也可以使用DELETE、PUT和PATCH方法。
您拥有的消息类越多,这种方法可以节省的代码就越多。(我刚刚包含了两个-- Book和Movie --用于演示。)
上面提到的缺陷是EntityContainer类膨胀了消息字段,任何类实例都只使用其中的两个字段。我不知道事情是如何在引擎盖下工作的,所以我不能评估这件事的严重性。
https://stackoverflow.com/questions/20540090
复制相似问题