Does GAE ProtoRPC support json data field for request - google-app-engine

I am doing development on python and GAE,
When I try to use ProtoRPC for web service, I cannot find a way to let my request contain a json format data in message. example like this:
request format:
{"owner_id":"some id","jsondata":[{"name":"peter","dob":"1911-1-1","aaa":"sth str","xxx":sth int}, {"name":...}, ...]}'
python:
class some_function_name(messages.Message):
owner_id = messages.StringField(1, required=True)
jsondata = messages.StringField(2, required=True) #is there a json field instead of StringField?
any other suggestion?

What you'd probably want to do here is use a MessageField. You can define your nested message above or within you class definition and use that as the first parameter to the field definition. For example:
class Person(Message):
name = StringField(1)
dob = StringField(2)
class ClassRoom(Message):
teacher = MessageField(Person, 1)
students = MessageField(Person, 2, repeated=True)
Alternatively:
class ClassRoom(Message):
class Person(Message):
...
...
That will work too.
Unfortunately, if you want to store arbitrary JSON, as in any kind of JSON data without knowing ahead of time, that will not work. All fields must be predefined ahead of time.
I hope that it's still helpful to you to use MessageField.

Related

GAE: Writing the API: a simple PATCH method (Python)

I have a google-cloud-endpoints, in the docs, I did'nt find how to write a PATCH method.
My request:
curl -XPATCH localhost:8080/_ah/api/hellogreeting/1 -d '{"message": "Hi"}'
My method handler looks like this:
from models import Greeting
from messages import GreetingMessage
#endpoints.method(ID_RESOURCE, Greeting,`
path='hellogreeting/{id}', http_method='PATCH',
name='greetings.patch')
def greetings_patch(self, request):
request.message, request.username
greeting = Greeting.get_by_id(request.id)
greeting.message = request.message # It's ok, cuz message exists in request
greeting.username = request.username # request.username is None. Writing the IF conditions in each string(checking on empty), I think it not beatifully.
greeting.put()
return GreetingMessage(message=greeting.message, username=greeting.username)
So, now in Greeting.username field will be None. And it's wrong.
Writing the IF conditions in each string(checking on empty), I think it not beatifully.
So, what is the best way for model updating partially?
I do not think there is one in Cloud Endpoints, but you can code yours easily like the example below.
You will need to decide how you want your patch to behave, in particular when it comes to attributes that are objects : should you also apply the patch on the object attribute (in which case use recursion) or should you just replace the original object attribute with the new one like in my example.
def apply_patch(origin, patch):
for name in dir( patch ):
if not name.startswith( '__' ):
setattr(origin,name,getattr(patch,name))

pattern for updating a datastore object

I'm wondering what the right pattern should be to update an existing datastore object using endpoints-proto-datastore.
For example, given a model like the one from your GDL videos:
class Task(EndpointsModel):
detail = ndb.StringProperty(required=True)
owner = ndb.StringProperty()
imagine we'd like to update the 'detail' of a Task.
I considered something like:
#Task.method(name='task.update',
path='task/{id}',
request_fields=('id', 'detail'))
def updateTask(self, task):
pass
However, 'task' would presumably contain the previously-stored version of the object, and I'm not clear on how to access the 'new' detail variable with which to update the object and re-store it.
Put another way, I'd like to write something like this:
def updateTask(self, task_in_datastore, task_from_request):
task_in_datastore.detail = task_from_request.detail
task_in_datastore.put()
Is there a pattern for in-place updates of objects with endpoints-proto-datastore?
Thanks!
See the documentation for details on this
The property id is one of five helper properties provided by default
to help you perform common operations like this (retrieving by ID). In
addition there is an entityKey property which provides a base64
encoded version of a datastore key and can be used in a similar
fashion as id...
This means that if you use the default id property your current object will be retrieved and then any updates from the request will replace those on the current object. Hence doing the most trivial:
#Task.method(name='task.update',
path='task/{id}',
request_fields=('id', 'detail'))
def updateTask(self, task):
task.put()
return task
will perform exactly what you intended.
Task is your model, you can easily update like this:
#Task.method(name='task.update',
path='task/{id}',
request_fields=('id', 'detail'))
def updateTask(self, task):
# Task.get_by_id(task.id)
Task.detail = task.detail
Task.put()
return task

