A Pythonic, object-oriented interface for working with MongoDB.

Overview

PyMODM

MongoDB has paused the development of PyMODM. If there are any users who want to take over and maintain this project, or if you just have questions, please respond to this forum post.

Documentation View build status

A generic ODM around PyMongo, the MongoDB Python driver. PyMODM works on Python 2.7 as well as Python 3.3 and up. To learn more, you can browse the official documentation or take a look at some examples.

Why PyMODM?

PyMODM is a "core" ODM, meaning that it provides simple, extensible functionality that can be leveraged by other libraries to target platforms like Django. At the same time, PyMODM is powerful enough to be used for developing applications on its own. Because MongoDB engineers are involved in developing and maintaining the project, PyMODM will also be quick to adopt new MongoDB features.

Support / Feedback

For issues with, questions about, or feedback for PyMODM, please look into our support channels. Please do not email any of the PyMODM developers directly with issues or questions - you're more likely to get an answer on the MongoDB Community Forums.

Bugs / Feature Requests

Think you’ve found a bug? Want to see a new feature in PyMODM? Please open a case in our issue management tool, JIRA:

Bug reports in JIRA for all driver projects (e.g. PYMODM, PYTHON, JAVA) and the Core Server (i.e. SERVER) project are public.

How To Ask For Help

Please include all of the following information when opening an issue:

  • Detailed steps to reproduce the problem, including full traceback, if possible.

  • The exact python version used, with patch level:

    $ python -c "import sys; print(sys.version)"
    
  • The exact version of PyMODM used, with patch level:

    $ python -c "import pymodm; print(pymodm.version)"
    
  • The PyMongo version used, with patch level:

    $ python -c "import pymongo; print(pymongo.version)"
    
  • The operating system and version (e.g. Windows 7, OSX 10.8, ...)

  • Web framework or asynchronous network library used, if any, with version (e.g. Django 1.7, mod_wsgi 4.3.0, gevent 1.0.1, Tornado 4.0.2, ...)

Security Vulnerabilities

If you’ve identified a security vulnerability in a driver or any other MongoDB project, please report it according to the instructions here.

Example

Here's a basic example of how to define some models and connect them to MongoDB:

from pymongo import TEXT
from pymongo.operations import IndexModel
from pymodm import connect, fields, MongoModel, EmbeddedMongoModel


# Connect to MongoDB first. PyMODM supports all URI options supported by
# PyMongo. Make sure also to specify a database in the connection string:
connect('mongodb://localhost:27017/myApp')


# Now let's define some Models.
class User(MongoModel):
    # Use 'email' as the '_id' field in MongoDB.
    email = fields.EmailField(primary_key=True)
    fname = fields.CharField()
    lname = fields.CharField()


class BlogPost(MongoModel):
    # This field references the User model above.
    # It's stored as a bson.objectid.ObjectId in MongoDB.
    author = fields.ReferenceField(User)
    title = fields.CharField(max_length=100)
    content = fields.CharField()
    tags = fields.ListField(fields.CharField(max_length=20))
    # These Comment objects will be stored inside each Post document in the
    # database.
    comments = fields.EmbeddedModelListField('Comment')

    class Meta:
        # Text index on content can be used for text search.
        indexes = [IndexModel([('content', TEXT)])]

# This is an "embedded" model and will be stored as a sub-document.
class Comment(EmbeddedMongoModel):
    author = fields.ReferenceField(User)
    body = fields.CharField()
    vote_score = fields.IntegerField(min_value=0)


# Start the blog.
# We need to save these objects before referencing them later.
han_solo = User('[email protected]', 'Han', 'Solo').save()
chewbacca = User(
    '[email protected]', 'Chewbacca', 'Thomas').save()


post = BlogPost(
    # Since this is a ReferenceField, we had to save han_solo first.
    author=han_solo,
    title="Five Crazy Health Foods Jabba Eats.",
    content="...",
    tags=['alien health', 'slideshow', 'jabba', 'huts'],
    comments=[
        Comment(author=chewbacca, body='Rrrrrrrrrrrrrrrr!', vote_score=42)
    ]
).save()


# Find objects using familiar MongoDB-style syntax.
slideshows = BlogPost.objects.raw({'tags': 'slideshow'})

# Only retrieve the 'title' field.
slideshow_titles = slideshows.only('title')

