KindError in Google App Engine - python

I defined a simple class in GAE for keeping user profiles data like this:
class User(db.Model):
email = db.EmailProperty()
role = db.StringProperty(default=roles.USER)
first_name = db.StringProperty()
last_name = db.StringProperty()
...
I use memcache to keep session information. memcache data looks like this { 'key': 'agpjYW5kaXJhdGVzcgoLEgRVc2VyGCMM'}. I get session_id value from the cookie. When I try to get user info linked to that cookie like this:
session_id = request['session_id']
data = memcache.get(session_id)
user = User.get(data['key'])
I get KindError exception:
KindError: Kind 'User' is not a subclass of kind 'User'
I know this user exists, memcache exists. User class is defined only once in my project. Why this error occurs and how can I make it work?
UPDATE: I tried to use db.get() instead of User.get() and it worked. So, what's the problem there can be?

Model.get() does check whether the supplied key is of the correct kind, as defined in the documentation. If not of the correct kind it will throw a KindError.
db.get() does not do any type checking and therefore will succeed with the supplied value if it exists in the data store, but will not necessarily return a User entity.
So you need to check whether the key in your memcache is actually of the User kind. Are you sure it's not overwritten with the key of a different model at some point?

The App Engine framework defines a class called 'User' as part of the Users API. In addition, you have your own class by the same name. When the exception occurs, you're trying to use one, but getting the other.
To avoid this, rename your model. You should also be careful how you import modules in Python. Instead of:
from google.appengine.api.users import User
or worse:
from google.appengine.api.users import *
you should use:
from google.appengine.api import users
And then refer to users.User, which is unambiguous.

The problem, it seems to me, is more subtle than that. I was getting the error with this call to Model.get() (I'm retrieving a top-level singleton object, always there):
datastore = GDSDatastore.get(gds.Key.from_path(*path))
so I investigated with this code:
datastore = gds.get(gds.Key.from_path(*path))
if not(datastore is None or isinstance(datastore, GDSDatastore)):
logger.error("KindError isinstance(GDSDatastore)=%s class=%s" % (isinstance(datastore, GDSDatastore), datastore.__class__.__name__))
raise gds.KindError('Kind %r is not a GDSDatastore instance' %
(datastore.kind()))
The vast majority of the time I get no error, but today I got this interesting log:
KindError isinstance(GDSDatastore)=False class=GDSDatastore
Now, that strikes me as rather peculiar.
(Note: GDSDatastore is defined locally: class GDSDatastore(gds.Model))

Related

How to set assignee to ticket in Zenpy python client library

I am integrating Zendesk ITSM API in my python library using Zenpy client.
For creating ticket using zenpy client , I can set reporter of ticket by email id, but for assignee I am not able to set assignee by email id.
This is the code I have tried so far.
ticket_payload = Ticket(
description="some description",
type="task",
priority="high",
status="open"
)
ticket_payload.requester = User(email="requester1#gmail.com"])
ticket_payload.assignee = User(email="assignee1#gmail.com")
response = self.zenpy_client.tickets.create(ticket_payload)
Response ticket I get has reporter set, but assigneee is None.
If I pass id along with the email as
ticket_payload.assignee = User(id= 354876354,email="assignee1#gmail.com")
then it works and I can see assignee is set to ticket. But this is not required for reporter.
Is this the defult behaviour of zenpy, or am I missing something?
It took me a while but I think I understand your issue. From the doc in the API Objects section under the Object Properties section:
When the assignee attribute is accessed, Zenpy first attempts to
locate the related User in the User cache and if it cannot be found
will generate and execute an API call to retrieve, instantiate, cache
and return the object.
I mistakenly assumed that is meant I could set the value of the assignee attribute and zenpy would look it up and then put the id in the assignee_id field. This is an incorrect interpretation. The quote means if you set the assignee_id and then attempt to access the assignee attribute it will look it up and return it. But there must be an id.
From the next section of the docs:
It is important to note that most property setters throw away all
information except for the id. This is because Zendesk only expects
the id, so any modifications made to the object will not be persisted
automatically.
So when you set the assignee or requestor without an id, the email address is thrown away and the respective id is set to None.
As to why the requester ID is set, it is probably set to the User that you use to authenticate the request rather than the one that you specify.

GAE Python NDB Datastore - No need for memcache.set?

