Google App Engine: A handler to delete 'any' entity - python

My entities can currently be viewed using the path /12345 where 12345 represents its entity ID.
I want to create a handler which deletes any entity using /12345/delete.
The handler would look something like this:
class DeleteHandler(BaseHandler):
def get(self, entity_id):
# cannot retrieve entity using just entity_id
# We cannot use KIND.get_by_id(entity_id), since 'entity kind' is unknown
How can we get the entity kind using the url, so that we may use get_by_id() to retrieve the entity, and then delete it?
Am I approaching this correctly? Or is there a better way?

Entity ids are not unique across kinds, so I don't really know how you are expecting this to work.
The websafe encoded version of the full key, however, does give enough information to uniquely identify the entity. You can then use that to instantiate a key, and you can call delete on that key.

It doesn't feel right.
I would go simply with kind/12345/delete
To answer your question, one approach could be with whitelisting.
Set somewhere the kinds (classes) you want this operation to delete their objects (entities).
Suppose you have these Kinds:
class Foo(ndb.Expando):
pass
class Boo(ndb.Expando):
pass
class Bar(ndb.Expando):
pass
Now since you have the id:
kinds_to_delete = ['Foo', 'Boo', 'Bar']
for kind_to_delete in kinds_to_delete:
ndb.Key(kind_to_delete, entity_id).delete()
Or as a dirty oneliner:
[ndb.Key(kind, entity_id).delete() for kind in ['Foo', 'Boo', 'Bar']]

/<key>/delete
Maybe it's easier if you passed around the base64 representation of the key

Related

How to check the existance of single Entity? Google App Engine, Python

Sorry for noobster question again.
But I'm trying to do some very easy stuff here, and I don't know how. Documentation gives me hints which do not work, or apply.
I recieve a POST request and grab a variable out of it. It says "name".
I have to search all over my entities Object (for example) and find out if there's one that has the same name. Is there's none, I must create a new Entity with this name. Easy it may look, but I keep Failing.
Would really appreciate any help.
My code currently is this one:
objects_qry = Object.query(Object.name == data["name"])
if (not objects_qry ):
obj = Object()
obj .name = data["name"]
obj .put()
class Object(ndb.Model):
name = ndb.StringProperty()
Using a query to perform this operation is really inefficient.
In addition your code is possibly unreliable, if name doesn't exist and you have two requests at the same time for name you could end up with two records. And you can't tell because your query only returns the first entity with the name property equal to some value.
Because you expect only one entity for name a query is expensive and inefficient.
So you have two choices you can use get_or_insert or just do a get, and if you have now value create a new entity.
Any way here is a couple of code samples using the name as part of the key.
name = data['name']
entity = Object.get_or_insert(name)
or
entity = Object.get_by_id(name)
if not entity:
entity = Object(id=name)
entity.put()
Calling .query just creates a query object, it doesn't execute it, so trying to evaluate is as a boolean is wrong. Query object have methods, fetch and get that, respectively, return a list of matching entities, or just one entity.
So your code could be re-written:
objects_qry = Object.query(Object.name == data["name"])
existing_object = objects_qry.get()
if not existing_object:
obj = Object()
obj.name = data["name"]
obj.put()
That said, Tim's point in the comments about using the ID instead of a property makes sense if you really care about names being unique - the code above wouldn't stop two simultaneous requests from creating entities with the same name.

NDB key vs get_by_id

Just to know if I'm mistaked or not:
get() operations uses NDB cache, so this (Chapter is ndb.Model class):
# Get the entity
chapter_key = ndb.Key('Book', long(bookId), 'Chapter', long(chapterId))
chapter = chapter_key.get()
can use the ndb cache if is 2nd or more read of the entity.
But if I make this?
Chapter.get_by_id(long(id), parent=ndb.Key('Book', long(bookId)))
is this managed by ndb also, or this operation is a standart db operation and don't use cache?
Model.get_by_id will use the context-cache and memcache in exactly the same way as Key.get
As Greg's answer is correct, I just wanted to mention that instead of putting keys together manually, you may use urlsafe string, and pass it between functions.
Assuming you have a key:
page_key = ndb.Key('Book', long(bookId), 'Chapter', long(chapterId), 'Page', long(pageId))
Create urlsafe from it:
page_url_string = page_key.urlsafe()
To retrive the model, simply use:
page = ndb.Key(urlsafe=page_url_string).get()
Consider it if you're using models with several parentness, there should be no case when putting keys manually would be required, code gets messy really quick, as you need to pass additional variables between functions.

Is it possible to have an entity kind with no property?

