Question

I am trying to use endpoints to update some JSON values in my datastore. I have the following Datastore in GAE...

class UsersList(ndb.Model):
    UserID = ndb.StringProperty(required=True)
    ArticlesRead = ndb.JsonProperty()
    ArticlesPush = ndb.JsonProperty()

In general what I am trying to do with the API is have the method take in a UserID and a list of articles read (with an article being represented by a dictionary holding an ID and a boolean field saying whether or not the user liked the article). My messages (centered on this logic) are the following...

class UserID(messages.Message):
    id = messages.StringField(1, required=True)


class Articles(messages.Message):
    id = messages.StringField(1, required=True)
    userLiked = messages.BooleanField(2, required=True)


class UserIDAndArticles(messages.Message):
    id = messages.StringField(1, required=True)
    items = messages.MessageField(Articles, 2, repeated=True)


class ArticleList(messages.Message):
    items = messages.MessageField(Articles, 1, repeated=True)

And my API/Endpoint method that is trying to do this update is the following...

@endpoints.method(UserIDAndArticles, ArticleList,
                  name='user.update',
                  path='update',
                  http_method='GET')
def get_update(self, request):
    userID = request.id
    articleList = request.items
    queryResult = UsersList.query(UsersList.UserID == userID)

    currentList = []

    #This query always returns only one result back, and this for loop is the only way
    # I could figure out how to access the query results.
    for thing in queryResult:
        currentList = json.loads(thing.ArticlesRead)

    for item in articleList:
        currentList.append(item)

    for blah in queryResult:
        blah.ArticlesRead = json.dumps(currentList)
        blah.put()

    for thisThing in queryResult:
        pushList = json.loads(thisThing.ArticlesPush)

    return ArticleList(items = pushList)

I am having two problems with this code. The first is that I can't seem to figure out (using the localhost Google APIs Explorer) how to send a list of articles to the endpoints method using my UserIDAndArticles class. Is it possible to have a messages.MessageField() as an input to an endpoint method?

The other problem is that I am getting an error on the 'blah.ArticlesRead = json.dumps(currentList)' line. When I try to run this method with some random inputs, I get the following error...

TypeError: <Articles
 id: u'hi'
 userLiked: False> is not JSON serializable

I know that I have to make my own JSON encoder to get around this, but I'm not sure what the format of the incoming request.items is like and how I should encode it.

I am new to GAE and endpoints (as well as this kind of server side programming in general), so please bear with me. And thanks so much in advance for the help.

Was it helpful?

Solution

A couple things:

  • http_method should definitely be POST, or better yet PATCH because you're not overwriting all existing values but only modifying a list, i.e. patching.
  • you don't need json.loads and json.dumps, NDB does it automatically for you.
  • you're mixing Endpoints messages and NDB model properties.

Here's the method body I came up with:

# get UsersList entity and raise an exception if none found.
uid = request.id
userlist = UsersList.query(UsersList.UserID == uid).get()
if userlist is None:
    raise endpoints.NotFoundException('List for user ID %s not found' % uid)

# update user's read articles list, which is actually a dict.
for item in request.items:
    userslist.ArticlesRead[item.id] = item.userLiked
userslist.put()

# assuming userlist.ArticlesPush is actually a list of article IDs.
pushItems = [Article(id=id) for id in userlist.ArticlesPush]
return ArticleList(items=pushItems)

Also, you should probably wrap this method in a transaction.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top