GithubHelp home page GithubHelp logo

refty / mongo-thingy Goto Github PK

View Code? Open in Web Editor NEW
68.0 8.0 12.0 160 KB

:leaves: Powerful schema-less ODM for MongoDB and Python (sync + async)

Home Page: https://mongo-thingy.readthedocs.io

License: MIT License

Python 100.00%
python python-library mongodb mongodb-orm mongodb-odm orm odm pymongo python3

mongo-thingy's Introduction

Mongo-Thingy

PyPI Supported Python Versions License Code style
Tests Tests Docs

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:

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 with docker-compose up -d);
  • install developers requirements with pip install -r requirements.txt;
  • run pytest.

Sponsors

    Numberly         Refty    

mongo-thingy's People

Contributors

dependabot[bot] avatar flowtter avatar ramnes avatar shir0kamii avatar trubesv avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mongo-thingy's Issues

Remove six

We don't support Python 2 anymore, so let's stop depending on six.

Implement Cursor.delete()

So that we can remove a bunch of documents that exist from a cursor.

This is useful when we want to reuse a cursor rather than writing a new remove query.

For example:

class Foo(Thingy):
    def get_bars(self):
        return Bar.find({"foo_id": self.id})

    def delete_bars(self):
        return self.get_bars().delete()

Support other back-ends than PyMongo

I was randomly browsing other similar tools, and ended up reading this issue: slacy/minimongo#40

That'd be great to:

  • make sure Mongo-Thingy can run on top of a mongomock instance, and modify the code if needed
  • write tests to ensure this
  • state it in the README

Connect mongo-thingy to montyDB

Hello,
I've been trying to connect mongo-thingy with montydb. Using the example you gave, I tried

import mongo_thingy
import montydb

mongo_thingy.connect(client_cls=montydb.MontyClient)

This resulted in an error. I then tried

client = montydb.MontyClient(":memory:")
mongo_thingy.connect(client_cls=montydb.MontyClient)

It somehow fixed the error (the connect worked) but i now get AttributeError: Undefined database. on __get_database()

I was wondering if something had to be setup beforehand to use this library or if I was just doing something wrong here ?

JSON serialization is failing for Thingy classes.

I have a model and API controller:

class User(Thingy):
    pass

from flask import request
from flask_json import as_json

@app.route("/users/")
@as_json
def index():
  query = request.args
  user = User.find_one(query)
  return user

I am using Flask-JSON to decode Mongo-Thingy models. I tried both json.dumps(user) and both gave me the below error. I am not really sure it is mongo-thingy issue. It looks like it is related to Python 3.7 dataclasses to json conversion failure.

Thanking you in advance.

Sithu