I would like to keep a list of 'things' in Datastore. No other information other than their names are required for these 'things', and these names must be unique, i.e. they can be thought as key_name for an entity. In this case, I don't need any explicit property/field for the entity kind (since the name of a 'thing' can be kept inside key_name of an entity). Is this possible? How? The alternative is to replicate this piece of information by having a dedicated field/property for name.
class EntityKindWithNoProperty(db.Model):
name = db.StringProperty()
I feel like this approach is duplication of information. What do you think?
This works
class Empty(ndb.Model):
pass
e = Empty(id="somestring")
e.put()
For uniqueness you can potentially generate the IDs in advance then consume them one by one with numeric keys. Or, depending on your use model, get_or_insert.
Have you considered having a single entity with a list property?
class ThingSingleton(db.Model):
things = db.StringListProperty()
The best approach to use would depend on your query patterns and the expected cardinality of things.

Recursive delete in google app engine

I'm using google app engine with django 1.0.2 (and the django-helper) and wonder how people go about doing recursive delete.
Suppose you have a model that's something like this:
class Top(BaseModel):
pass
class Bottom(BaseModel):
daddy = db.ReferenceProperty(Top)
Now, when I delete an object of type 'Top', I want all the associated 'Bottom' objects to be deleted as well.
As things are now, when I delete a 'Top' object, the 'Bottom' objects stay and then I get data that doesn't belong anywhere. When accessing the datastore in a view, I end up with:
Caught an exception while rendering: ReferenceProperty failed to be resolved.
I could of course find all objects and delete them, but since my real model is at least 5 levels deep, I'm hoping there's a way to make sure this can be done automatically.
I've found this article about how it works with Java and that seems to be pretty much what I want as well.
Anyone know how I could get that behavior in django as well?
You need to implement this manually, by looking up affected records and deleting them at the same time as you delete the parent record. You can simplify this, if you wish, by overriding the .delete() method on your parent class to automatically delete all related records.
For performance reasons, you almost certainly want to use key-only queries (allowing you to get the keys of entities to be deleted without having to fetch and decode the actual entities), and batch deletes. For example:
db.delete(Bottom.all(keys_only=True).filter("daddy =", top).fetch(1000))
Actually that behavior is GAE-specific. Django's ORM simulates "ON DELETE CASCADE" on .delete().
I know that this is not an answer to your question, but maybe it can help you from looking in the wrong places.
Reconsider the data structure. If the relationship will never change on the record lifetime, you could use "ancestors" feature of GAE:
class Top(db.Model): pass
class Middle(db.Model): pass
class Bottom(db.Model): pass
top = Top()
middles = [Middle(parent=top) for i in range(0,10)]
bottoms = [Bottom(parent=middle) for i in range(0,10) for middle in middles]
Then querying for ancestor=top will find all the records from all levels. So it will be easy to delete them.
descendants = list(db.Query().ancestor(top))
# should return [top] + middles + bottoms
If your hierarchy is only a small number of levels deep, then you might be able to do something with a field that looks like a file path:
daddy.ancestry = "greatgranddaddy/granddaddy/daddy/"
me.ancestry = daddy.ancestry + me.uniquename + "/"
sort of thing. You do need unique names, at least unique among siblings.
The path in object IDs sort of does this already, but IIRC that's bound up with entity groups, which you're advised not to use to express relationships in the data domain.
Then you can construct a query to return all of granddaddy's descendants using the initial substring trick, like this:
query = Person.all()
query.filter("ancestry >", gdaddy.ancestry + "\U0001")
query.filter("ancestry <", gdaddy.ancestry + "\UFFFF")
Obviously this is no use if you can't fit the ancestry into a 500 byte StringProperty.

How do I get the key value of a db.ReferenceProperty without a database hit?

Is there a way to get the key (or id) value of a db.ReferenceProperty, without dereferencing the actual entity it points to? I have been digging around - it looks like the key is stored as the property name preceeded with an _, but I have been unable to get any code working. Examples would be much appreciated. Thanks.
EDIT: Here is what I have unsuccessfully tried:
class Comment(db.Model):
series = db.ReferenceProperty(reference_class=Series);
def series_id(self):
return self._series
And in my template:
more
The result:
more
Actually, the way that you are advocating accessing the key for a ReferenceProperty might well not exist in the future. Attributes that begin with '_' in python are generally accepted to be "protected" in that things that are closely bound and intimate with its implementation can use them, but things that are updated with the implementation must change when it changes.
However, there is a way through the public interface that you can access the key for your reference-property so that it will be safe in the future. I'll revise the above example:
class Comment(db.Model):
series = db.ReferenceProperty(reference_class=Series);
def series_id(self):
return Comment.series.get_value_for_datastore(self)
When you access properties via the class it is associated, you get the property object itself, which has a public method that can get the underlying values.
You're correct - the key is stored as the property name prefixed with '_'. You should just be able to access it directly on the model object. Can you demonstrate what you're trying? I've used this technique in the past with no problems.
Edit: Have you tried calling series_id() directly, or referencing _series in your template directly? I'm not sure whether Django automatically calls methods with no arguments if you specify them in this context. You could also try putting the #property decorator on the method.

Categories