The most idiomatic and friendly-yet-powerful way to use MongoDB with Python
Project description
Mongo-Thingy is the most idiomatic and friendly-yet-powerful way to use MongoDB with Python.
It is an "Object-Document Mapper" that gives you full advantage of MongoDB schema-less design by not asking you to define schemas in your code.
What you'll get:
- a simple and robust pure-Python code base, with 100% coverage and few dependencies;
- PyMongo query language - no need to learn yet another one;
- both sync and async support! choose what suits you best;
- Thingy views - control what to show, and create fields based on other fields;
- swappable backend - wanna use SQLite behind the scenes? well, you can;
- versioning (optional) - rollback to any point in any thingy history;
- and more!
Compatibility
We support all Python and MongoDB versions supported by PyMongo, namely:
- CPython 3.7+ and PyPy3.7+
- MongoDB 3.6, 4.0, 4.2, 4.4, and 5.0.
As a backend, Mongo-Thingy supports the following libraries:
-
Synchronous:
-
Asynchronous:
- Motor (default when Motor is installed)
- Motor with Tornado (default when Motor and Tornado are installed)
- Mongomock-Motor
Install
pip install mongo-thingy
Examples
First steps
Connect, insert and find thingies
>>> from mongo_thingy import connect, Thingy
>>> connect("mongodb://localhost/test")
>>> class User(Thingy):
... pass
>>> user = User({"name": "Mr. Foo", "age": 42}).save()
>>> User.count_documents()
1
>>> User.find_one({"age": 42})
User({'_id': ObjectId(...), 'name': 'Mr. Foo', 'age': 42})
In an AsyncIO (or Tornado) environment, use the asynchronous class instead:
>>> from mongo_thingy import connect, AsyncThingy
>>> connect("mongodb://localhost/test")
>>> class User(AsyncThingy):
... pass
>>> user = await User({"name": "Mr. Foo", "age": 42}).save()
>>> await User.count_documents()
1
>>> await User.find_one({"age": 42})
User({'_id': ObjectId(...), 'name': 'Mr. Foo', 'age': 42})
To use another backend than the default ones, just pass its client class with
client_cls
:
>>> import mongomock
>>> connect(client_cls=mongomock.MongoClient)
Update a thingy
>>> user.age
42
>>> user.age = 1337
>>> user.save()
User({'_id': ObjectId(...), 'name': 'Mr. Foo', 'age': 1337})
Thingy views power
Complete information with properties
>>> class User(Thingy):
... @property
... def username(self):
... return "".join(char for char in self.name if char.isalpha())
>>> User.add_view(name="everything", defaults=True, include="username")
>>> user = User.find_one()
>>> user.view("everything")
{'_id': ObjectId(...), 'name': 'Mr. Foo', 'age': 1337, 'username': 'MrFoo'}
Hide sensitive stuff
>>> User.add_view(name="public", defaults=True, exclude="password")
>>> user.password = "t0ps3cr3t"
>>> user.view()
{'_id': ObjectId(...), 'name': 'Mr. Foo', 'age': 1337, 'password': 't0ps3cr3t'}
>>> user.view("public")
{'_id': ObjectId(...), 'name': 'Mr. Foo', 'age': 1337}
Only use certain fields/properties
>>> User.add_view(name="credentials", include=["username", "password"])
>>> user.view("credentials")
{'username': 'MrFoo', 'password': 't0ps3cr3t'}
Apply views on cursors
>>> cursor = User.find()
>>> for credentials in cursor.view("credentials"):
... print(credentials)
{'username': 'MrFoo', 'password': 't0ps3cr3t'}
{'username': 'MrsBar', 'password': '123456789'}
...
And if your cursor is already exhausted, you can still apply a view!
>>> users = User.find().to_list(None)
>>> for credentials in users.view("credentials"):
... print(credentials)
{'username': 'MrFoo', 'password': 't0ps3cr3t'}
{'username': 'MrsBar', 'password': '123456789'}
...
Versioning
>>> from mongo_thingy.versioned import Versioned
>>> class Article(Versioned, Thingy):
... pass
>>> article = Article(content="Cogito ergo sum")
>>> article.version
0
>>> article.save()
Article({'_id': ObjectId('...'), 'content': 'Cogito ergo sum'})
>>> article.version
1
>>> article.content = "Sum ergo cogito"
>>> article.save()
Article({'_id': ObjectId('...'), 'content': 'Sum ergo cogito'})
>>> article.version
2
>>> article.revert()
Article({'_id': ObjectId('...'), 'content': 'Cogito ergo sum'})
>>> article.version
3
Database/collection "discovery"
Default behaviour
>>> class AuthenticationGroup(Thingy):
... pass
>>> connect("mongodb://localhost/")
>>> AuthenticationGroup.collection
Collection(Database(MongoClient(host=['localhost:27017'], ...), 'authentication'), 'group')
Use mismatching names for Thingy class and database collection
You can either specify the collection name:
>>> class Foo(Thingy):
... collection_name = "bar"
or the collection directly:
>>> class Foo(Thingy):
... collection = db.bar
You can then check what collection is being used with:
>>> Foo.collection
Collection(Database(MongoClient('localhost', 27017), 'database'), 'bar')
Indexes
Create an index
>>> User.create_index("email", sparse=True, unique=True)
Add one or more indexes, create later
>>> User.add_index("email", sparse=True, unique=True)
>>> User.add_index("username")
>>> User.create_indexes()
Create all indexes of all thingies at once
>>> from mongo_thingy import create_indexes
>>> create_indexes()
Dealing with camelCase data
>>> from mongo_thingy.camelcase import CamelCase
>>> class SystemUser(CamelCase, Thingy):
... collection_name = "systemUsers"
>>> user = SystemUser.find_one()
>>> user.view()
{'_id': ObjectId(...), 'firstName': 'John', 'lastName': 'Doe'}
>>> user.first_name
'John'
>>> user.first_name = "Jonny"
>>> user.save()
SystemUser({'_id': ObjectId(...), firstName: 'Jonny', lastName: 'Doe'})
Tests
To run the tests suite:
- make sure you have a MongoDB database running on
localhost:27017
(you can spawn one withdocker-compose up -d
); - install developers requirements with
pip install -r requirements.txt
; - run
pytest
.
Sponsors
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file Mongo-Thingy-0.17.2.tar.gz
.
File metadata
- Download URL: Mongo-Thingy-0.17.2.tar.gz
- Upload date:
- Size: 9.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.10.13
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | f83cae3364b688da8985c1d178fb22a2bd930863b9163d08f9778e5ecf87335f |
|
MD5 | a9df53bbe59fd6dfec668329df7de2ec |
|
BLAKE2b-256 | 48654794eb1f8045c6d446d7ad3c1e4d2d56ba433cd560cf1f75697ff022e4ea |
File details
Details for the file Mongo_Thingy-0.17.2-py3-none-any.whl
.
File metadata
- Download URL: Mongo_Thingy-0.17.2-py3-none-any.whl
- Upload date:
- Size: 9.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.10.13
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | bf9546821125665bab2b3e14b79b3af9b5a4ed2cbdc34e1c2c6592d3a2c11d8f |
|
MD5 | ac1b90ddc5b09a644a47bd6c3d1e49b3 |
|
BLAKE2b-256 | 1ee5f7aa6e34468b9b9c15ca48d4f70e960837a1c378a779f02fc1ac617238d0 |