Open dhermes opened 11 years ago
A few other missing things which may be worth pursuing:
stats.py:
class BaseStatistic(model.Model):
polymodel.py:
class _ClassKeyProperty(model.StringProperty):
class PolyModel(model.Model):
msgprop.py:
class EnumProperty(model.IntegerProperty):
class MessageProperty(model.StructuredProperty):
metadata.py:
class _BaseMetadata(model.Model):
blobstore.py:
class BlobInfo(model.Model):
NOTE: THIS POST WAS EDITED AFTER BEING MIGRATED.
Code Hosting Comment Metadata: sub>author.htmlLink=https://code.google.com/u/dhermes@google.com/</sub sub>author.name=dhermes@google.com</sub id=1 published=2013-01-10T04:57:04.000Z
This can be made to work with a few tweaks, mainly in an _EndpointsQueryInfo
container class and in EndpointsModel.ToMessageCollection
.
A full sample which extends the basic sample:
from google.appengine.ext import ndb
from google.appengine.ext.ndb import polymodel
from protorpc import remote
import endpoints
from endpoints_proto_datastore.ndb import EndpointsModel
from endpoints_proto_datastore.ndb.model import _EndpointsQueryInfo
def _DowncastMessage(message, final_message_class):
message_class = message.__class__
downcasted_message = final_message_class()
for field in final_message_class.all_fields():
# KeyError allowed to happen, if the field is missing,
# a downcast should be performed
field_on_message = message_class.field_by_name(field.name)
# Make sure fields are the same type
if field_on_message.__class__ != field.__class__:
raise TypeError('Downcasted field of the wrong type: %r. Should be %r.' %
(field_on_message.__class__, field.__class__))
value = getattr(message, field.name)
setattr(downcasted_message, field.name, value)
return downcasted_message
class _PolyModelQueryInfo(_EndpointsQueryInfo):
def _PopulateFilters(self):
entity = self._entity
for prop in entity._properties.itervalues():
if isinstance(prop, polymodel._ClassKeyProperty):
continue
attr_name = prop._code_name
current_value = getattr(entity, attr_name)
# Only filter for non-null values
if current_value is not None:
self._AddFilter(prop == current_value)
class MyModel(EndpointsModel, polymodel.PolyModel):
_message_fields_schema = ('attr1', 'created')
attr1 = ndb.StringProperty()
created = ndb.DateTimeProperty(auto_now_add=True)
def __init__(self, *args, **kwargs):
# Don't need to call both constructors since PolyModel doesn't define one
# and descends from model.Model, a superclass of EndpointsModel
super(MyModel, self).__init__(*args, **kwargs)
self._endpoints_query_info = _PolyModelQueryInfo(self)
@classmethod
def ToMessageCollection(cls, items, collection_fields=None,
next_cursor=None):
proto_model = cls.ProtoCollection(collection_fields=collection_fields)
items_as_message = [item.ToMessage(fields=collection_fields)
for item in items]
final_proto_class = cls.ProtoModel(fields=collection_fields)
items_as_message = [_DowncastMessage(item, final_proto_class)
for item in items_as_message]
result = proto_model(items=items_as_message)
if next_cursor is not None:
result.nextPageToken = next_cursor.to_websafe_string()
return result
class NextModel(MyModel):
_message_fields_schema = ('attr1', 'attr2', 'created')
attr2 = ndb.StringProperty()
@endpoints.api(name='myapi', version='v1', description='My Little API')
class MyApi(remote.Service):
@MyModel.method(path='mymodel', http_method='POST', name='mymodel.insert')
def MyModelInsert(self, my_model):
my_model.put()
return my_model
@MyModel.query_method(path='mymodels', name='mymodel.list')
def MyModelList(self, query):
return query
@NextModel.method(path='nextmodel', name='nextmodel.insert')
def NextModelInsert(self, my_model):
my_model.put()
return my_model
@NextModel.query_method(path='nextmodels', name='nextmodel.list')
def NextModelList(self, query):
return query
application = endpoints.api_server([MyApi], restricted=False)
NOTE: THIS POST WAS EDITED AFTER BEING MIGRATED.
Code Hosting Comment Metadata: sub>author.htmlLink=https://code.google.com/u/dhermes@google.com/</sub sub>author.name=dhermes@google.com</sub id=2 published=2013-01-10T09:11:00.000Z
After discussing, we have decided to write a subclass EndpointsPolyModel
which performs most of the things above and extends EndpointsModel
and polymodel.PolyModel
in the same fashion.
As of right now, it's unclear when. Feel free to star the issue if it is urgent for you or if you are using the code from the comment above.
NOTE: THIS POST WAS EDITED AFTER BEING MIGRATED.
Code Hosting Comment Metadata: sub>author.htmlLink=https://code.google.com/u/dhermes@google.com/</sub sub>author.name=dhermes@google.com</sub id=3 published=2013-01-10T18:05:26.000Z updates.labels=[ -Type-Discussion, Type-FeatureRequest, ] updates.status=Accepted updates.summary=Add support for polymodel
If users wanted a generic query for an ID to return the model with all properties, this may be worth offering. For example:
class Event(EndpointsModle, polymodel.PolyModel):
# all the other stuff needed from http://pastebin.com/hdj4eqTY
attr1 = ndb.StringProperty()
class UserAddedEvent(Event):
attr2 = ndb.IntegerProperty()
class RegistrationEvent(Event):
attr3 = ndb.FloatProperty()
To be able to return an instance of any of these, we'd need
from protorpc import messages
class EventUmbrella(messages.Message):
attr1 = messages.StringField(1)
attr2 = messages.IntegerField(2)
attr3 = messages.FloatField(3)
# Maybe a string field to indicate the type returned
event_class = messages.StringField(4) # can't use class as a varname in Python
And then use the protorpc
class instead of response_fields
:
@Event.method(..., response_message=EventUmbrella, ...)
def simple_get(self, entity):
# ... do stuff to entity
umbrella_message = ConvertToEventUmbrella(entity)
return umbrella_message
ToMessageCollection
would also need to be tweaked on the base EndpointsPolyModel
class to allow this umbrella class instead of whatever is implicitly used by ProtoCollection
.
Also, it may be worth offering a method which such that
f(Event, UserAddedEvent, RegistrationEvent) = EventUmbrella
Link to Full Discussion: https://groups.google.com/forum/?fromgroups=#!topic/endpoints-trusted-testers/gd3zXk6XkZM
NOTE: THIS POST WAS EDITED AFTER BEING MIGRATED.
Code Hosting Comment Metadata: sub>author.htmlLink=https://code.google.com/u/dhermes@google.com/</sub sub>author.name=dhermes@google.com</sub id=4 published=2013-01-11T05:57:13.000Z
Code Hosting Comment Metadata: sub>author.htmlLink=https://code.google.com/u/dhermes@google.com/</sub sub>author.name=dhermes@google.com</sub id=5 published=2013-02-23T06:29:43.000Z updates.summary=Add support for NDB polymodel
NDB polymodel exists now: https://developers.google.com/appengine/docs/python/ndb/polymodelclass
Code Hosting Comment Metadata: author.htmlLink=https://code.google.com/u/108103421167573850503/ sub>author.name=james@khanacademy.org</sub id=6 published=2013-04-23T17:48:12.000Z
Thanks James.
Unfortunately, that's not what this bug is about :)
It's about whether or not it's worth it/possible to support this in endpoints-proto-datastore.
Code Hosting Comment Metadata: sub>author.htmlLink=https://code.google.com/u/dhermes@google.com/</sub sub>author.name=dhermes@google.com</sub id=7 published=2013-04-23T18:21:34.000Z
Hi
How can I star this issue? I would love to see this feature as I plan to use a Polymodel datastore model. The model shall look like this:
Have you tried some of the suggestions above?
I take it that MyModel::list
not returning the attr2
property of NextModel
entities is addressed, above, and that the implementation is not trivial. Would I be correct in this?
Try to add support for polymodel https://developers.google.com/appengine/docs/python/ndb/polymodelclass
Need to investigate/discuss before fully committing.
Code Hosting Comment Metadata: sub>author.htmlLink=https://code.google.com/u/dhermes@google.com/</sub sub>author.name=dhermes@google.com</sub id=0 published=2013-01-10T01:53:13.000Z
Code Hosting Issue Metadata: published=2013-01-10T01:53:13.000Z stars=2 updated=2013-04-23T18:21:34.000Z originalIssue=https://code.google.com/p/endpoints-proto-datastore/issues/detail?id=32 sub>author=@dhermes</sub