Beanie - is an Asynchronous Python object-document mapper (ODM) for MongoDB

Overview

Beanie

Beanie - is an Asynchronous Python object-document mapper (ODM) for MongoDB, based on Motor and Pydantic.

When using Beanie each database collection has a corresponding Document that is used to interact with that collection. In addition to retrieving data, Beanie allows you to add, update, or delete documents from the collection as well.

Beanie saves you time by removing boiler-plate code and it helps you focus on the parts of your app that actually matter.

Data and schema migrations are supported by Beanie out of the box.

Installation

PIP

pip install beanie

Poetry

poetry add beanie

Quick Start

from typing import Optional, List

import motor
from beanie import Document, init_beanie
from pydantic import BaseModel


class Tag(BaseModel):
    name: str
    color: str


class Note(Document):
    title: str
    text: Optional[str]
    tag_list: List[Tag] = []


async def main():
    # Crete Motor client
    client = motor.motor_asyncio.AsyncIOMotorClient(
        "mongodb://user:[email protected]:27017"
    )
    
    # Init beanie with the Note document class
    await init_beanie(database=client.db_name, document_models=[Note])

    # Get all the notes
    all_notes = await Note.find_all().to_list()

Documentation

ODM

  • Tutorial - ODM usage examples
  • API - Full list of the ODM classes and methods with descriptions

Migrations

Example Projects

  • FastAPI Demo - Beanie and FastAPI collaboration demonstration. CRUD and Aggregation.
  • Indexes Demo - Regular and Geo Indexes usage example wrapped to a microservice.

Articles

Resources

  • GitHub - GitHub page of the project
  • Changelog - list of all the valuable changes
  • Discord - ask your questions, share ideas or just say Hello!!

Supported by JetBrains

JetBrains

