ndb.KeyProperty value get() returns None - google-app-engine

I'm storing the key to another entity in an ndb.KeyPropery attribute called teacher
class Section(ndb.Model):
name = ndb.StringProperty(required=True)
teacher = ndb.KeyProperty(required=True, kind=Staff)
students = ndb.IntegerProperty(repeated=True)
active = ndb.BooleanProperty(default=True)
I'm storing things there just fine, but when I retrieve a value it doesn't seem to fully behave like an ndb.Key
(in interactive console on dev server...)
from google.appengine.ext import ndb
import amt
from sections.model import Section
section = Section.query(Section.name == 'test').fetch()[0]
print(section.teacher)
print(type(section.teacher))
print(section.teacher.kind())
print(section.teacher.id())
print(section.teacher.get())
print(ndb.Key(section.teacher.kind(), int(section.teacher.id())).get())
gives...
Key('Staff', '5486563022602240')
<class 'google.appengine.ext.ndb.key.Key'>
Staff
5486563022602240
None
Kxxxxxxx, Mxxx
So why does section.teacher.get() return None and ndb.Key(section.teacher.kind(), int(section.teacher.id())).get() return my entity (__str__ is overridden to just print out a name...)

Notice the difference in the key Key('Staff', '5486563022602240') and your code that fetches ndb.Key(section.teacher.kind(), int(section.teacher.id()) these are different keys.
The first has a key_name defined (str) and your fetching successfully with an id (int).
Maybe you stored the wrong key in teacher the first time around.
How did you construct your original key ?

Related

ndb query by KeyProperty

I'm struggling with a KeyProperty query, and can't see what's wrong.
My model is
class MyList(ndb.Model):
user = ndb.KeyProperty(indexed=True)
status = ndb.BooleanProperty(default=True)
items = ndb.StructuredProperty(MyRef, repeated=True, indexed=False)
I create an instance of MyList with the appropriate data and can run the following properly
cls = MyList
lists = cls.query().fetch()
Returns
[MyList(key=Key('MyList', 12), status=True, items=..., user=Key('User', 11))]
But it fails when I try to filter by user, i.e. finding lists where the user equals a particular entity; even when using the one I've just used for insert, or from the previous query result.
key = lists[0].user
lists = cls.query(cls.user=key).fetch()
Returns
[]
But works fine with status=True as the filter, and I can't see what's missing?
I should add it happens in a unit testing environment with the following v3_stub
self.policy = datastore_stub_util.PseudoRandomHRConsistencyPolicy(probability=0)
self.testbed.init_datastore_v3_stub(
require_indexes=True,
root_path="%s/../"%(os.path.dirname(__file__)),
consistency_policy=self.policy
)
user=Key('User', 11) is a key to a different class: User. Not MyList
Perhaps you meant:
user = ndb.KeyProperty(kind='User', indexed=True)
Your code looks fine, but I have noticed some data integrity issues when developing locally with NDB. I copied your model and code, and I also got the empty list at first, but then after a few more attempts, the data is there.
Try it a few times?
edit: possibly related?
google app engine ndb: put() and then query(), there is always one less item

why is my key_name value not as i set it?

First off, I'm relatively new to Google App Engine, so I'm probably doing something silly.
I want the username to be set as key in model User
class User(db.Model):
#username is key
password = db.StringProperty(required = True)
type = db.StringProperty(required = True)
approved = db.BooleanProperty(required = True)
To insert i do this
user = User(key_name = self.request.get('username'), password = password, type = type, approved = False)
user.put()
I believe that when you set key_name manually it should be exactly what you set it to be but when i query user modle
users = db.GqlQuery("SELECT * FROM User")
for(user in users):
self.response.write(user.key())
I got the output as agxkZXZ-dmhvc3RlbDNyEQsSBFVzZXIiB2JodXNoYW4M
Please someone help!!
To start with you should read the docs on the Key class https://developers.google.com/appengine/docs/python/datastore/keyclass and how keys a structured - https://developers.google.com/appengine/docs/python/datastore/#Python_Kinds_keys_and_identifiers
Any way to your problem, note that the output of self.response.write(user.key()) is giving you the string agxkZXZ-dmhvc3RlbDNyEQsSBFVzZXIiB2JodXNoYW4M which is correct behaviour.
This is a URL safe form of the key which encodes all artifacts that make up the key.
This means you can round trip
user.key() = db.Key(encoded=str(user.key())
This allows you to use keys as part of URL. Whether that's wise or not is another discussion.
If you want to just show the name you used as the key_name then the docs for the Key class show you that the method name() will return the name.
As in user.key().name() or you could use id_or_name method which does what the name implies.
Perhaps self.request.get('username') is returning Null or None, and that results in the Datastore generating a default entry. According to the Users Service documentation it might need users.get_current_user().nickname() instead. Check by logging the values.
As Tim says you retrieve the name from the key using user.key().name().

How to remove value from GAE NDB Property (type BlobKeyProperty)

It might be the most dumb question and my apologies for the same but I am confused
I have the following entity:
class Profile(ndb.Model):
name = ndb.StringProperty()
identifier = ndb.StringProperty()
pic = ndb.BlobKeyProperty() # stores the key to the profile picture blob
I want to delete the "pic" property value of the above entity so that it should look as fresh as if "pic" was never assigned any value. I do not intend to delete the complete entity. Is the below approach correct:
qry = Profile.query(Profile.identifier==identifier)
result_record_list = qry.fetch()
if result_record_list:
result_record_list[0].pic.delete() # or result_record_list[0].pic = none # or undefined or null
I am deleting the actual blob referred by this blob key separately
assign None to it and put it back to the datastore.
result_record_list[0].pic = None
result_record_list[0].put()
The datastore is an OO schemaless databse. So you can add and remove properties from the the Kind (ndb.Model) without the need of a schema update.
If you also want to cleanup the entities look at this anwser from Guido

Why does db.Model.get_by_id() return None when no parent is specified?

I'm running the following code in the GAE interactive console (/_ah/admin/interactive), and I do not understand why get_by_id() returns None when the parent is not specified. The docs do not make this limitation clear and I can't think of a reason to enforce it.
import my_model
print my_model.all().fetch(1)[0].key().id() # Returns 33006, used later
print my_model.get_by_id(33006)
print my_model.get_by_id(my_model.all().fetch(1)[0].key().id())
parent = my_model.all().fetch(1)[0].parent()
print my_model.get_by_id(33006, parent=parent)
Output:
33006
None
None
<my_model object at 0x109a6a690>
db.Model definition and code showing object creation with ancestor:
class my_model(db.Model):
user_id = db.StringProperty(indexed=True)
email = db.StringProperty(indexed=True, default=None)
def create(parent):
obj = my_model(user_id='x', email='y', parent=parent)
obj.put()
The answer to your question is: because the same ID could be in another entity but with a different parent.
The IDs will be all different with the same parent or for all entities without a parent, but if there is an ancestor then your numerical IDs are not unique.

Python - read with key in App Engine

I have a python program in Google App Engine
When finding an object in the datastore when I have the key as a string, how can I do a direct read. Below is my code which is performing a loop, not good.....
class Opportunity(db.Model):
customer = db.ReferenceProperty(Customer,collection_name='opportunitys')
BNusername = db.StringProperty()
opportunity_no = db.StringProperty()
# etc etc etc.....
#BnPresets holds the object key as a string
opportunitys = Opportunity.all()
opportunitys.filter('BNusername =',BnPresets.myusername)
for oprec in opportunitys:
if str(oprec.key()) == BnPresets.recordkey:
opportunity = oprec
# I have the object here and can process etc etc
You can instantiate db.Key from string by passing it directly to the constructor:
opportunity_key = db.Key(BnPresets.recordkey)
Once you have that, simply db.get to obtain the entity identified by this key:
opportunity = db.get(opportunity_key)
I guess (by looking at the query you use) that you also want to verify the username of the object you got:
if opportunity.BNusername == BnPresets.myusername
process_opportunity(opportunity)
That should be pretty much it. The bottom line is that you should use the key first - as it uniquely identifies your entity - rather than querying for some other property and iterating through results.

Resources