PK <JBgåød ød simon-0.2.0/meta.html
When defining a class, you can do more than just give it a name. By defining a class named Meta within a model, you can control several aspects of its behavior. Any options that are omitted will be given their default values, as show below.
class User(Model):
class Meta:
auto_timestamp = True
collection = 'users'
database = 'default'
field_map = {'id': '_id'}
map_id = True
safe = True
sort = None
By default, calling save() will cause the created and modified fields to update accordingly. Adding auto_timestamp = False to the Meta class will disable this behavior.
class Meta:
auto_timestamp = False # do not automatically add timestamps
By default, the collection associated with a model will be the name of the model with an s appended to it. Adding collection to the Meta class will allow its value to altered.
class Meta:
collection = 'simon' # store documents in the simon collection
By default, all collections will be located in the default database. If you use the connect() method to connect to additional databases, the database to use with a model can be controlled by adding the database option to the Meta class.
connect('localhost', name='logs', alias='logs')
class Meta:
database = 'logs' # use the logs database
By default, the _id field of all models is exposed through the id attribute. Additional fields can be added to the mapping by including them in the field_map dictionary. The keys of the dictionary represent attribute names and the values represent the keys used in the document.
When the map_id option is True (the default), you can define a custom mapping without having to include 'id': '_id'. It will be added for you.
class Meta:
# map friends to list_of_friends
field_map = {'list_of_friends': 'friends'}
map_id = False # do not map _id to id
You can also use field_map to expose nested fields as top-level attributes.
class Meta:
field_map = {'x': 'location.x', 'y': 'location.y'}
Why would you want to use this behavior? Unlike a relational database which stores its schema at the table level, MongoDB’s dynamic schema requires key names to be stored as part of each document. The longer the names of your keys, the more storage space you will need (keep in mind this is only really a problem with extremely large collections). When using shortened key names, it may make the names harder to remember, resulting in code that is harder to read and maintain. By utilizing field_map, more meaningful names can be used in code while storing shorter variations in the database.
class User(Model):
class Meta:
field_map = {
'first_name': 'fname',
'last_name': 'lname',
'location': 'loc',
}
user = User.create(first_name='Simon', last_name='Seville',
location='Fresno, CA')
This query executing in the mongo Shell would look a little different:
db.users.insert({fname: 'Simon', lname: 'Seville', loc: 'Fresno, CA'})
While Simon tries to expose MongoDB’s dynamic schema by not enforcing a schema on a model, there may be times when you wish to make sure that a document contains certain fields before it is saved. You can designate a field as required by adding it to the required_fields option in the Meta class.
class Meta:
required_fields = 'email'
With this setting, you wouldn’t be able to save a document unless it contained an email field.
You can also require multiple fields.
class Meta:
required_fields = ('email', 'name')
If you try to save a document that is missing andy of the required fields, TypeError will be raised.
With the introduction of MongoClient, updates are performed with write concern enabled. Simon mimics this behavior by setting the safe option in the Meta class to True. To revert to the previous behavior seen in versions of PyMongo prior to 2.4, set the safe option to False. When write concern is disabled at the model level, it can still be used on a case by case basis by providing safe=True as a parameter to method calls.
class Meta:
safe = False # don't use write concern for this model by default
More information about write concern is available in the MongoDB Docs.
By default, calls to all() and find() will use natural order for sorting. If you want to have a model default to a different sort order, you can do so by defining the sort option in the Meta class.
class Meta:
sort = 'name' # sort by name ascending
The default sort can also handle multiple fields.
class Meta:
sort = ('name', 'email') # sort by name and email ascending
For a explanation of how to take full advantage of the sort option, check out the sort() method.
More information about natural sort is available in the MongoDB Docs.
Meet Simon. He wants to help you create simple MongoDB models.
Simon is a model library for MongoDB. It aims to introduce the features of MongoDB and PyMongo in a Pythonic way. It allows you to work with objects and methods instead of only allowing you to work with everything as a dict.
Simon tries emphasize the flexibility and power of MongoDB. It does this in a couple of ways. First, unlike other libraries of its kind, Simon does not enforce a schema on your documents. This allows you to take advantage of the dynamic schemas offered by MongoDB. Second, while Simon allows you to perform traditional saves, it also allows you full control over performing atomic updates. This is covered in greater detail in the Basic Usage section.
To install the latest stable version of Simon:
$ pip install Simon
or, if you must:
$ easy_install Simon
To install the latest development version:
$ git clone git@github.com:dirn/Simon.git
$ cd Simon
$ python setup.py install
For more information, check out the PyMongo docs and the MongoDB docs.
There are a couple of different approaches that can be taken when writing data to a MongoDB database. Simon provides a few different methods to perform writes to help expose the full power of each.
The basic way to create or update a document is with the save() method. It will save the document associated with the instance to the database. If an update is being performed, the version of the document in the database will be overwritten by the version associated with the instance. This is known as document replacement. Any changes made to the version of the document in the database that have not been introduced to the instance will be lost.
user = User(name='Simon')
user.save()
This can be condensed into one step using the create() method.
user = User.create(name='Simon')
save() can also be used to save changes to a document.
user.email = 'simon@example.com'
user.save()
The first of these calls to save() will result in an insert. The second will result in an update. In the mongo Shell they would be written as:
db.users.insert({name: 'Simon'})
db.users.update({_id: ObjectId(...)}, {email: 'simon@example.com'})
MongoDB also offers a more powerful way to save changes to documents: atomic updates. By utilizing atomic updates, you can write selective changes to portions of a document without replacing the whole thing. Simon provides several different ways to perform atomic updates.
The save_fields() method will perform an atomic update updating only the specified fields.
# update only the score field
user.score = 100
user.save_fields('score')
You can also update multiple fields at once.
user.score = 200
user.friends = ['Alvin', 'Theodore']
user.save_fields(['score', 'friends'])
In the mongo Shell these would be:
db.users.update({_id: ObjectId(...)}, {$set: {score: 100}})
db.users.update({_id: ObjectId(...)}, {$set: {score: 200, friends: ['Alvin', 'Theodore']}})
The update() method provides a shortcut to the behavior offered by save_fields().
user.update(score=100)
user.update(score=200, friends=['Alvin', 'Theodore'])
The increment() method provides a way to increment the values of the specified fields. If the field does not exist, it will be added with the initial value of 0.
When incrementing only one field, only the name of the field needs to be given to increment(). A value can also be provided if incrementing by any value other than 1.
user.increment('score')
user.increment('score', 100)
increment() can also be used to increment multiple fields at once.
user.increment(score=100, level=1)
The equivalent queries in the mongo Shell would be:
db.users.update({_id: ObjectId(...)}, {$inc: {score: 1}})
db.users.update({_id: ObjectId(...)}, {$inc: {score: 100}})
db.users.update({_id: ObjectId(...)}, {$inc: {score: 100, level: 1}})
The remove_fields() method will remove the specified fields from the document in the database.
Using it works just like save_fields().
user.remove_fields('level')
user.remove_fields(['level', 'friends'])
To execute these same queries in the mongo Shell:
db.users.update({_id: ObjectId(...)}, {$unset: {level: 1}})
db.users.update({_id: ObjectId(...)}, {$unset: {level: 1, friends: 1}})
The raw_update() method allows any update query to be specified.
This method will let you execute any update that can’t appropriately be expressed through one of the other methods. Just make sure you use it with caution as Simon can do little to protect you.
user.raw_update({'$set': {'level': 1}, '$inc': {'score': 100}, '$unset': {'friends': 1}})
This query would be passed through to MongoDB as:
db.users.update({_id: ObjectId(...)}, {$set: {level: 1}, $inc: {score: 100}, $unset: {friends: 1}})
When Simon was first started, the default behavior with MongoDB was to perform writes without write concern. This led to faster performance but had the potential for data loss. Queries performed with write concern enabled will request the result of getLastError() before returning execution to the application. More information is available in the MongoDB Docs.
Simon was built with respect for this behavior as the default. All of the methods discussed above as well as delete() accept an argument called safe that can override the default behavior.
user = User(name='Simon')
user.save(safe=True)
user.update(email='simon@example.com', safe=True)
user.delete(safe=True)
This also applies to the get_or_create() method discussed in Querying.
Unlike get(), all() and find() return an instance of QuerySet. The QuerySet class utilizes PyMongo cursors to limit the amount of data that is actually transferred from the database.
Additionally it also exposes a few additional methods for controlling the database that is returned.
Full documentation for QuerySet is available in Simon API.
Instances of QuerySet can be sorted through the sort() method. It is called by passing in a series of field names, each one optionally prefixed by a - to denote that the field should be sorted in descending order.
If you sort by a field that doesn’t exist in all documents, a document without the field will be treated as if it has a value less than that of a document that has the field.
# sort by name ascending
users = User.all().sort('name')
Sorting by multiple fields is just as easy.
# sort by name and email ascending
users = User.all().sort('name', 'email')
# sort by name ascending and email descending
users = User.all().sort('name', '-email')
When sorting by multiple fields, the direction of one field’s sort will not affect the direction of other sorts.
# sort by name ascending, email descending, and date_of_birth ascending
users = User.all().sort('name', '-email', 'date_of_birth')
Here are the queries in the mongo Shell:
users = db.users.find().sort({name: 1})
users = db.users.find().sort({name: 1, email: 1})
users = db.users.find().sort({name: 1, email: -1})
users = db.users.find().sort({name: 1, email: -1, date_of_birth: 1})
When querying for documents, you may only want a subset of the documents that match your query. Simon allows you to control this through two methods, limit() and skip(). These allow you to control the number of documents returned and the number of documents to omit.
# retrieve the first 10 documents
users = User.all().limit(10)
# skip the first 10 documents
users = User.all().skip(10)
limit() and skip() can be chained together to create paged results.
# retrieve the second page of 10 documents
users = User.all().limit(10).skip(10)
The methods can be used in any order.
# retrieve the second page of 10 documents
users = User.all().skip(10).limit(10)
Here are the queries in the mongo Shell:
users = db.users.find().limit(10)
users = db.users.find().skip(10)
users = db.users.find().limit(10).skip(10)
users = db.users.find().skip(10).limit(10)
It is possible to get a list of unique values for a single field using distinct().
# get a list of all email addresses for users named Simon
emails = User.find(name='Simon').distinct('email')
Unlike Simon, the same query in the mongo Shell is handled at the collection level:
names = db.users.distinct('email', {name: 'Simon'})
Sometimes all you need is how many documents match your query. Simon provides that information in count().
count = User.all().count()
Simon makes sure that the any calls to limit() and skip() are factored in. Executing the same thing in mongo Shell would look like:
count = db.users.find().count(true)