Traceback (most recent call last):
  File "/Users/xxxxx/.local/share/virtualenvs/app-7ln-8Bmp/lib/python3.7/site-packages/flask/app.py", line 2446, in wsgi_app
    response = self.full_dispatch_request()
  File "/Users/xxxxx/.local/share/virtualenvs/app-7ln-8Bmp/lib/python3.7/site-packages/flask/app.py", line 1951, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/Users/xxxxxx/.local/share/virtualenvs/app-7ln-8Bmp/lib/python3.7/site-packages/flask/app.py", line 1820, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/Users/xxxxxx/.local/share/virtualenvs/app-7ln-8Bmp/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/Users/xxxxxx/.local/share/virtualenvs/app-7ln-8Bmp/lib/python3.7/site-packages/flask/app.py", line 1949, in full_dispatch_request
    rv = self.dispatch_request()
  File "/Users/xxxxx/.local/share/virtualenvs/app-7ln-8Bmp/lib/python3.7/site-packages/flask/app.py", line 1935, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/Users/xxxxx/.local/share/virtualenvs/app-7ln-8Bmp/lib/python3.7/site-packages/flask_json.py", line 233, in wrapper
    return _build_response(rv)
  File "/Users/xxxxxx/.local/share/virtualenvs/app-7ln-8Bmp/lib/python3.7/site-packages/flask_json.py", line 181, in _build_response
    return json_response(data_=data)
  File "/Users/xxxxxxx/.local/share/virtualenvs/app-7ln-8Bmp/lib/python3.7/site-packages/flask_json.py", line 139, in json_response
    response = jsonify(data_) if data_ is not None else jsonify(**kwargs)
  File "/Users/xxxxx/.local/share/virtualenvs/app-7ln-8Bmp/lib/python3.7/site-packages/flask/json/__init__.py", line 370, in jsonify
    dumps(data, indent=indent, separators=separators) + "\n",
  File "/Users/xxxxxxx/.local/share/virtualenvs/app-7ln-8Bmp/lib/python3.7/site-packages/flask/json/__init__.py", line 211, in dumps
    rv = _json.dumps(obj, **kwargs)
  File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/__init__.py", line 238, in dumps
    **kw).encode(obj)
  File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
  File "/Users/xxxxxxx/.local/share/virtualenvs/app-7ln-8Bmp/lib/python3.7/site-packages/flask_json.py", line 456, in default
    return super(JSONEncoderEx, self).default(o)
  File "/Users/xxxxxxx/.local/share/virtualenvs/app-7ln-8Bmp/lib/python3.7/site-packages/flask/json/__init__.py", line 97, in default
    return dataclasses.asdict(o)
  File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/dataclasses.py", line 1013, in asdict
    return _asdict_inner(obj, dict_factory)
  File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/dataclasses.py", line 1019, in _asdict_inner
    for f in fields(obj):
  File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/dataclasses.py", line 978, in fields
    return tuple(f for f in fields.values() if f._field_type is _FIELD)
AttributeError: 'NoneType' object has no attribute 'values'
[2019-10-16 23:05:12]: 41336 INFO 127.0.0.1 - - [16/Oct/2019 23:05:12] "GET /api/users/ HTTP/1.1" 500 -

Should we drop PyMongo<3.5 support?

http://api.mongodb.com/python/current/changelog.html#changes-in-version-3-5

Things that are modified in the pymongo-latest branch:

Fixed on PyMongo 3.5.0: mongodb/mongo-python-driver@7b1cbac

PyMongo 3.6.0 support

PyMongo 3.6.0 breaks several features of Mongo-Thingy:

============================================================================================== test session starts ===============================================================================================
platform linux -- Python 3.4.6, pytest-3.0.7, py-1.4.34, pluggy-0.4.0 -- /home/ramnes/mm/mongo-thingy/.venv/bin/python3.4
cachedir: .cache
rootdir: /home/ramnes/mm/mongo-thingy, inifile: setup.cfg
plugins: cov-2.4.0
collected 40 items 

tests/__init__.py::test_thingy_database PASSED
tests/__init__.py::test_thingy_collection PASSED
tests/__init__.py::test_thingy_names PASSED
tests/__init__.py::test_thingy_database_name PASSED
tests/__init__.py::test_thingy_collection_name PASSED
tests/__init__.py::test_thingy_database_from_collection PASSED
tests/__init__.py::test_thingy_collection_from_database PASSED
tests/__init__.py::test_thingy_database_from_name PASSED
tests/__init__.py::test_thingy_collection_from_name PASSED
tests/__init__.py::test_thingy_add_index PASSED
tests/__init__.py::test_thingy_count PASSED
tests/__init__.py::test_thingy_connect_disconnect[disconnect0-connect0] PASSED
tests/__init__.py::test_thingy_connect_disconnect[disconnect0-connect1] PASSED
tests/__init__.py::test_thingy_connect_disconnect[disconnect1-connect0] PASSED
tests/__init__.py::test_thingy_connect_disconnect[disconnect1-connect1] PASSED
tests/__init__.py::test_thingy_create_index PASSED
tests/__init__.py::test_thingy_create_indexes PASSED
tests/__init__.py::test_thingy_distinct PASSED
tests/__init__.py::test_thingy_find PASSED
tests/__init__.py::test_thingy_find_one PASSED
tests/__init__.py::test_thingy_find_one_and_replace PASSED
tests/__init__.py::test_thingy_id PASSED
tests/__init__.py::test_thingy_save PASSED
tests/__init__.py::test_thingy_save_force_insert PASSED
tests/__init__.py::test_thingy_delete PASSED
tests/__init__.py::test_create_indexes PASSED
tests/__init__.py::test_github_issue_6 PASSED
tests/cursor.py::test_cursor_bind PASSED
tests/cursor.py::test_cursor_getitem FAILED
tests/cursor.py::test_cursor_next PASSED
tests/cursor.py::test_cursor_view PASSED
tests/versioned.py::test_version_save PASSED
tests/versioned.py::test_version_indexes PASSED
tests/versioned.py::test_versioned_get_revisions FAILED
tests/versioned.py::test_versioned_author FAILED
tests/versioned.py::test_versioned_version PASSED
tests/versioned.py::test_versioned_revisions FAILED
tests/versioned.py::test_versioned_revisions_operation FAILED
tests/versioned.py::test_versioned_versioned PASSED
tests/versioned.py::test_versioned_revert FAILED