The documentation (https://cloud.google.com/appengine/docs/python/ndb/) states that
NDB uses Memcache as a cache service for "hot spots" in the data
I am now using memcache only as follows:
memcache.set(key=(id), value=params, time=0)
That expires (auto flushes) pretty often and so I would like to use NDB Datastore.
I thought I would have to always put the key-value in both NDB and Memcache, then check both.
Is this being done automatically by NDB?
Ie.
ancestor_key = ndb.Key("Book", guestbook_name or "*notitle*")
greetings = Greeting.query_book(ancestor_key).fetch(20)
Would that implicitly set Memcache ?
And when I read from NDB, would it implicitly try a memcache.get(key) first?
Thanks for your patience.
EDIT - What I tried:
As a test I tried something like this:
class Book(ndb.Model):
content = ndb.StringProperty()
class update(webapp2.RequestHandler):
def post(self):
p1='1'
p2='2'
p3='3'
p4='4'
p5='5'
id='test'
paramarray = (p1,p2,p3,p4,p5)
book = Book(name=id,value=paramarray)
# OR likes this - book = Book(ndb.Key(id),value=paramarray)
book.put()
Both versions error out.
Trying to get a key of the var id with the values of paramarray
EDIT 2 Daniel, Thank you for everything.
Have follow up formatting questions, will ask a new question.
Yes; see the full documentation on ndb caching. Basically, every write is cached both in a request-local in-context cache, and in the main memcached store; a get by key will look up in both caches first before falling back to the real datastore.
Edit I can't understand why you think your example would work. You defined a model with a content property, but then try to set name and value properties on it; naturally that will fail.
You should go through the ndb documentation, which gives a good introduction to using the model class.

Django session data obtainable from models.py without key

I want to access a session variable from a model, in order to customize what gets shown in a template using {{item.seller_email}}. relevant code from models.py:
SESSION_STORE = import_module(settings.SESSION_ENGINE).SessionStore
class Item(Action):
def seller_email(self):
seller = self.get_seller()
session = SESSION_STORE()
debug('session: %s' % vars(session))
viewer = fetch(Client, email = session.get('client', None))
administrator = viewer.is_admin() if viewer else False
debug('viewer: %s, administrator: %s' % (viewer, administrator))
if administrator:
return seller.email
else:
return seller.anonymized[16:]
the answer here seems to say that this is not possible nor desired, but I lack the understanding of Django's session mechanism to be sure: Access session / request information outside of views in Django
this seems to indicate that the current session can be retrieved outside of views, though, just by not passing a key: https://docs.djangoproject.com/en/1.6/topics/http/sessions/#using-sessions-out-of-views, but I've tried it both that way and the above way with the same results: an "empty" session is created.
I'm sure there are other, better ways of doing this, and I will find one myself, but can someone familiar with Django internals explain why this is not possible?
Because the model shouldn't know anything about session, that creates unnecessary entanglement(?) beetween different components and violation of MVC pattern.
What I suggest to do, is to call the function, specifying parameters needed to get a proper result, viewer object in this case.
If using the code in the question, how do you expect your model to work if it's used in a management command (where there is no http request and as a result no session) ?
Or from a celery job ?

How to retrieve object by entityKey with endpoints-proto-datastore?

I have a model
class MyModel(EndpointsModel):
_message_fields_schema = ('entityKey', 'prop')
prop = ndb.StringProperty()
and an API method:
#MyModel.method(request_fields=('entityKey',),
path='mymodel/{entityKey}', http_method='GET', name='mymodel.get')
def mymodel_get(self, mymodel):
if not mymodel.from_datastore:
raise endpoints.NotFoundException('mymodel not found.')
return mymodel
But when I try a query like
http://localhost:8080/_ah/api/myapi/v1/mymodel/adsfasf-cm9jay1zdG9wchELEgRBcmVhGICAgICAgIAJDA
I get a 404. I know the object exists, and I know that's the correct urlsafe key. What's going on? The same code works when using 'id' instead of 'entityKey' and query with the integer key.
The entityKey you mention is the urlsafe combination of the object's type and id.
To create a key from that string use this code:
rev_key = ndb.Key(urlsafe=urlString)
For more information see this section in the NDB Datastore API docs.
The code you have written is correct, your model and method are fine.
Which leads me to believe your problem is actually in the app.yaml file.
Make sure one of your handlers looks like this:
handlers:
- url: /_ah/spi/.*
script: MyApi.APPLICATION
Note that although the handler points to /_ah/spi/.* the actual path is /_ah/api/
Good luck

How to set users.userid as a key in ndb

I'm trying to write appengine python code that uses the built-in authentication 'users' object and using userid as an ndb key
Here's my model:
class UserAccounts(ndb.Model):
UserID = ndb.KeyProperty(required=True)
In my handler:
I get the current user
user = users.get_current_user()
Instantiate an entry
account = Models.UserAccounts()
Set the userid to the ndb entry
account.UserID = userid
When I run it, I get this:
Expected Key, got '113804764227149124198'
Where am I going wrong? As much as possible, I'd like to use a KeyProperty instead of StringProperty for performance reasons.
by:
account.UserID = userid
I assume you meant:
account.UserID = user.user_id()
The user id is a string, not a key, so you can't use a KeyProperty here. In fact, AFAIK, User objects as returned from users.get_current_user() don't have a key (at least not one that is documented) since they aren't datastore entries. There is an ndb.UserProperty, but I don't think it's use is generally encouraged.
What performance reasons are you referring to?
I think what you want is something like this UserAccounts(id=user_id), this way the user_id is the key. With this approach you can remove the userid field from the model definition

Categories

Resources