Retrieving db.Property choices

Searching on the documentation provided by google and browsing SO I haven't found a way to retrieve the choices set on a db.Property object (I want to retrieve it in order to create forms based on the model).
I'm using the following recipe to do what I need, Is this correct? Is there any other way of doing it? (simpler, more elegant, more pythonic, etc.)
For a model like this:
class PhoneNumber(db.Model):
contact = db.ReferenceProperty(Contact,
collection_name='phone_numbers')
phone_type = db.StringProperty(choices=('home', 'work'))
number = db.PhoneNumberProperty()
I do the following modification:
class PhoneNumber(db.Model):
_phone_types = ('home', 'work')
contact = db.ReferenceProperty(Contact,
collection_name='phone_numbers')
phone_type = db.StringProperty(choices=_phone_types)
number = db.PhoneNumberProperty()
#classmethod
def get_phone_types(self):
return self._phone_types
You should be able to use PhoneNumber.phone_type.choices. If you want you could make that into a class method too:
#classmethod
def get_phone_types(class_):
return class_.phone_type.choices
You can decide if you prefer the class method approach or not.
Don't forget about Python's dir built-in! It is very useful when exploring objects.

Google App Engine(Python) : How to specify a column in the entity using URL argument?

I am trying to make general purpose image display class which
receives two parameters,
Key, and "Column location info in entity"
from URL and return and display specified image delivered from blob.
If you know what I am doing wrong, please give me a hint.
I have datastore "item" like below,
Key | image_index | image1 | image2 | image3 |
and I am requesting image with the URL like below,
http://stackoverwlow.com/image/{key}/{image_N}
I made url handler in the main like below,
def main():
application = webapp.WSGIApplication(
[('/', MainPage),
('/image/([^/]+)/([^/]+)', imageDisplay),
], debug=True)
wsgiref.handlers.CGIHandler().run(application)
and
I made imageDisplay class like below,
from google.appengine.ext import db
from google.appengine.ext import webapp
class imageDisplay(webapp.RequestHandler):
def get(self, _key, _size):
image = db.get(_key)
self.response.headers['Content-Type'] = 'image/jpg'
self.response.out.write(image._size)
But, if I try this code, it will return following error,
global name 'image_size' is not defined
If I specify which image in the entity should be displayed, it works.
So, the data is there.
self.response.out.write(image.image3)
My question is, how to specify "_size" from obtained entity "image"?
Thank you in advance.
With best regards
You need to fetch the attribute programmatically, like this:
self.response.out.write(getattr(image, size))
Since you're addressing your values like an array, you should probably just use one, though: use a db.ListProperty(db.Blob), instead!

Google App Engine - Is this also a Put method? Or something else

Was wondering if I'm unconsciously using the Put method in my last line of code ( Please have a look). Thanks.
class User(db.Model):
name = db.StringProperty()
total_points = db.IntegerProperty()
points_activity_1 = db.IntegerProperty(default=100)
points_activity_2 = db.IntegerProperty(default=200)
def calculate_total_points(self):
self.total_points = self.points_activity_1 + self.points_activity_2
#initialize a user ( this is obviously a Put method )
User(key_name="key1",name="person1").put()
#get user by keyname
user = User.get_by_key_name("key1")
# QUESTION: is this also a Put method? It worked and updated my user entity's total points.
User.calculate_total_points(user)
While that method will certainly update the copy of the object that is in-memory, I do not see any reason to believe that the change will be persisted to the the datastore. Datastore write operations are costly, so they are not going to happen implicitly.
After running this code, use the datastore viewer to look at the copy of the object in the datastore. I think that you may find that it does not have the changed total_point value.

Resources