BadValueError: Expected string, got Key when creating child entity - google-app-engine

Let me start by saying I'm really new to Python, app engine, and the datastore, so please be kind if I'm missing something obvious.
I'm trying to set up some data in the datastore with ancestor relationships, sort of the same way as in the example guestbook application from Google:
class Guestbook(webapp2.RequestHandler):
def post(self):
greeting = Greeting(parent=guestbook_key)
I created this code as an experiment to see if I could get it to work:
class Subscriber(ndb.Model):
user_nickname = ndb.StringProperty()
user_id = ndb.StringProperty()
class Music(ndb.Model):
level = ndb.StringProperty() # top or sub
parent = ndb.StringProperty() # only if sub
title = ndb.StringProperty()
class SetupHandler(webapp2.RequestHandler):
def get(self):
user = users.get_current_user()
subscriber = Subscriber(id=user.user_id())
subscriber.user_id = user.user_id()
subscriber.user_nickname = user.nickname()
haydn = Music(parent=subscriber.key)
haydn.level = "top"
haydn.title = "Haydn Trumpet Concerto"
haydn.put()
It seems to me as though I'm trying to do basically the same thing.. but I get this error:
File "... Application\datastoreexplore\main.py", line 41, in get
haydn = Music(parent=subscriber.key) BadValueError: Expected string, got Key('Subscriber', '12169615523634875051')
I've spent hours reading docs and searching through stackoverflow and elsewhere. I'm stumped. What am I doing wrong??

Your problem is thou have defined parent as a StringProperty but then passing in a key which is a valid constructor argument if you want to create an entity with anancestor. You should not have property called parent.

Related

Retrieving Datastore Object With Key

Using examples from the GAE documentation I have successfully put and object to the datastore as I can view it in the admin console. Retrieving has been difficult, here is my code.
import webapp2
from google.appengine.ext import ndb
user_key = ndb.Key('Info_model', 'Bill')
class Info_model(ndb.Model):
username = ndb.StringProperty()
phone = ndb.IntegerProperty()
active = ndb.BooleanProperty()
class Create_entity(webapp2.RequestHandler):
def get(self):
user1 = Info_model(username = 'Bill',
phone = 1231231234,
active = False)
user1.put()
self.response.write('<!doctype html><html><body>Entity created.<pre>')
self.response.write('</pre></body></html>')
class MainPage(webapp2.RequestHandler):
def get(self):
self.response.out.write('<html><body>')
#get object from datastore using example from GAE Documentation.
user_key = ndb.Key('Info_model', 'Bill')
user1 = user_key.get()
self.response.write(user1.username)
self.response.write(user1.phone)
self.response.write(user1.active)
self.response.write('</body></html>')
application = webapp2.WSGIApplication([
('/', MainPage),
('/create', Create_entity)
], debug=True)
From datastore documentation:
It says:
Retrieving Entities from Keys
Given an entity's key, you can retrieve the entity from the Datastore:
sandy = sandy_key.get()
I believe this example assumes we have set a variable named sandy_key as an ndb.Key() but it doesn't really say.
I have successfully run all the ndb tutorial examples but they create a new object for each entry. I want to have only one object, call it up, edit it and put() it again. I suspect I have made the key incorrectly or called it incorrectly. I have also tried:
user1 = ndb.get('agxkZXZ-aWZnYWxlcnRyFwsSCkluZm9fbW9kZWwYgICAgICAgAkM')
Having copied the key from the admin console. This does not work, Eclipse code editor says, "Undefined variable frome import:get". I have tried a different example from the GAE documentation:
# Create an entity and write it to the Datastore.
ent = MyModel(name='booh', xyz=[10**100, 6**666])
assert ent.abc == 0
key = ent.put()
# Read an entity back from the Datastore and update it.
ent = key.get()
ent.abc += 1
ent.xyz.append(ent.abc//3)
ent.put()
But this seems to be made for all being in one scope. If I create an object in one class then try to retrieve it in another class, the variable ent in ent=key.get() is undefined.
As well I have tried many other examples in the documentation but many are incomplete and assume the reader is not a novice.
Given I have an object in the datastore, how can I retrieve that object specifically and print it out like the following:
class MainPage(webapp2.RequestHandler):
def get(self):
self.response.out.write('<html><body>')
#get object from datastore using example from GAE Documentation.
user_key = ndb.Key('Info_model', 'Bill')
user1 = user_key.get()
self.response.write(user1.username)
self.response.write(user1.phone)
self.response.write(user1.active)
self.response.write('</body></html>')
Sorry for the noob question, if there is a more appropriate forum for beginner GAE programmers please let me know.
The problem is, that you have created a key here ...
user_key = ndb.Key('Info_model', 'Bill')
... but this is never put() to the datastore.
Later (in your Create_entity get method) you are using ...
user1 = Info_model(username = 'Bill',
phone = 1231231234,
active = False)
user1.put()
... and this is correctly 'putting' the Info_model entity into the datastore.
However, this code ...
user_key = ndb.Key('Info_model', 'Bill')
user1 = user_key.get()
... attempts to get an entity of kind Info_model from the datastore with key_name "Bill", but this is not what you put into the datastore in the first place.
Maybe what you are trying to achieve, is to create an entity of kind Info_model, with key_name 'Bill', which you can then later get by keyname? If that is the case, try this code ...
user1 = Info_model.get_or_insert('Bill',
username='Bill',
phone=1231231234,
active=False)
user2_key = ndb.Key('Info_model', 'Bill')
user2 = user2_key.get()
assert user1.key == user2.key
Do be aware, however, that an entities key_name is final - you cannot change this later. You may want to consider how you would handle the scenario where Bill wanted to change his username.
As a side note, I would recommend using an ndb.StringProperty for the phone property, as often phone numbers start with a 0, or contain spaces or characters such as +

Simple datastore entity with 2 fields that are also unique

All I am trying to produce is an entity that holds a unique username, and a unique device ID, and the ability to return an error if either of these conditions are not met on submission.
The only way I can see is to perform a query within a transaction, then filter the results. This however requires an ancestor (which seems unnecessary for a single simple entity).
What is the best method to go about doing this?
Here is an example that does what you want.
I put 2 entities to show you also how to make relationships
class Person(ndb.Expando):
registration_date = ndb.DateTimeProperty(auto_now_add=True)
#property
def info(self):
info = PersonInfo.query(ancestor=self.key).get()
return info
class PersonInfo(ndb.Expando):
email = ndb.StringProperty()
nick_name = ndb.StringProperty()
edit_date = ndb.DateTimeProperty(auto_now=True)
Later in the controller for register:
class RegisterPersonHandler(webapp2.RequestHandler):
def get(self):
user = users.get_current_user() #Stub here
if not user:
self.redirect(users.create_login_url(self.request.uri), abort=True)
return
person = Person.get_or_insert(user.user_id())
if not self._register(person, user):
# more logging is needed
logging.warning('Warning registration failed')
return
#ndb.transactional()
def _register(self, person, user):
''' Registration process happens here
'''
# check if the person has info and if not create it
info = PersonInfo.query(ancestor=person.key).get()
if not info:
info = PersonInfo(id=user.user_id(), parent=person.key)
info.nick_name = user.nickname()
info.email = user.email()
info.put()
return True
To answer also the comment question:
How can you programatically tell whether the returned entity is a new
or existing one though?
Try checking against a property that is default. Eg creation_date etc.
Though you can also check on something you need or on another entity's existence like I do because I expect the data to be consistent, and if not then create the bond.

Using ndb.KeyProperty how to reference the same model?

I have a simple scenario where there is a User class which has the name, email and followers property.
class User(ndb.Model):
name = ndb.StringProperty(required = True)
search_name = ndb.StringProperty(required = True)
pw_hash = ndb.StringProperty(required = True)
email = ndb.StringProperty()
follows = ndb.KeyProperty(Follow, repeated=True)
followers = ndb.KeyProperty(User, repeated=True)
While executing this I am getting the error.
File "C:\ujjal\my project\cravel\code2\Users.py", line 46, in User
followers = ndb.KeyProperty(User, repeated=True)
NameError: name 'User' is not defined
INFO 2012-09-11 11:45:23,953 dev_appserver.py:2967] "GET / HTTP/1.1" 500 -
Any suggestion as to how model the "followers" attribute would be highly appreciated.
Thanks in Advance
Using a kind on a key property e.g. some_prop = ndb.KeyProperty(User) only enforces that the kind of the key must be of kind User. So you can still use a KeyProperty without a kind if need be.
However, if you want to enforce that all follower keys must be of kind User(inside the User model) then surround the kind with quotes:
followers = ndb.KeyProperty(kind='User', repeated=True)
It's explained a little better in the ndb cheat sheet
If you just want "followers" to be a KeyProperty then just put:
followers = ndb.KeyProperty(repeated=True)
or, what I think you are after, to specify the type of key.
follows = ndb.KeyProperty(kind=User,repeated=True)
I think you are just missing kind=User probably.
https://developers.google.com/appengine/docs/python/ndb/properties#types