Issues
  • start supporting type annotation for queries results

    start supporting type annotation for queries results

    Make Query accept a Generic, in order to always know the type of document which they are operating.

    opened by KiraPC 10
  • `Set` not supported?

    `Set` not supported?

    Is there any way to use Set as datatype in models? image

    opened by Vovcharaa 7
  • Added bsonable encoder

    Added bsonable encoder

    Added support for all types that could be encoded in bson compatible types + added bson_encoders field to Config class for custom encoders.

    opened by Vovcharaa 6
  • Remove lock file

    Remove lock file

    Removes the poetry.lock file. Due to this mypy can be updated, which caused errors that should be fixed. I updated mypy in pre-commit as well, and moved the config to pyproject.toml.

    opened by jorants 6
  • documentation is down

    documentation is down

    https://roman-right.github.io/beanie/document/ is loading but not loading properly and most of the links are 404ing, e.g.:

    https://roman-right.github.io/api/document/

    opened by dconathan 6
  • Cleanup root dir

    Cleanup root dir

    Removes some files from the root of the repo:

    • Code of conduct is added to documentation
    • Old code of conduct and contribution guidelines are removed.
    • pytest was upgraded to version 6 and now excepts config in pyproject.toml, which was changed to match the script in scripts
    • Removed script files. Most where one simple line anyway, only the tests required complicated arguments which are now in the config.
    opened by jorants 6
  • Not possible to use custom type for _id field

    Not possible to use custom type for _id field

    Something like this does't work:

    class Task(Document):
        id: str = Field(..., alias="_id")
        updated: datetime = Field(..., default_factory=datetime.utcnow)
    

    It would be cool to be able to set _id manually.

    opened by paul-nameless 6
  • Projections

    Projections

    Added a function to build the projections, this function will be called once when we first ask for it. The overhead is low, but still makes sense to cache this. This is cached as an "internal" attribute named _projection. We get the attribute by calling _get_projection() this returns the projection if it exists or builds it and returns it. We use pydantic's __fields__ value to find all of the items in the model we care for.

    I added a quick test as well for clarity, it is likely not needed as this line is called during the .findx() tests but thought it was worthwhile for documenting the expected return data.

    I also changed the filter args on the findx calls to kwargs again for clarity as to what we are calling into on each call.

    opened by nzsmith1 5
  • migrations/test_free_fall.py fails

    migrations/test_free_fall.py fails

    Don't know if it is my setup but migrations/test_free_fall.py fails on my machine. Running mongodb v4.4.4 on arch linux, python 3.9.5.

    opened by jorants 5
  • [Question] What's the best way to dynamically call Motor.io functions?

    [Question] What's the best way to dynamically call Motor.io functions?

    First and foremost thank you for creating such a useful tool! It's exactly what I've been looking for.

    Now for context I was looking to use a motor method count_documents() that was not exposed at the Document level. I noticed in the documentation that it's suggested to use Document.get_motor_collection().count_documents(). This indeed worked but its not very clean imo. I could also explicitly redefine in my User class any methods I wanted use from motor that weren't defined in Document. But this too felt like extra code I shouldn't have to write. So instead I created this metaclass to dynamically call them.

    What are your thoughts about this? Is there a better way? Are there any gotchas I should be concerned with? If you believe this change would actually be helpful to others would you like me to submit a PR?

    from beanie import Document
    from motor.motor_asyncio import AsyncIOMotorCollection # Note: I had to use AsyncIOMotorCollection because I ran into recursive issues due to other areas in the codebase where you call "getattr".
    from pydantic.main import ModelMetaclass # Note: Pydantic's BaseModel has a custom metaclass; therefore to use a metaclass of our own we have to extend theirs.
    
    class DocumentMetaClass(ModelMetaclass):
        def __getattr__(cls, attr):
            if callable(getattr(AsyncIOMotorCollection, attr, None)): # Note: I only want to execute this code if the attr that's missing exists in motor.
                motor_collection = cls.get_motor_collection()
                motor_func = classmethod(getattr(motor_collection, attr))
                setattr(cls, attr, motor_func)
                return getattr(cls, attr)
    
    class User(Document, metaclass=DocumentMetaClass): # Note: This assignment of metaclass could be done at the Document level if it's helpful to others.
        username: str
        email: str
    
    # skipping async code...etc
    
    await User.count_documents({ 'username': user.username }, limit = 1)
    
    opened by RodneyU215 5
  • Pylint false positive for async loop on FindMany

    Pylint false positive for async loop on FindMany

    class Product(Document):
        price: float
    
    async def main():
        ...
        async for result in Product.find():
            ...
    

    When iterating on the results of a find operation, the code works but Pylint (2.11.1) complains with the following error:

    main.py:9:24: E1133: Non-iterable value Product.find() is used in an iterating context (not-an-iterable)
    
    opened by didmar 0
  • Distinct method?

    Distinct method?

    Hello,

    What a wonderfull work you've done! I'm starting using Beanie with fastapi and I couldn't find anything in the docs or in the code that allow me to use the db.collection.disctinct from mongodb docs.

    I'm using the following document:

    class Customer(BaseModel):
        name: str
        email: Optional[str]
        # some fields
    
    class Invoice(Document)
    
       customer: Customer
        # some fields
    

    Coming from pymongo, I use collection.distinct('customer') to get the list of distinct customers from the collection 'invoices', is there a way to do this (or something equivalent using aggregate and project) with Beanie?

    opened by tgrandjean 0
  • [improvement] deleting a document and getting the operation's result

    [improvement] deleting a document and getting the operation's result

    Currently, there is no syntactic sugar for deleting a document and retrieving the deletion result, to know whether the doc existed or not.

    result = MyDocument.find_one({...}).delete()
    

    This does not work, as result is always None

    The only way to do this for now is to use the Motor API:

    deletion: DeleteResult = await MyDocument.get_motor_collection().delete_one({...})
    # deletion.deleted_count can be used to know whether a document was deleted or not
    
    opened by didmar 0
  • Fix broken URLs

    Fix broken URLs

    There are two more broken URLs I can not find their suitable replacements.

    • Sorting can be done with the sort method.
    • A full list of avalible methods can be found here.
    opened by grimmer0125 0
  • alias _id Causing Casting issues with fastapi

    alias _id Causing Casting issues with fastapi

    class OutTestModel(BaseModel):
        id: Optional[PydanticObjectId]
        name:str
    
    
    class TestModel(Outtestmodel,Document):
        id:Optional[PydanticObjectId]
        secret_message:str
    
    @Router.get("/failtest",response_model=OutTestModel)
    async def wontwork():
      mymodel = TestModel(name="jeff",secret_message="this is gonna break")
      await mymodel.save()
      return mymodel
    

    The above code OutTestModel will not get the id value from TestModel.

    I believe the cause is https://github.com/roman-right/beanie/blob/1641dd81be64dd1dc11af667deb2e50feb2de2be/beanie/odm/documents.py#L922-L927

    How hard would it be to optionally just use _id for to and from mongo parts? The only other work around is to not us alias for the response model which opens a whole other can of worms.

    opened by zrothberg 2
  • Nested object field do not use Field.alias value in ExpressionField

    Nested object field do not use Field.alias value in ExpressionField

    When executing an expression of a nested object field of a Document class, the value of the aliases is not used to create the expression string for the mongo query.

    E.g.

    class HeaderObject(BaseModel):
        header_id: str = Field(alias="headerId")
    
    
    class Header(Document):
        class Collection:
            name = "test-header"
    
        header: HeaderObject
    
    
    print(Header.header.header_id == 1) # actual => {'header.header_id': 1}; expected => {'header.headerId': 1}
    

    So the solution is that beanie during the class init, check the type of the field, if it is object, so go inside it to get the alias.

    opened by KiraPC 6
  • Read and Write concerns

    Read and Write concerns

    Any plans to implement https://pymongo.readthedocs.io/en/3.12.0/api/pymongo/write_concern.html

    Seems fairly striaght forward to do though unsure how you would want to handle the API for it. Happy to mock something up.

    opened by zrothberg 1
  • Fields not showing in pycharm

    Fields not showing in pycharm

    So it works great. Seriously love this lib. Is there anyway to get the fields to be visible when typing in pycharm? Does it work in any other ides? Everything looks fine when I pop it under the debugger.

    opened by zrothberg 3
  • Init TimeSeries Collections

    Init TimeSeries Collections

    Hello,

    How to create TimeSeries Collection ? https://docs.mongodb.com/manual/core/timeseries-collections/#create-a-time-series-collection

    The mongo query as exemple:

    db.createCollection(
      "weather24h",
      {
        timeseries: {
          timeField: "timestamp",
          metaField: "metadata",
          granularity: "hours"
        },
        expireAfterSeconds: 86400
      }
    )
    
    opened by acrozes 1