----------- coverage: platform linux, python 3.4.6-final-0 -----------
Name                        Stmts   Miss  Cover   Missing
---------------------------------------------------------
mongo_thingy/__init__.py       99      0   100%
mongo_thingy/cursor.py         26      1    96%   16
mongo_thingy/versioned.py      59      5    92%   35, 85-89
---------------------------------------------------------
TOTAL                         184      6    97%

FAIL Required test coverage of 100% not reached. Total coverage: 96.74%

==================================================================================================== FAILURES ====================================================================================================
______________________________________________________________________________________________ test_cursor_getitem _______________________________________________________________________________________________

collection = Collection(Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=False, connect=True), 'mongo_thingy_tests'), 'test_cursor_getitem')

    def test_cursor_getitem(collection):
        class Foo(Thingy):
            _collection = collection
    
        collection.insert_many([{"bar": "baz"},
                                {"bar": "qux"}])
        cursor = Cursor(Foo, collection)
    
>       result = cursor[0]

Foo        = <class 'tests.cursor.test_cursor_getitem.<locals>.Foo'>
collection = Collection(Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=False, connect=True), 'mongo_thingy_tests'), 'test_cursor_getitem')
cursor     = <mongo_thingy.cursor.Cursor object at 0x7ff86322f780>

tests/cursor.py:23: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
mongo_thingy/cursor.py:15: in __getitem__
    document = super(Cursor, self).__getitem__(index)
.venv/lib/python3.4/site-packages/pymongo/cursor.py:607: in __getitem__
    clone = self.clone()
.venv/lib/python3.4/site-packages/pymongo/cursor.py:268: in clone
    return self._clone(True)
.venv/lib/python3.4/site-packages/pymongo/cursor.py:276: in _clone
    base = self._clone_base(None)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <mongo_thingy.cursor.Cursor object at 0x7ff86322f780>, session = None

    def _clone_base(self, session):
        """Creates an empty Cursor object for information to be copied into.
            """
>       return self.__class__(self.__collection, session=session)
E       TypeError: __init__() missing 1 required positional argument: 'collection'

self       = <mongo_thingy.cursor.Cursor object at 0x7ff86322f780>
session    = None

.venv/lib/python3.4/site-packages/pymongo/cursor.py:293: TypeError
---------------------------------------------------------------------------------------------- Captured stderr call ----------------------------------------------------------------------------------------------
Exception ignored in: <bound method Cursor.__del__ of <mongo_thingy.cursor.Cursor object at 0x7ff86322f630>>
Traceback (most recent call last):
  File "/home/ramnes/mm/mongo-thingy/.venv/lib/python3.4/site-packages/pymongo/cursor.py", line 241, in __del__
    self.__die()
  File "/home/ramnes/mm/mongo-thingy/.venv/lib/python3.4/site-packages/pymongo/cursor.py", line 298, in __die
    already_killed = self.__killed
AttributeError: 'Cursor' object has no attribute '_Cursor__killed'
__________________________________________________________________________________________ test_versioned_get_revisions __________________________________________________________________________________________

TestVersionedThingy = <class 'tests.conftest.TestVersionedThingy.<locals>.TestVersionedThingy'>

    def test_versioned_get_revisions(TestVersionedThingy):
        thingy = TestVersionedThingy({"bar": "baz"}).save()
        cursor = thingy.get_revisions()
        assert isinstance(cursor, Cursor)
    
