All Projects → art049 → Odmantic

art049 / Odmantic

Licence: isc
Async ODM (Object Document Mapper) for MongoDB based on python type hints

Programming Languages

python
139335 projects - #7 most used programming language

Projects that are alternatives of or similar to Odmantic

Mongoengine
MongoEngine is a Python Object-Document Mapper for working with MongoDB. Documentation is available at https://mongoengine-odm.readthedocs.io - there is currently a tutorial, a user guide, and an API reference.
Stars: ✭ 3,632 (+1413.33%)
Mutual labels:  orm, mongo, odm, mongodb
Laravel Mongodb
A MongoDB based Eloquent model and Query builder for Laravel (Moloquent)
Stars: ✭ 5,860 (+2341.67%)
Mutual labels:  orm, mongo, mongodb
Denodb
MySQL, SQLite, MariaDB, PostgreSQL and MongoDB ORM for Deno
Stars: ✭ 498 (+107.5%)
Mutual labels:  orm, mongo, mongodb
Mongo Thingy
🍃 The most idiomatic and friendly-yet-powerful way to use MongoDB with Python
Stars: ✭ 49 (-79.58%)
Mutual labels:  orm, odm, mongodb
Fooproxy
稳健高效的评分制-针对性- IP代理池 + API服务,可以自己插入采集器进行代理IP的爬取,针对你的爬虫的一个或多个目标网站分别生成有效的IP代理数据库,支持MongoDB 4.0 使用 Python3.7(Scored IP proxy pool ,customise proxy data crawler can be added anytime)
Stars: ✭ 195 (-18.75%)
Mutual labels:  async, asyncio, mongodb
Westore
更好的小程序项目架构
Stars: ✭ 3,897 (+1523.75%)
Mutual labels:  mongo, mongodb, nosql
Turbulette
😴 Turbulette - A batteries-included framework to build high performance, fully async GraphQL APIs
Stars: ✭ 29 (-87.92%)
Mutual labels:  async, asyncio, orm
Mondocks
An alternative way to interact with MongoDB databases from F# that allows you to use mongo-idiomatic constructs
Stars: ✭ 20 (-91.67%)
Mutual labels:  mongo, mongodb, nosql
Variety
A schema analyzer for MongoDB
Stars: ✭ 1,592 (+563.33%)
Mutual labels:  mongo, mongodb, nosql
Db
Data access layer for PostgreSQL, CockroachDB, MySQL, SQLite and MongoDB with ORM-like features.
Stars: ✭ 2,832 (+1080%)
Mutual labels:  orm, mongodb, nosql
Php Mongo
MongoDB ODM. Part of @PHPMongoKit
Stars: ✭ 228 (-5%)
Mutual labels:  mongo, odm, mongodb
Umongo
sync/async MongoDB ODM, yes.
Stars: ✭ 331 (+37.92%)
Mutual labels:  asyncio, odm, mongodb
Android Nosql
Lightweight, simple structured NoSQL database for Android
Stars: ✭ 284 (+18.33%)
Mutual labels:  mongo, mongodb, nosql
Mgm
Mongo Go Models (mgm) is a fast and simple MongoDB ODM for Go (based on official Mongo Go Driver)
Stars: ✭ 265 (+10.42%)
Mutual labels:  mongo, odm, mongodb
Wither
An ODM for MongoDB built on the official MongoDB Rust driver.
Stars: ✭ 174 (-27.5%)
Mutual labels:  orm, odm, mongodb
Orango
ArangoDB Object Modeling for Node.js, Foxx and Modern Web Browsers
Stars: ✭ 103 (-57.08%)
Mutual labels:  orm, odm, nosql
Tortoise Orm
Familiar asyncio ORM for python, built with relations in mind
Stars: ✭ 2,558 (+965.83%)
Mutual labels:  async, asyncio, orm
Qxorm
QxOrm library - C++ Qt ORM (Object Relational Mapping) and ODM (Object Document Mapper) library - Official repository
Stars: ✭ 176 (-26.67%)
Mutual labels:  orm, odm, mongodb
Gmqtt
Python MQTT v5.0 async client
Stars: ✭ 195 (-18.75%)
Mutual labels:  async, asyncio
Fastapi Gino Arq Uvicorn
High-performance Async REST API, in Python. FastAPI + GINO + Arq + Uvicorn (w/ Redis and PostgreSQL).
Stars: ✭ 204 (-15%)
Mutual labels:  async, asyncio

ODMantic

build coverage python-3.6-3.7-3.8 Package version Gitter

Documentation: https://art049.github.io/odmantic/


Asynchronous ODM(Object Document Mapper) for MongoDB based on standard python type hints. It's built on top of pydantic for model definition and validation.

Core features:

  • Simple: define your model by typing your fields using python types, build queries using python comparison operators

  • Developer experience: field/method autocompletion, type hints, data validation, perform database operations with a functional API

  • Fully typed: leverage static analysis to reduce runtime issues

  • AsyncIO: works well with ASGI frameworks (FastAPI, quart, sanic, Starlette, ...)

  • Serialization: built in JSON serialization and JSON schema generation

Requirements

Python: 3.6 and later (tested against 3.6, 3.7, 3.8 and 3.9)

MongoDB: 4.0 and later

Two direct dependencies:

  • pydantic: makes data validation and schema definition both handy and elegant.

  • motor: an asyncio MongoDB driver officially developed by the MongoDB team.

Installation

pip install odmantic

Example

To enjoy an async context without any code boilerplate, you can reproduce the following steps using the AsyncIO REPL (only for Python 3.8+).

python3.8 -m asyncio

If you are using an earlier version of Python you can use IPython which provide an Autoawait feature (starting from Python 3.6).