Releases(1.6.1)
Owner
Roman
This is my public github profile
Roman
Pony Object Relational Mapper

Downloads Pony Object-Relational Mapper Pony is an advanced object-relational mapper. The most interesting feature of Pony is its ability to write que

null 2.7k Oct 25, 2021
The ormar package is an async mini ORM for Python, with support for Postgres, MySQL, and SQLite.

python async mini orm with fastapi in mind and pydantic validation

null 674 Oct 23, 2021
Tortoise ORM is an easy-to-use asyncio ORM inspired by Django.

Tortoise ORM was build with relations in mind and admiration for the excellent and popular Django ORM. It's engraved in it's design that you are working not with just tables, you work with relational data.

Tortoise 2.4k Oct 20, 2021
A curated list of awesome tools for SQLAlchemy

Awesome SQLAlchemy A curated list of awesome extra libraries and resources for SQLAlchemy. Inspired by awesome-python. (See also other awesome lists!)

Hong Minhee (洪 民憙) 2.3k Oct 21, 2021
A Python Library for Simple Models and Containers Persisted in Redis

Redisco Python Containers and Simple Models for Redis Description Redisco allows you to store objects in Redis. It is inspired by the Ruby library Ohm

sebastien requiem 434 Oct 1, 2021
A pure Python Database Abstraction Layer

pyDAL pyDAL is a pure Python Database Abstraction Layer. It dynamically generates the SQL/noSQL in realtime using the specified dialect for the databa

null 397 Oct 23, 2021
Rich Python data types for Redis

Created by Stephen McDonald Introduction HOT Redis is a wrapper library for the redis-py client. Rather than calling the Redis commands directly from

Stephen McDonald 273 Sep 2, 2021
Piccolo - A fast, user friendly ORM and query builder which supports asyncio.

A fast, user friendly ORM and query builder which supports asyncio.

null 566 Oct 20, 2021
The Orator ORM provides a simple yet beautiful ActiveRecord implementation.

Orator The Orator ORM provides a simple yet beautiful ActiveRecord implementation. It is inspired by the database part of the Laravel framework, but l

Sébastien Eustace 1.3k Oct 17, 2021
Pydantic model support for Django ORM

Pydantic model support for Django ORM

Jordan Eremieff 189 Oct 23, 2021
SQLModel is a library for interacting with SQL databases from Python code, with Python objects.

SQLModel is a library for interacting with SQL databases from Python code, with Python objects. It is designed to be intuitive, easy to use, highly compatible, and robust.

Sebastián Ramírez 5.5k Oct 24, 2021
A pythonic interface to Amazon's DynamoDB

PynamoDB A Pythonic interface for Amazon's DynamoDB. DynamoDB is a great NoSQL service provided by Amazon, but the API is verbose. PynamoDB presents y

null 1.7k Oct 23, 2021
a small, expressive orm -- supports postgresql, mysql and sqlite

peewee Peewee is a simple and small ORM. It has few (but expressive) concepts, making it easy to learn and intuitive to use. a small, expressive ORM p

Charles Leifer 8.7k Oct 25, 2021
An async ORM. 🗃

ORM The orm package is an async ORM for Python, with support for Postgres, MySQL, and SQLite. ORM is built with: SQLAlchemy core for query building. d

Encode 1.4k Oct 11, 2021
MongoEngine flask extension with WTF model forms support

Flask-MongoEngine Info: MongoEngine for Flask web applications. Repository: https://github.com/MongoEngine/flask-mongoengine About Flask-MongoEngine i

MongoEngine 788 Oct 14, 2021
Adds SQLAlchemy support to Flask

Flask-SQLAlchemy Flask-SQLAlchemy is an extension for Flask that adds support for SQLAlchemy to your application. It aims to simplify using SQLAlchemy

The Pallets Projects 3.6k Oct 22, 2021
Easy-to-use data handling for SQL data stores with support for implicit table creation, bulk loading, and transactions.

dataset: databases for lazy people In short, dataset makes reading and writing data in databases as simple as reading and writing JSON files. Read the

Friedrich Lindenberg 4.1k Oct 18, 2021
A very simple CRUD class for SQLModel! ✨

Base SQLModel A very simple CRUD class for SQLModel! ✨ Inspired on: Full Stack FastAPI and PostgreSQL - Base Project Generator FastAPI Microservices I

Marcelo Trylesinski 12 Oct 22, 2021