>       version = cursor[0]

TestVersionedThingy = <class 'tests.conftest.TestVersionedThingy.<locals>.TestVersionedThingy'>
cursor     = <mongo_thingy.versioned.RevisionCursor object at 0x7ff863c5d390>
thingy     = TestVersionedThingy({'bar': 'baz', '_id': ObjectId('5a86cbcd08255411f3cce174')})

tests/versioned.py:22: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
mongo_thingy/versioned.py:18: in __getitem__
    return super(RevisionCursor, self).__getitem__(index)
mongo_thingy/cursor.py:15: in __getitem__
    document = super(Cursor, self).__getitem__(index)
.venv/lib/python3.4/site-packages/pymongo/cursor.py:607: in __getitem__
    clone = self.clone()
.venv/lib/python3.4/site-packages/pymongo/cursor.py:268: in clone
    return self._clone(True)
.venv/lib/python3.4/site-packages/pymongo/cursor.py:276: in _clone
    base = self._clone_base(None)
.venv/lib/python3.4/site-packages/pymongo/cursor.py:293: in _clone_base
    return self.__class__(self.__collection, session=session)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <mongo_thingy.versioned.RevisionCursor object at 0x7ff863c5d1d0>
args = (Collection(Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=False, connect=True), 'mongo_thingy_tests'), 'test_revision'),), kwargs = {'session': None}

    def __init__(self, *args, **kwargs):
        self.thingy = kwargs.pop("thingy", None)
>       super(RevisionCursor, self).__init__(*args, **kwargs)
E       TypeError: __init__() missing 1 required positional argument: 'collection'

__class__  = <class 'mongo_thingy.versioned.RevisionCursor'>
args       = (Collection(Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=False, connect=True), 'mongo_thingy_tests'), 'test_revision'),)
kwargs     = {'session': None}
self       = <mongo_thingy.versioned.RevisionCursor object at 0x7ff863c5d1d0>

mongo_thingy/versioned.py:13: TypeError
_____________________________________________________________________________________________ test_versioned_author ______________________________________________________________________________________________

TestVersionedThingy = <class 'tests.conftest.TestVersionedThingy.<locals>.TestVersionedThingy'>

    def test_versioned_author(TestVersionedThingy):
        thingy = TestVersionedThingy({"bar": "baz"}).save()
>       assert thingy.revisions[0].author is None

TestVersionedThingy = <class 'tests.conftest.TestVersionedThingy.<locals>.TestVersionedThingy'>
thingy     = TestVersionedThingy({'bar': 'baz', '_id': ObjectId('5a86cbcd08255411f3cce177')})

tests/versioned.py:29: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
mongo_thingy/versioned.py:18: in __getitem__
    return super(RevisionCursor, self).__getitem__(index)
mongo_thingy/cursor.py:15: in __getitem__
    document = super(Cursor, self).__getitem__(index)
.venv/lib/python3.4/site-packages/pymongo/cursor.py:607: in __getitem__
    clone = self.clone()
.venv/lib/python3.4/site-packages/pymongo/cursor.py:268: in clone
    return self._clone(True)
.venv/lib/python3.4/site-packages/pymongo/cursor.py:276: in _clone
    base = self._clone_base(None)
.venv/lib/python3.4/site-packages/pymongo/cursor.py:293: in _clone_base
    return self.__class__(self.__collection, session=session)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <mongo_thingy.versioned.RevisionCursor object at 0x7ff864330400>
args = (Collection(Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=False, connect=True), 'mongo_thingy_tests'), 'test_revision'),), kwargs = {'session': None}

    def __init__(self, *args, **kwargs):
        self.thingy = kwargs.pop("thingy", None)
>       super(RevisionCursor, self).__init__(*args, **kwargs)
E       TypeError: __init__() missing 1 required positional argument: 'collection'

__class__  = <class 'mongo_thingy.versioned.RevisionCursor'>
args       = (Collection(Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=False, connect=True), 'mongo_thingy_tests'), 'test_revision'),)
kwargs     = {'session': None}
self       = <mongo_thingy.versioned.RevisionCursor object at 0x7ff864330400>

