When you think about data storage with respect to App Engine, it is better to think of this as a mechanism to persist object data than to think about rows and columns that one worries about in a traditional relational database management system (RDBMS). Instead of defining tables, the developer defines Python objects. A persistable object inherits from one of the following types from the google.appengine.ext.db module:
- Model: A data model whose properties are all defined within the class definition.
- Expando: A data model whose properties are determinted dynamically.
- PolyModel: Allows for models that use inheritance.
Each value in the data class must be an instance of a property type. There are strings, numeric, dates, boolean, blogs, lists, postal address, geographic points, and other values. You can see the complete list in the App Engine docs. All objects in the models have a unique key property, key(). The property isn’t just unique across the type– the property is unique across all objects. In the photo application, we have a fairly simple set of data: people who own pictures and the pictures plus metadata. Owners are fairly simple: we only need to be able to look them up by name or key. Recalling that ALL of our objects automatically have a key, the owner model is exceptionally simple:
This says that we have an Owner that inherits from db.Model. Owner has one property, name, that is a String.
owner = db.ReferenceProperty(Owner)
caption = db.StringProperty()
description = db.TextProperty()
image = db.BlobProperty()
date = db.DateTimeProperty(auto_now_add=True)
public = db.BooleanProperty()
Every ImageFile has an Owner. The owner property is ReferenceProperty. The value of a ReferenceProperty is the same as the key value for the referenced item. In this case, we state that the ReferenceProperty will always reference an Owner. We allow for caption to be a string and description to be a TextProperty. A TextProperty is like a StringProperty in that both can hold Unicode characters. They are different in these two forms:
- StringPropertys can be used for sorting. TextProperty cannot.
- StringProperty supports only 500 characters. TextProperty can be larger. TextProperty is a BlobProperty with encoding.
And yes,a BlobProperty is what you think it is– a block of bytes. For the date on the ImageFile, you will see that the item has auto_now_add set to True. This forces the date to be set to the current time when the Model is added to the data store.
App Engine has standard Create|Retrieve|Update|Delete (CRUD) operations. Objects that inherit from Model|Expando|PolyModel merge Create|Update into one operation: put(). Delete is db.delete(), and get is implemented by db.get(). Get supports retrieving an object by ID or by query. For example, to get all the ImageFile objects for a known user, one would write this query:
images = db.GqlQuery("SELECT * FROM ImageFile WHERE owner=:1", owner)
The preceding code assumes a valid Owner object. In the query, the owner.key() value will appear in place of :1. If you know the key for the image you want to extract, then you can get the item directly:
image = db.get(self.request.get("id"))
The preceding gets the ID from the query string of a request. Finally, with an object in hand, one can also delete the object:
requestedImage = db.get(self.request.get("id"))
if requestedImage.owner.key() == currentUser.key():
And this is all one needs to know in order to handle manipulating data in App Engine. Please note that this is a fairly simple application with simple needs. This sample application has been proving itself as a good way to learn the basics. It is also bringing up things to investigate further:
Are cascading deletes handled?
Is referential integrity handled?
Can I do batch updates?
Can I delete the results of a query?
But, those are questions to be answered on another day.