Google App Engine doesn’t have a unique constraint in the classical sense of relational databases. This is a favourite construct of application developers and it’s unfortunate that it’s not present. At the same time, a basic understanding of the underlying datastore points to why it’s tough, or at least inefficient.

There are places where you’re willing to sacrifice some performance in order to guarantee a unique value. Datastore does guarantee uniqueness on its keys, so we can use a secondary helper model to guarantee uniqueness.



class Unique(db.Model):

@classmethod

def check(cls, scope, value):

def tx(scope, value):

key_name = "U%s:%s" % (scope, value,)

ue = Unique.get_by_key_name(key_name)

if ue:

raise UniqueConstraintViolation(scope, value)

ue = Unique(key_name=key_name)

ue.put()

db.run_in_transaction(tx, scope, value)

class UniqueConstraintViolation(Exception):

def __init__(self, scope, value):

super(UniqueConstraintViolation, self).__init__("Value '%s' is not unique within scope '%s'." % (value, scope, ))



This class simply leverages the key uniqueness aspect of datastore to ensure that the value doesn’t exist. A call to check() where a matching scope and value already exists will raise a UniqueConstraintViolation.

To use this, you need to build out a common create method on your models. Below, I’ve used a classmethod to achieve this:



class Account(db.Model):

name = db.StringProperty()

email = db.StringProperty() # unique=True - wouldn't that be great?

@classmethod

def create(cls, name, email):

Unique.check("email", email)

a = Account(name=name, email=email)

a.put()

return a



I need to make a call to Unique.check() first to ensure uniqueness (which causes at least a lookup, but likely a lookup and a put – both are a performance hit relative to doing nothing at all), and then I create my own account. Unique.check() will throw if the value is not unique, preventing the Account from being created.

Note that this technique lugs around an additional dictionary in datastore (costing some $$, though realistically not much). Also note that if you jam too many scopes into this class, you’ll get degrading performance (though it’s still just a key lookup which is very efficient).