mongo_thingy/versioned.py:13: TypeError
____________________________________________________________________________________________ test_versioned_revisions ____________________________________________________________________________________________

TestVersionedThingy = <class 'tests.conftest.TestVersionedThingy.<locals>.TestVersionedThingy'>

    def test_versioned_revisions(TestVersionedThingy):
        thingy = TestVersionedThingy({"bar": "baz"}).save()
        thingy.bar = "qux"
        thingy.save()
    
>       assert thingy.revisions[0].document.get("bar") == "baz"

TestVersionedThingy = <class 'tests.conftest.TestVersionedThingy.<locals>.TestVersionedThingy'>
thingy     = TestVersionedThingy({'bar': 'qux', '_id': ObjectId('5a86cbce08255411f3cce17d')})

tests/versioned.py:49: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
mongo_thingy/versioned.py:18: in __getitem__
    return super(RevisionCursor, self).__getitem__(index)
mongo_thingy/cursor.py:15: in __getitem__
    document = super(Cursor, self).__getitem__(index)
.venv/lib/python3.4/site-packages/pymongo/cursor.py:607: in __getitem__
    clone = self.clone()
.venv/lib/python3.4/site-packages/pymongo/cursor.py:268: in clone
    return self._clone(True)
.venv/lib/python3.4/site-packages/pymongo/cursor.py:276: in _clone
    base = self._clone_base(None)
.venv/lib/python3.4/site-packages/pymongo/cursor.py:293: in _clone_base
    return self.__class__(self.__collection, session=session)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <mongo_thingy.versioned.RevisionCursor object at 0x7ff863bf5588>
args = (Collection(Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=False, connect=True), 'mongo_thingy_tests'), 'test_revision'),), kwargs = {'session': None}

    def __init__(self, *args, **kwargs):
        self.thingy = kwargs.pop("thingy", None)
>       super(RevisionCursor, self).__init__(*args, **kwargs)
E       TypeError: __init__() missing 1 required positional argument: 'collection'

__class__  = <class 'mongo_thingy.versioned.RevisionCursor'>
args       = (Collection(Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=False, connect=True), 'mongo_thingy_tests'), 'test_revision'),)
kwargs     = {'session': None}
self       = <mongo_thingy.versioned.RevisionCursor object at 0x7ff863bf5588>

mongo_thingy/versioned.py:13: TypeError
_______________________________________________________________________________________ test_versioned_revisions_operation _______________________________________________________________________________________

TestVersionedThingy = <class 'tests.conftest.TestVersionedThingy.<locals>.TestVersionedThingy'>

    def test_versioned_revisions_operation(TestVersionedThingy):
        thingy = TestVersionedThingy({"bar": "baz"}).save()
>       assert thingy.revisions[0].operation == "create"

TestVersionedThingy = <class 'tests.conftest.TestVersionedThingy.<locals>.TestVersionedThingy'>
thingy     = TestVersionedThingy({'bar': 'baz', '_id': ObjectId('5a86cbce08255411f3cce181')})

tests/versioned.py:61: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
mongo_thingy/versioned.py:18: in __getitem__
    return super(RevisionCursor, self).__getitem__(index)
mongo_thingy/cursor.py:15: in __getitem__
    document = super(Cursor, self).__getitem__(index)
.venv/lib/python3.4/site-packages/pymongo/cursor.py:607: in __getitem__
    clone = self.clone()
.venv/lib/python3.4/site-packages/pymongo/cursor.py:268: in clone
    return self._clone(True)
.venv/lib/python3.4/site-packages/pymongo/cursor.py:276: in _clone
    base = self._clone_base(None)
.venv/lib/python3.4/site-packages/pymongo/cursor.py:293: in _clone_base
    return self.__class__(self.__collection, session=session)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <mongo_thingy.versioned.RevisionCursor object at 0x7ff863bdc898>
args = (Collection(Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=False, connect=True), 'mongo_thingy_tests'), 'test_revision'),), kwargs = {'session': None}

    def __init__(self, *args, **kwargs):
        self.thingy = kwargs.pop("thingy", None)
