Walkthrough¶
Bread is organized around the idea of models, that have properties. A model is the abstract definition of some object. An instantiated model is sometimes referred to as an Entity.
Creating a Model¶
>>> class Pet(bread.Model):
... name = bread.StringProperty(required=True)
... type = bread.StringProperty()
... age = bread.IntegerProperty()
... adopted_on = bread.DateTimeProperty()
The example shows creating a new model, Pet, that has some properties a pet
might have, like its name, age, and when it was adopted.
We can also see that model properties can have arguments; for example, the
name property is required.
Creating an Entity¶
>>> larry = Pet(name='Larry Bird', type='bird', age=2)
>>> larry
Pet(_updated=datetime.datetime(2015, 6, 3, 17, 17, 58, 865153, tzinfo=tzutc()), name=u'Larry Bird', age=2, adopted_on=None, _deleted=None, _created=datetime.datetime(2015, 6, 3, 17, 17, 58, 865132, tzinfo=tzutc()), type=u'bird')
>>> larry.save()
>>> larry
Pet(key=Key(of_type=<class __main__.Pet>, uuid='2tAQKS7iJaHb2BG5SJjMPC'), _updated=datetime.datetime(2015, 6, 3, 17, 19, 1, 702525, tzinfo=tzutc()), name=u'Larry Bird', age=2, adopted_on=None, _deleted=None, _created=datetime.datetime(2015, 6, 3, 17, 17, 58, 865132, tzinfo=tzutc()), type=u'bird')
So creating a model is fine, but what if we want to actually use it? Well, a model behaves just like you might expect a Python class to. In the example, a new Pet is created by passing in keyword arguments to the class constructor, and the resulting instance is put in a variable. The variable is now populated with all the data from the arguments.
Though we’ve created a new Pet, we haven’t persisted it yet. To do so,
larry.save() is called to commit the object to the backend.
When we show the object again, notice that there is now a property on the
object called key. The key holds a reference to the original model and the
UUID assigned to the entity. The UUID is a short UUID (technically speaking,
base57 with a specific alphabet) and will always be generated when the entity
is saved if it was not already available. You can interact with the key
property; for example, to get the UUID string, entity.key.uuid will provide
it.
Updating an Entity¶
>>> dict(larry)
{'_updated': datetime.datetime(2015, 6, 3, 17, 19, 1, 702525, tzinfo=tzutc()), 'name': u'Larry Bird', 'age': 2, 'adopted_on': None, '_deleted': None, '_created': datetime.datetime(2015, 6, 3, 17, 17, 58, 865132, tzinfo=tzutc()), 'type': u'bird'}
>>> larry.age += 1
>>> larry.save()
To update an entity, make any modifications you’d like, then just call
save() again. The entity will be saved to the backend.
Getting an Entity¶
>>> uuid = larry.key.uuid
>>> uuid
'2tAQKS7iJaHb2BG5SJjMPC'
>>> Pet.get(uuid)
>>> Pet.get(uuid)
Pet(key=Key(of_type=<class __main__.Pet>, uuid='2tAQKS7iJaHb2BG5SJjMPC'), _updated=datetime.datetime(2015, 6, 3, 17, 30, 20, 778198, tzinfo=tzutc()), name=u'Larry Bird', age=3, adopted_on=None, _deleted=None, _created=datetime.datetime(2015, 6, 3, 17, 17, 58, 865132, tzinfo=tzutc()), type=u'bird')
There are several ways to retreive an entity. Calling get() on the model is
a conventient way to load an entity and will return it directly; this method
also accepts a Key or a UUID as a string.
You can also load an entity if you have a Key populated with the model and
UUID.
Deleting an Entity¶
>>> larry.delete()
>>> larry
Pet(key=Key(of_type=<class __main__.Pet>, uuid='2tAQKS7iJaHb2BG5SJjMPC'), _updated=None, name=None, age=None, adopted_on=None, _deleted=None, _created=None, type=None)
>>> assert Pet.get(larry.key) is None
Deleting an entity is as simple as calling delete() on the entity. The
entity will be wiped, with the exception of the key property. Attempting to
load the entity again will return None as it would for any non-existent key.
Deletion is implemented as soft delete by default; the entity will still be
available in the datastore, but will never be returned as the result of a
query, or any other operation. To override this behavior, call delete with the
soft parameter set to false, like larry.delete(soft=False), and the entity
will be fully deleted.
Querying for an Entity¶
>>> for age in range(20):
... pet = Pet(name='Cat Stevens', type='cat')
... pet.age = age
... pet.save()
>>> pets = Pet.query(Pet.type == 'cat', limit=20).run()
>>> pets.count
20
>>> pets = Pet.query(Pet.age >= 18, limit=20).run()
>>> pets.count
2
>>> pets = Pet.query(Pet.age < 3, Pet.type == 'cat').run()
>>> pets.count
3
Querying for entities is done by calling query() on the model you created.
The method accepts things like a limit parameter, specifying the maximum
number of results to be returned, as well as the actual query filters.
Most queries are Python inequality statements, like Pet.age < 3. There are
some operators that are not overridden, notably in, which is expressed as
Pet.breed.is_in([breed1, breed2]).
In the example, we create 20 new Pets, each with a different age from 0 to 19. We can then query for all of those Pets by type, or by age, or by type and age, as in the last example.
The result of running the query is a ResultSet, which has a number of
useful properties, notably entities, count, and more. The ResultSet
also stores the original query under query, so that the limit and other
query parameters can be retreived.
Efficient primitive operations¶
You may want to perform a single primitive operation over a list of
keys. Bread supports these sorts of operations for the following
model methods: .get, .delete and .save. For example:
>>> Person.get(["a", Key(Person, "b")])
[Person(...), None]
>>> Person.save([p1, p2])
>>> Person.delete([p1.key, p2.key, Key(Person, "c")])