# u'Five Crazy Health Foods Jabba Eats.'
print(slideshow_titles.first().title)
Comments
  • Fix dereferencing embedded documents refs and dereferencing tests.

    Fix dereferencing embedded documents refs and dereferencing tests.

    What does these changes do:

    1. Fix bug with dereference function: it does not dereference refs in embedded documents from list ListField(EmbeddedDoc(ReferenceField(X))). It performs an additional call to db using dereference_id function while accessing the field.
      • Add test to reproduce.
      • Add fix.
    2. Fix false positive test test.test_dereference.DereferenceTestCase.test_reference_not_found. It saves an empty Post model which has title as a pk. That forces to store post document with _id as an ObjectId. So post.delete() does not actually delete any documents from database.
    opened by ilex 6
  • PYMODM-95 Validating EmbeddedMongoModel in EmbeddedDocument type fields

    PYMODM-95 Validating EmbeddedMongoModel in EmbeddedDocument type fields

    Checked and validated type of related_field in EmbeddedDocumentField and EmbeddedDocumentListField. Raised Value error the same was as RelatedModelFieldsBase.

    opened by shreybatra 5
  • PYMODM-58: to_python() gets called multiple times on already-converted values

    PYMODM-58: to_python() gets called multiple times on already-converted values

    This PR introduces a new class pymodm.base.models.LazyDecoder to help with bookkeeping associated with tracking the encoding of MongoModel objects being retrieved from and written to the database.

    opened by prashantmital 5
  • PyMODM-48 - Allow unknown fields when parsing documents

    PyMODM-48 - Allow unknown fields when parsing documents

    https://jira.mongodb.org/browse/PYMODM-48

    Disallowing unknown fields seemed like the right choice at first, but now there are complaints about this decision (including http://stackoverflow.com/q/41472478/3271558). In the future, we could make validating unexpected fields optional, by creating a new option on the class Meta. I'm going to hold of on doing so until there's a clear need for that, though.

    opened by llvtt 5
  • Call .get_default() only once (fixes: PyMODM-65)

    Call .get_default() only once (fixes: PyMODM-65)

    At first I tried caching the default value in inst._data, but that broke a bunch of tests, because reading a default value of a field is not supposed to result in saving that default value to MongoDB.

    So instead I added inst._defaults. This doesn't get saved to MongoDB, but __get__ reads from it instead of calling .get_default() again.

    opened by dpercy 4
  • Fix dereference

    Fix dereference

    What do these changes do:

    • Remove ReferenceField._is_instance property as it is useless (field is shared between model instances).
    • Rewrite dereferencing to fix some issues:
      • Dereference unhashable ids (auto dereferencing still does not work for this case as reference id is treated as dereferenced document not as an id).
      • Set reference field value to None if there is no document with such id (as auto dereferencing does).
      • Fix bug when some fields reference to different collections but have the same id. So that only one of that field will be set with correct value as the document_map was {id --> document}. Example:
    class User(MongoModel):
        name = fields.CharField(primary_key=True)
    class Category(MongoModel):
        title = fields.CharField(primary_key=True)
    class Post(MongoModel):
        user = fields.ReferenceField(User)
        category = fields.ReferenceField(Category)
    user = User(name='Bob').save()
    category = Category(title='Bob').save()
    post = Post(user=user, category=category)
    with no_auto_dereference(post):
        post.refresh_from_db()
        dereference(post)
        # one of the follow statements will fail as both post.user and post.category
        # will be User (or Category).
        assert isinstance(post.user, User)
        assert isinstance(post.category, Category)
    
    • Dereference DBRefs in dict-like fields. As there is no field for DBRef the only way to store it is within dict-like fields (DictField, OrderedDictField). So dereference traverses such containers to find DBRef values and try to dererference them.
    opened by ilex 3
  • Add auto_create_indexes Meta option and QuerySet.create_indexes method.

    Add auto_create_indexes Meta option and QuerySet.create_indexes method.

    What do these changes do:

    • Add auto_create_indexes option to control whether model's indexes should be created when model class definition is evaluated or not. The default value is True so that default behavior would be fully compatible with current one. With this option it is possible to control indexes creation and makes models classes definition independent from connect function call before any model class (mainly with indexes) is evaluated.
    • Add QuerySet.create_indexes method to explicitly create indexes which are defined in model's Meta. As a bonus this method can be used with context_managers.
    • Add tests to test indexes creation.
    opened by ilex 3
  • Fix ReferenceField gets dereferenced multiple times if no_auto_dereference used

    Fix ReferenceField gets dereferenced multiple times if no_auto_dereference used

    Sample code

    import uuid
    from pymodm import connect, MongoModel, fields
    
    
    class User(MongoModel):
        _id = fields.UUIDField(primary_key=True)
        name = fields.CharField()
        job_title = fields.CharField()
    
    
    class Asset(MongoModel):
        _id = fields.UUIDField(primary_key=True)
        user = fields.ReferenceField(User)
        comments = fields.CharField()
    
    
    if __name__ == "__main__":
        connect('mongodb://*:*@<host>/test?authSource=admin')
        user = User(
            _id=uuid.uuid4(),
            name='Jack',
            job_title='Software Engineer',
        )
        user.save()
        asset_id = uuid.uuid4()
        Asset(
            _id=asset_id,
            user=user,
            comments='Old comments',
        ).save()
        asset = Asset.objects.get({'_id': asset_id})
        asset.comments = 'New comments'
        asset.save()
        # The ReferenceField will be dereferenced each time asset.user is used
        print(asset.user.name)
        print(asset.user.job_title)
    
    

    Analysis

    TopLevelMongoModel.save() calls MongoModelBase.full_clean() and MongoModelBase.to_son(). Both of them use no_auto_dereference context manager and get the values of the fields.

    ReferenceField.__get__() calls ReferenceField.to_python()

        def __get__(self, inst, owner):
            value = super(ReferenceField, self).__get__(inst, owner)
            MongoModelBase = _import('pymodm.base.models.MongoModelBase')
            if inst is not None and isinstance(inst, MongoModelBase):
                return self.to_python(value)
            return self
    

    ReferenceField.to_python() calls ReferenceField.dereference_if_needed()

        def to_python(self, value):
            # Default/blank values can be returned immediately.
            if self.is_blank(value):
                return value
    
            # Attempt casting to referenced model.
            if isinstance(value, dict):
                try:
                    return self.related_model.from_document(value)
                except (ValueError, TypeError):
                    pass
    
            return self.dereference_if_needed(value)
    

    ReferenceField.dereference_if_needed() returns self.related_model._mongometa.pk.to_python(value) since no_auto_dereference context manager is used. The value of key 'user' in asset's _python_data is set to user's _id which is an uuid. So when asset.user is used later, because of there already is a key 'user' in asset's _python_data, super(ReferenceField, self).__get__(inst, owner) in ReferenceField.__get__() will return user's _id. asset.user will be dereferenced again and again.

        def dereference_if_needed(self, value):
            # Already dereferenced values can be returned immediately.
            if isinstance(value, self.related_model):
                return value
    
            # Attempt to dereference the value as an id.
            if self.model._mongometa._auto_dereference:
                dereference_id = _import('pymodm.dereference.dereference_id')
                return dereference_id(self.related_model, value)
    
            return self.related_model._mongometa.pk.to_python(value)
    

    This issue will result in a critical performance issue if getting the value of a ReferenceField after saving the model or using any other methods which get the ReferenceField's value within no_auto_dereference context manager.

    opened by spengjie 2
  • distinct and drop methods added

    distinct and drop methods added

    As per my understanding of your code, I have added the distinct method to return the distinct values for particular field. I have added the distinct method in queryset.py file

    opened by btob33 2
  • PYMODM-68 defer index creation

    PYMODM-68 defer index creation

    Here's one solution to PYMODM-68: instead of creating the indexes when the class is created, we could create them when the model's collection is first used. I think this is similar to what MongoEngine does.

    Another option would be to give the user the option to disable automatic index creation, like MongoEngine's auto_create_index=False.

    opened by dpercy 2
  • Fix: Aggregation add to queryset with sort

    Fix: Aggregation add to queryset with sort

    Fix for pymongo.errors.OperationFailure: the $sort key specification must be an object. This occurs when a queryset has been given an order_by then an aggregation is added. self._order_by is a list of tuples, but must be an object. The order of keys in order_by matters, so we must use SON.

    opened by achawkins 1
  • Fixes PYMODM-119

    Fixes PYMODM-119

    I created PYMODM-119, expecting to_son to automatically dereference fields.

    @ShaneHarvey explained why that cannot be, but agreed that this can be clarified in the docs.

    Here is a one line addition to the docstring of to_son

    It does not dereference fields.
    
    opened by krishraghuram 0
Owner
mongodb
mongodb
MongoDB data stream pipeline tools by YouGov (adopted from MongoDB)

mongo-connector The mongo-connector project originated as a MongoDB mongo-labs project and is now community-maintained under the custody of YouGov, Pl

YouGov 1.9k Jan 4, 2023
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 2.1k Dec 30, 2022
Async ODM (Object Document Mapper) for MongoDB based on python type hints

ODMantic Documentation: https://art049.github.io/odmantic/ Asynchronous ODM(Object Document Mapper) for MongoDB based on standard python type hints. I

Arthur Pastel 732 Dec 31, 2022
PyMongo - the Python driver for MongoDB

PyMongo Info: See the mongo site for more information. See GitHub for the latest source. Documentation: Available at pymongo.readthedocs.io Author: Mi

mongodb 3.7k Jan 8, 2023
Motor - the async Python driver for MongoDB and Tornado or asyncio

Motor Info: Motor is a full-featured, non-blocking MongoDB driver for Python Tornado and asyncio applications. Documentation: Available at motor.readt

mongodb 2.1k Dec 26, 2022
Motor - the async Python driver for MongoDB and Tornado or asyncio

Motor Info: Motor is a full-featured, non-blocking MongoDB driver for Python Tornado and asyncio applications. Documentation: Available at motor.readt

mongodb 1.6k Feb 6, 2021
sync/async MongoDB ODM, yes.

μMongo: sync/async ODM μMongo is a Python MongoDB ODM. It inception comes from two needs: the lack of async ODM and the difficulty to do document (un)

Scille 428 Dec 29, 2022
Micro ODM for MongoDB

Beanie - is an asynchronous ODM for MongoDB, based on Motor and Pydantic. It uses an abstraction over Pydantic models and Motor collections to work wi

Roman 993 Jan 3, 2023
A simple wrapper to make a flat file drop in raplacement for mongodb out of TinyDB

Purpose A simple wrapper to make a drop in replacement for mongodb out of tinydb. This module is an attempt to add an interface familiar to those curr

null 180 Jan 1, 2023
Monty, Mongo tinified. MongoDB implemented in Python !

Monty, Mongo tinified. MongoDB implemented in Python ! Inspired by TinyDB and it's extension TinyMongo. MontyDB is: A tiny version of MongoDB, against

David Lai 522 Jan 1, 2023
A simple password manager I typed with python using MongoDB .

Python with MongoDB A simple python code example using MongoDB. How do i run this code • First of all you need to have a python on your computer. If y

null 31 Dec 6, 2022
Query multiple mongoDB database collections easily

leakscoop Perform queries across multiple MongoDB databases and collections, where the field names and the field content structure in each database ma

bagel 5 Jun 24, 2021
MongoX is an async python ODM for MongoDB which is built on top Motor and Pydantic.

MongoX MongoX is an async python ODM (Object Document Mapper) for MongoDB which is built on top Motor and Pydantic. The main features include: Fully t

Amin Alaee 112 Dec 4, 2022
Implementing basic MongoDB CRUD (Create, Read, Update, Delete) queries, using Python.

MongoDB with Python Implementing basic MongoDB CRUD (Create, Read, Update, Delete) queries, using Python. We can connect to a MongoDB database hosted

MousamSingh 4 Dec 1, 2021
A CRUD and REST api with mongodb atlas.

Movies_api A CRUD and REST api with mongodb atlas. Setup First import all the python dependencies in your virtual environment or globally by the follo

Pratyush Kongalla 0 Nov 9, 2022
A library for python made by me,to make the use of MySQL easier and more pythonic

my_ezql A library for python made by me,to make the use of MySQL easier and more pythonic This library was made by Tony Hasson , a 25 year old student

null 3 Nov 19, 2021
Asynchronous, fast, pythonic DynamoDB Client

AsyncIO DynamoDB Asynchronous pythonic DynamoDB client; 2x faster than aiobotocore/boto3/botocore. Quick start With httpx Install this library pip ins

HENNGE 48 Dec 18, 2022
Py2neo is a comprehensive toolkit for working with Neo4j from within Python applications or from the command line.

Py2neo Py2neo is a client library and toolkit for working with Neo4j from within Python applications and from the command line. The library supports b

Nigel Small 1.2k Jan 2, 2023
Py2neo is a client library and toolkit for working with Neo4j from within Python

Py2neo Py2neo is a client library and toolkit for working with Neo4j from within Python applications. The library supports both Bolt and HTTP and prov

py2neo.org 1.2k Jan 2, 2023