>       super(RevisionCursor, self).__init__(*args, **kwargs)
E       TypeError: __init__() missing 1 required positional argument: 'collection'

__class__  = <class 'mongo_thingy.versioned.RevisionCursor'>
args       = (Collection(Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=False, connect=True), 'mongo_thingy_tests'), 'test_revision'),)
kwargs     = {'session': None}
self       = <mongo_thingy.versioned.RevisionCursor object at 0x7ff863bdc898>

mongo_thingy/versioned.py:13: TypeError
_____________________________________________________________________________________________ test_versioned_revert ______________________________________________________________________________________________

TestVersionedThingy = <class 'tests.conftest.TestVersionedThingy.<locals>.TestVersionedThingy'>

    def test_versioned_revert(TestVersionedThingy):
        thingy = TestVersionedThingy({"bar": "baz"}).save()
        assert thingy.version == 1
    
        thingy.revert()
        assert thingy.version == 2
        assert thingy.bar is None
    
>       thingy.revert()

TestVersionedThingy = <class 'tests.conftest.TestVersionedThingy.<locals>.TestVersionedThingy'>
thingy     = TestVersionedThingy({'_id': ObjectId('5a86cbce08255411f3cce187')})

tests/versioned.py:90: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
mongo_thingy/versioned.py:74: in revert
    previous_version = self.revisions[-2].document
mongo_thingy/versioned.py:18: in __getitem__
    return super(RevisionCursor, self).__getitem__(index)
mongo_thingy/cursor.py:15: in __getitem__
    document = super(Cursor, self).__getitem__(index)
.venv/lib/python3.4/site-packages/pymongo/cursor.py:607: in __getitem__
    clone = self.clone()
.venv/lib/python3.4/site-packages/pymongo/cursor.py:268: in clone
    return self._clone(True)
.venv/lib/python3.4/site-packages/pymongo/cursor.py:276: in _clone
    base = self._clone_base(None)
.venv/lib/python3.4/site-packages/pymongo/cursor.py:293: in _clone_base
    return self.__class__(self.__collection, session=session)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <mongo_thingy.versioned.RevisionCursor object at 0x7ff86425bfd0>
args = (Collection(Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=False, connect=True), 'mongo_thingy_tests'), 'test_revision'),), kwargs = {'session': None}

    def __init__(self, *args, **kwargs):
        self.thingy = kwargs.pop("thingy", None)
>       super(RevisionCursor, self).__init__(*args, **kwargs)
E       TypeError: __init__() missing 1 required positional argument: 'collection'

__class__  = <class 'mongo_thingy.versioned.RevisionCursor'>
args       = (Collection(Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=False, connect=True), 'mongo_thingy_tests'), 'test_revision'),)
kwargs     = {'session': None}
self       = <mongo_thingy.versioned.RevisionCursor object at 0x7ff86425bfd0>

mongo_thingy/versioned.py:13: TypeError
====================================================================================== 6 failed, 34 passed in 2.61 seconds =======================================================================================
Exception ignored in: <bound method RevisionCursor.__del__ of <mongo_thingy.versioned.RevisionCursor object at 0x7ff863c5d1d0>>
Traceback (most recent call last):
  File "/home/ramnes/mm/mongo-thingy/.venv/lib/python3.4/site-packages/pymongo/cursor.py", line 241, in __del__
    self.__die()
  File "/home/ramnes/mm/mongo-thingy/.venv/lib/python3.4/site-packages/pymongo/cursor.py", line 298, in __die
    already_killed = self.__killed
AttributeError: 'RevisionCursor' object has no attribute '_Cursor__killed'
Exception ignored in: <bound method RevisionCursor.__del__ of <mongo_thingy.versioned.RevisionCursor object at 0x7ff864330400>>
Traceback (most recent call last):
  File "/home/ramnes/mm/mongo-thingy/.venv/lib/python3.4/site-packages/pymongo/cursor.py", line 241, in __del__
    self.__die()
  File "/home/ramnes/mm/mongo-thingy/.venv/lib/python3.4/site-packages/pymongo/cursor.py", line 298, in __die
    already_killed = self.__killed