How to create JSON-Response when Entities are referenced via Ancestor?

Maybe my question is somehow unspecific, sorry for that. I'm learning python and app engine (webapp2) at the moment.
I have this class:
class Ice(db.Model):
"""Models an individual Guestbook entry with an author, content, and date."""
name = db.StringProperty()
description = db.StringProperty(multiline=True)
date = db.DateTimeProperty(auto_now_add=True)
def getTags(self):
return Tag.all().ancestor(self).fetch(10)
Tags are referenced via ancestor.
When I use a jinja-template i can call ice.getTags() foreach Ice.
Now i want to serialize my Ice-object to JSON and want to have all Tags that belong to the Ice-object in my JSON-Output.
This does serialisation for me:
It works okay, but it doesn't include the Tags.
I'm feeling, that i have to declare Tags as Ice-Attribute, but i don't know how.
class IceHandler(basehandler.BaseHandler):
def get(self):
ice_query = model.Ice.all().order('-date')
ices = ice_query.fetch(10)
self.response.write(json.encode(ices))
Thanks!

GAE - retrieving the last entry (python)

I am trying to get the most recent data item from the datastore.
I am trying to apply the method as explained here but I am getting the object <__main__.Rep object at 0x075F1730> not the item. Can anyone explain what I am doing wrong?
The Model is:
class Rep(db.Model):
sent = db.StringProperty()
time = db.DateTimeProperty(auto_now_add=True)
This is the handler:
class Repeater(webapp.RequestHandler):
def get(self):
reps = Rep()
reps.sent = self.request.get('sentence')
reps.put()
s = self.request.get('sentence')
query = reps.all()
last = query.order('-time').get()
sentences = query
Thanks!
I don't see anything wrong at all.
<__main__.Rep object at 0x075F1730> is presumably an instance of your Rep class, as expected.

Resources