MongoKat¶
MongoKat is a minimalist MongoDB ORM/ODM, inspired by the “hands off” API of MongoKit. It was created at Pricing Assistant, drawing from our experience managing a large Python codebase.
It differs from MongoKit in a few ways:
- Less features: we focus on basic Collection & Document methods.
- Less magic: MongoKit’s use of complex Python features like
__mro__
and__metaclass__
made bugs and memory leaks hard to debug.- Cleaner design: We enforce a separation between collection-level methods (find, aggregate, ...) and document-level methods (save, reload, ...)
- Better performance: The Cursor class is not wrapped anymore so the overhead of instanciating Documents instead of dicts is now close to zero.
- Requires pymongo 3.0+, taking advantage of its new features. To make transition to 3.0 easier (lots of pymongo’s APIs got renamed or deprecated) MongoKat still supports some 2.x-style parameters and method names.
- Support for simple hooks:
before_delete
,after_delete
,after_save
. Useful for keeping data up-to-date in ElasticSearch for instance, on a best-effort basis (some hooks may be lost under high load when using methods like update_many).- Support for protected fields that can’t be updated directly. Useful for making sure developers to use specific methods of a Document.
Installation¶
You can either clone the code from GitHub or install it via pip:
`
pip install mongokat
`
Code sample¶
# First, declare a Document/Collection pair (a "model"):
from mongokat import Collection, Document
class SampleDocument(Document):
def my_sum(self):
return self["a"] + self["b"]
class SampleCollection(Collection):
document_class = SampleDocument
def find_by_a(self, a_value):
return self.find_one({"a": a_value})
# Then use it in your code like this:
from pymongo import MongoClient
client = MongoClient()
Sample = SampleCollection(collection=client.my_db.my_col)
Sample.insert({"a": 1, "b": 2})
Sample.insert({"a": 2, "b": 3})
assert Sample.count() == 2
five = Sample.find_by_a(2)
assert five.my_sum() == 5
By the way, this is an actual test!
Migration guide from MongoKit¶
First you should get familiar with the new CRUD methods) from PyMongo 3.0. All of them work as expected in MongoKat.
We have generally tried to limit the changes needed for a migration to the models themselves, while the code actually using them should work without major changes.
Here is a list of things you should be aware of:
- You will have to split your Models into Document and Collection classes. For instance,
find()
belongs to a Collection, whereasreload()
belongs to a Document.- Initialization logic is different/cleaner, models are not magically registered everywhere, you have to explicitly instanciate them.
- Structures are not inherited.
API Reference¶
mongokat.collection.Collection¶
-
class
mongokat.collection.
Collection
(collection=None, database=None, client=None)[source]¶ mongokat.Collection wraps a pymongo.collection.Collection
-
document_class
¶ alias of
Document
-
structure
= None¶
-
immutable
= False¶
-
protected_fields
= ()¶
-
iter_column
(query=None, field='_id', **kwargs)[source]¶ Return one field as an iterator. Beware that if your query returns records where the field is not set, it will raise a KeyError.
-
trigger
(event, filter=None, update=None, documents=None, ids=None, replacements=None)[source]¶ Trigger the after_save hook on documents, if present.
-
connection
¶
-
db
¶
-
fetch
(spec=None, *args, **kwargs)[source]¶ return all document which match the structure of the object fetch() takes the same arguments than the the pymongo.collection.find method. The query is launch against the db and collection of the object.
-
fetch_one
(*args, **kwargs)[source]¶ return one document which match the structure of the object fetch_one() takes the same arguments than the the pymongo.collection.find method. If multiple documents are found, raise a MultipleResultsFound exception. If no document is found, return None The query is launch against the db and collection of the object.
-
mongokat.document.Document¶
-
class
mongokat.document.
Document
(doc=None, mongokat_collection=None, fetched_fields=None, gen_skel=None)[source]¶ -
mongokat_collection
= None¶
-
gen_skel
= True¶
-
b64id
¶ Returns the document’s _id as a base64-encoded string
-
ensure_fields
(fields, force_refetch=False)[source]¶ Makes sure we fetched the fields, and populate them if not.
-
reload
()[source]¶ allow to refresh the document, so after using update(), it could reload its value from the database.
Be carreful : reload() will erase all unsaved values.
If no _id is set in the document, a KeyError is raised.
-
save
(force=False, uuid=False, **kwargs)[source]¶ REPLACES the object in DB. This is forbidden with objects from find() methods unless force=True is given.
-