AttributeError: 'RevisionCursor' object has no attribute '_Cursor__killed'
Exception ignored in: <bound method RevisionCursor.__del__ of <mongo_thingy.versioned.RevisionCursor object at 0x7ff863bf5588>>
Traceback (most recent call last):
  File "/home/ramnes/mm/mongo-thingy/.venv/lib/python3.4/site-packages/pymongo/cursor.py", line 241, in __del__
    self.__die()
  File "/home/ramnes/mm/mongo-thingy/.venv/lib/python3.4/site-packages/pymongo/cursor.py", line 298, in __die
    already_killed = self.__killed
AttributeError: 'RevisionCursor' object has no attribute '_Cursor__killed'
Exception ignored in: <bound method RevisionCursor.__del__ of <mongo_thingy.versioned.RevisionCursor object at 0x7ff863bdc898>>
Traceback (most recent call last):
  File "/home/ramnes/mm/mongo-thingy/.venv/lib/python3.4/site-packages/pymongo/cursor.py", line 241, in __del__
    self.__die()
  File "/home/ramnes/mm/mongo-thingy/.venv/lib/python3.4/site-packages/pymongo/cursor.py", line 298, in __die
    already_killed = self.__killed
AttributeError: 'RevisionCursor' object has no attribute '_Cursor__killed'
Exception ignored in: <bound method RevisionCursor.__del__ of <mongo_thingy.versioned.RevisionCursor object at 0x7ff86425bfd0>>
Traceback (most recent call last):
  File "/home/ramnes/mm/mongo-thingy/.venv/lib/python3.4/site-packages/pymongo/cursor.py", line 241, in __del__
  File "/home/ramnes/mm/mongo-thingy/.venv/lib/python3.4/site-packages/pymongo/cursor.py", line 298, in __die
AttributeError: 'RevisionCursor' object has no attribute '_Cursor__killed'

Use accross multiple modules

I'm not clear on how to use connect (Thingy.connect) across multiple modules in a Flask app, and how to drop to using raw pymongo. In PyMongo I can create a module level variable (or use Flask-PyMongo to handle this)

client = MongoClient()

then just re-use that connection from other modules by importing it. Could I just create a single Thingy and connect instance for the app then import these:

connection_reference.py

`

 from mongo_thingy import connect, Thingy

 connect("mongodb://localhost/test")

`

module_2.py:
`
#where I want to use Thingy for Thingy defined objects

  from connection_reference import Thingy`

module_3.py

`
#in 90% of my app I want to use pymongo directly

 from connection_reference import Thingy

 db = Thingy._database
 #standard pymongo code

`

Improve the documentation

I would love to revamp the docs so that it looks modern, has examples and tutorials, and explains the advanced features.

mongo_thingy doesn't create indexes

Hey,

I have an API that uses mongo_thingy for its models in which I'm trying to add indexes the files look like this :

from mongo_thingy import Thingy
from mongo_thingy.versioned import Versioned

class Model(Versioned, Thingy):
    [...]

Model.add_index("key")

___all___ = ["Model"]

And I can't find the indexes in the database when launching my API.
I've tried putting a breakpoing inside the create_indexes function and when printing the registry I have :

-> for cls in registry:
(Pdb) registry
[<class 'thingy.Thingy'>, <class 'thingy.DatabaseThingy'>, <class 'mongo_thingy.Thingy'>]

So I'm thinking that this is the root cause, given that my model is not listed in the registry, there will be no index created for it.

How do I fix this ?

Convert ObjectId to string id for all the view

I am trying to convert ObjectID to string id for viewing because unable to convert ObjectID to json.

Here's what I am doing.

class Test(Thingy):
    @property
    def id(self):
        return str(self._id)

Test.add_view(name="strid", defaults=True, exclude="_id", include="id")

This is saving id as None in mongodb. What is the issue here?

Database discovery isn't documented nor working correctly

For instance:

If I have a database sport with a collection synchronised_swimming and I name the class

class SynchronisedSwimming(Thingy):
    collection_name = "synchronised_swimming"
    database_name = "sport"

I have to specify the database; if not it will use the synchronised database which does not make sense in that context.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.