Define your first model

from typing import Optional

from odmantic import Field, Model


class Publisher(Model):
    name: str
    founded: int = Field(ge=1440)
    location: Optional[str] = None

By defining the Publisher class, we've just created an ODMantic model 🎉. In this example, the model will represent book publishers.

This model contains three fields:

  • name: This is the name of the Publisher. This is a simple string field without any specific validation but it will be required to build a new Publisher.

  • founded: This is the year of foundation of the Publisher. Since the printing press has been invented in 1440, it would be handy to allow only values above 1440. The ge keyword argument passed to the Field is exactly doing this. The model will require a founded value greater or equal than 1440.

  • location: This field will contain the country code of the Publisher. Defining this field as Optional with a None default value makes it a non required field that will be set automatically when not specified.

The collection name has been defined by ODMantic as well. In this case it will be publisher.

Create some instances

instances = [
    Publisher(name="HarperCollins", founded=1989, location="US"),
    Publisher(name="Hachette Livre", founded=1826, location="FR"),
    Publisher(name="Lulu", founded=2002)
]

We defined three instances of the Publisher model. They all have a name property as it was required. All the foundations years are later than 1440. The last publisher has no location specified so by default this field is set to None (it will be stored as null in the database).

For now, those instances only exists locally. We will persist them in a database in the next step.

Populate the database with your instances

For the next steps, you'll need to start a local MongoDB server.The easiest way is to use docker. Simply run the next command in a terminal (closing the terminal will terminate the MongoDB instance and remove the container).

docker run --rm --net=host mongo

First, let's connect to the database using the engine. In ODMantic, every database operation is performed using the engine object.

from odmantic import AIOEngine

engine = AIOEngine()

By default, the AIOEngine (stands for AsyncIOEngine) automatically tries to connect to a MongoDB instance running locally (on port 27017). Since we didn't provide any database name, it will use the database named test by default.

The next step is to persist the instances we created before. We can perform this operation using the AIOEngine.save_all method.

await engine.save_all(instances)

Most of the engine I/O methods are asynchronous, hence the await keyword used here. Once the operation is complete, we should be able to see our created documents in the database. You can use Compass or RoboMongo if you'd like to have a graphical interface.

Another possibility is to use mongo CLI directly:

mongo --eval "db.publisher.find({})"

Output:

connecting to: mongodb://127.0.0.1:27017
{
  "_id": ObjectId("5f67b331514d6855bc5c54c9"),
  "founded": 1989,
  "location": "US",
  "name": "HarperCollins"
},
{
  "_id": ObjectId("5f67b331514d6855bc5c54ca"),
  "founded":1826,
  "location": "FR",
  "name": "Hachette Livre"
},
{
  "_id": ObjectId("5f67b331514d6855bc5c54cb"),
  "founded": 2002,
  "location": null,
  "name": "Lulu"
}

The created instances are stored in the test database under the publisher collection.

We can see that an _id field has been added to each document. MongoDB need this field to act as a primary key. Actually, this field is added by ODMantic and you can access it under the name id.

print(instances[0].id)
#> ObjectId("5f67b331514d6855bc5c54c9")

Find instances matching a criteria

Since we now have some documents in the database, we can start building some queries.

First, let's find publishers created before the 2000s:

early_publishers = await engine.find(Publisher, Publisher.founded <= 2000)
print(early_publishers)
#> [Publisher(name="HarperCollins", founded=1989, location="US),
#>  Publisher(name="Hachette Livre", founded=1826, location="FR")]

Here, we called the engine.find method. The first argument we need to specify is the Model class we want to query on (in our case Publisher). The second argument is the actual query. Similarly to SQLAlchemy, you can build ODMantic queries using the regular python operators.

When awaited, the engine.find method will return the list of matching instances stored in the database.

Another possibility is to query for at most one instance. For example, if we want to retrieve a publisher from Canada (CA):

ca_publisher = await engine.find_one(Publisher, Publisher.location == "CA")
print(ca_publisher)
#> None

Here the result is None because no matching instances have been found in the database. The engine.find_one method returns an instance if one exists in the database otherwise, it will return None.

Modify an instance

Finally, let's edit some instances. For example, we can set the location for the publisher named Lulu. First, we need to gather the instance from the database:

lulu = await engine.find_one(Publisher, Publisher.name == "Lulu")
print(lulu)
#> Publisher(name="Lulu", founded=2002, location=None)

We still have the same instance, with no location set. We can change this field:

lulu.location = "US"
print(lulu)
#> Publisher(name="Lulu", founded=2002, location="US)

The location has been changed locally but the last step to persist this change is to save the document:

await engine.save(lulu)

We can now check the database state:

mongo --eval "db.publisher.find({name: 'Lulu'})"

Output:

connecting to: mongodb://127.0.0.1:27017
{
  "_id": ObjectId("5f67b331514d6855bc5c54cb"),
  "founded": 2002,
  "location": "US",
  "name": "Lulu"
}

The document have been successfully updated !

Now, what if we would like to change the foundation date with an invalid one (before 1440) ?

lulu.founded = 1000
#> ValidationError: 1 validation error for Publisher
#> founded
#>   ensure this value is greater than 1440
#>   (type=value_error.number.not_gt; limit_value=1440)

This will raise an exception as it's not matching the model definition. The raised exception is actually a ValidationError created by from pydantic.

Next steps

If you already have experience with Pydantic and FastAPI, the Usage with FastAPI example might be interesting for you.

Otherwise, to get started on more advanced practices like relations and building more advanced queries, you can directly check the other sections of the documentation.

If you wish to contribute to the project (Thank you! 😃), you can have a look to the Contributing section of the documentation.

License

This project is licensed under the terms of the ISC license.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].