{ "info": { "author": "ilex", "author_email": "ilexhostmaster@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3 :: Only", "Topic :: Database" ], "description": "===========\naiomongodel\n===========\n\n.. image:: https://travis-ci.org/ilex/aiomongodel.svg?branch=master\n :target: https://travis-ci.org/ilex/aiomongodel\n\n.. image:: https://readthedocs.org/projects/aiomongodel/badge/?version=latest\n :target: http://aiomongodel.readthedocs.io/en/latest/?badge=latest\n :alt: Documentation Status\n\nAn asynchronous ODM similar to `PyMODM`_ on top of `Motor`_ an asynchronous \nPython `MongoDB`_ driver. Works on ``Python 3.5`` and up. Some features\nsuch as asynchronous comprehensions require at least ``Python 3.6``. ``aiomongodel``\ncan be used with `asyncio`_ as well as with `Tornado`_.\n\nUsage of ``session`` requires at least MongoDB version 4.0.\n\n.. _PyMODM: http://pymodm.readthedocs.io/en/stable\n.. _Motor: https://pypi.python.org/pypi/motor\n.. _MongoDB: https://www.mongodb.com/\n.. _asyncio: https://docs.python.org/3/library/asyncio.html\n.. _Tornado: https://pypi.python.org/pypi/tornado\n\nInstall\n=======\n\nInstall `aiomongodel` using `pip`::\n\n pip install aiomongodel\n\nDocumentation\n=============\n\nRead the `docs`_.\n\n.. _docs: http://aiomongodel.readthedocs.io/\n\nGetting Start\n=============\n\nModeling\n--------\n\nTo create a model just create a new model class, inherit it from \n``aiomongodel.Document`` class, list all the model fields and place \na ``Meta`` class with model meta options. To create a subdocument, create\na class with fields and inherit it from ``aiomongodel.EmbeddedDocument``.\n\n.. code-block:: python\n\n # models.py\n\n from datetime import datetime\n\n from pymongo import IndexModel, DESCENDING \n\n from aiomongodel import Document, EmbeddedDocument\n from aiomongodel.fields import (\n StrField, BoolField, ListField, EmbDocField, RefField, SynonymField, \n IntField, FloatField, DateTimeField, ObjectIdField)\n\n class User(Document):\n _id = StrField(regex=r'[a-zA-Z0-9_]{3, 20}')\n is_active = BoolField(default=True)\n posts = ListField(RefField('models.Post'), default=lambda: list())\n quote = StrField(required=False)\n\n # create a synonym field\n name = SynonymField(_id)\n\n class Meta:\n collection = 'users'\n\n class Post(Document):\n # _id field will be added automatically as \n # _id = ObjectIdField(defalut=lambda: ObjectId())\n title = StrField(allow_blank=False, max_length=50)\n body = StrField()\n created = DateTimeField(default=lambda: datetime.utcnow())\n views = IntField(default=0)\n rate = FloatField(default=0.0)\n author = RefField(User, mongo_name='user')\n comments = ListField(EmbDocField('models.Comment'), default=lambda: list())\n\n class Meta:\n collection = 'posts'\n indexes = [IndexModel([('created', DESCENDING)])]\n default_sort = [('created', DESCENDING)]\n\n class Comment(EmbeddedDocument):\n _id = ObjectIdField(default=lambda: ObjectId())\n author = RefField(User)\n body = StrField()\n\n # `s` property of the fields can be used to get a mongodb string name\n # to use in queries\n assert User._id.s == '_id'\n assert User.name.s == '_id' # name is synonym\n assert Post.title.s == 'title'\n assert Post.author.s == 'user' # field has mongo_name\n assert Post.comments.body.s == 'comments.body' # compound name\n\nCRUD\n----\n\n.. code-block:: python\n\n from motor.motor_asyncio import AsyncIOMotorClient\n\n async def go(db):\n # create model's indexes \n await User.q(db).create_indexes()\n\n # CREATE\n # create using save\n # Note: if do_insert=False (default) save performs a replace\n # with upsert=True, so it does not raise if _id already exists\n # in db but replace document with that _id.\n u = await User(name='Alexandro').save(db, do_insert=True)\n assert u.name == 'Alexandro'\n assert u._id == 'Alexandro'\n assert u.is_active is True\n assert u.posts == []\n assert u.quote is None\n # using query\n u = await User.q(db).create(name='Ihor', is_active=False)\n\n # READ\n # get by id\n u = await User.q(db).get('Alexandro')\n assert u.name == 'Alexandro'\n # find\n users = await User.q(db).find({User.is_active.s: True}).to_list(10)\n assert len(users) == 2\n # using for loop\n users = []\n async for user in User.q(db).find({User.is_active.s: False}):\n users.append(user)\n assert len(users) == 1\n # in Python 3.6 an up use async comprehensions\n users = [user async for user in User.q(db).find({})]\n assert len(users) == 3\n\n # UPDATE\n u = await User.q(db).get('Ihor')\n u.is_active = True\n await u.save(db)\n assert (await User.q(db).get('Ihor')).is_active is True\n # using update (without data validation)\n # object is reloaded from db after update.\n await u.update(db, {'$push': {User.posts.s: ObjectId()}})\n\n # DELETE\n u = await User.q(db).get('Ihor')\n await u.delete(db)\n\n\n loop = asyncio.get_event_loop()\n client = AsyncIOMotorClient(io_loop=loop)\n db = client.aiomongodel_test\n loop.run_until_complete(go(db))\n\nValidation\n----------\nUse model's ``validate`` method to validate model's data. If\nthere are any invalid data an ``aiomongodel.errors.ValidationError``\nwill raise.\n\n.. note:: \n\n Creating model object or assigning it with invalid data does\n not raise errors! Be careful while saving model without validation.\n\n.. code-block:: python\n\n class Model(Document):\n name = StrField(max_length=7)\n value = IntField(gt=5, lte=13)\n data = FloatField()\n\n def go():\n m = Model(name='xxx', value=10, data=1.6)\n # validate data\n # should not raise any error\n m.validate()\n\n # invalid data\n # note that there are no errors while creating\n # model with invalid data\n invalid = Model(name='too long string', value=0)\n try:\n invalid.validate()\n except aiomongodel.errors.ValidationError as e:\n assert e.as_dict() == {\n 'name': 'length is greater than 7',\n 'value': 'value should be greater than 5',\n 'data': 'field is required'\n }\n\n # using translation - you can translate messages\n # to your language or modify them\n translation = {\n \"field is required\": \"This field is required\",\n \"length is greater than {constraint}\": (\"Length of the field \"\n \"is greater than \"\n \"{constraint} characters\"),\n # see all error messages in ValidationError docs\n # for missed messages default messages will be used\n }\n assert e.as_dict(translation=translation) == {\n 'name': 'Length of the field is greater than 7 characters',\n 'value': 'value should be greater than 5',\n 'data': 'This field is required'\n }\n\n\nQuerying\n--------\n\n.. code-block:: python\n\n async def go(db):\n # find returns a cursor \n cursor = User.q(db).find({}, {'_id': 1}).skip(1).limit(2)\n async for user in cursor:\n print(user.name)\n assert user.is_active is None # we used projection\n\n # find one\n user = await User.q(db).find_one({User.name.s: 'Alexandro'})\n assert user.name == 'Alexandro'\n\n # update\n await User.q(db).update_many(\n {User.is_active.s: True},\n {'$set': {User.is_active.s: False}})\n\n # delete \n await User.q(db).delete_many({})\n\nModels Inheritance\n------------------\n\nA hierarchy of models can be built by inheriting one model from another.\nA ``aiomongodel.Document`` class should be somewhere in hierarchy for model\nadn ``aiomongodel.EmbeddedDocument`` for subdocuments. \nNote that fields are inherited but meta options are not. \n\n.. code-block:: python\n\n class Mixin:\n value = IntField()\n\n class Parent(Document):\n name = StrField()\n\n class Child(Mixin, Parent):\n # also has value and name fields\n rate = FloatField()\n\n class OtherChild(Child):\n # also has rate and name fields\n value = FloatField() # overwrite value field from Mixin\n\n class SubDoc(Mixin, EmbeddedDocument):\n # has value field\n pass\n\nModels Inheritance With Same Collection\n---------------------------------------\n\n.. code-block:: python\n\n class Mixin:\n is_active = BoolField(default=True)\n\n class User(Mixin, Document):\n _id = StrField() \n role = StrField()\n name = SynonymField(_id)\n\n class Meta:\n collection = 'users'\n\n @classmethod\n def from_mongo(cls, data):\n # create appropriate model when loading from db\n if data['role'] == 'customer':\n return super(User, Customer).from_mongo(data)\n if data['role'] == 'admin':\n return super(User, Admin).from_mongo(data)\n\n class Customer(User):\n role = StrField(default='customer', choices=['customer']) # overwrite role field\n address = StrField()\n\n class Meta:\n collection = 'users'\n default_query = {User.role.s: 'customer'}\n\n class Admin(User):\n role = StrField(default='admin', choices=['admin']) # overwrite role field\n rights = ListField(StrField(), default=lambda: list())\n\n class Meta:\n collection = 'users'\n default_query = {User.role.s: 'admin'}\n\n\nTransaction\n-----------\n\n.. code-block:: python\n\n from motor.motor_asyncio import AsyncIOMotorClient\n\n async def go(db):\n # create collection before using transaction\n await User.create_collection(db)\n\n async with await db.client.start_session() as session:\n try:\n async with s.start_transaction():\n # all statements that use session inside this block\n # will be executed in one transaction\n\n # pass session to QuerySet\n await User.q(db, session=session).create(name='user') # note session param\n # pass session to QuerySet method \n await User.q(db).update_one(\n {User.name.s: 'user'},\n {'$set': {User.is_active.s: False}},\n session=session) # note session usage\n assert await User.q(db, session).count_documents({User.name.s: 'user'}) == 1\n\n # session could be used in document crud methods\n u = await User(name='user2').save(db, session=session)\n await u.delete(db, session=session)\n\n raise Exception() # simulate error in transaction block\n except Exception:\n # transaction was not committed \n assert await User.q(db).count_documents({User.name.s: 'user'}) == 0\n\n\n loop = asyncio.get_event_loop()\n client = AsyncIOMotorClient(io_loop=loop)\n db = client.aiomongodel_test\n loop.run_until_complete(go(db))\n\n\nLicense\n=======\n\nThe library is licensed under MIT License.\n\nChangelog\n=========\n\n0.2.0 (2018-09-12)\n------------------\n\nMove requirements to motor>=2.0.\n\nRemove ``count`` method from ``MotorQuerySetCursor``.\n\nAdd session support to ``MotorQuerySet`` and ``Document``.\n\nAdd ``create_collection`` method to ``Document``.\n\nFix ``__aiter__`` of ``MotorQuerySetCursor`` for python 3.7.\n\nDeprecate ``count`` method of ``MotorQuerySet``.\n\nDeprecate ``create`` method of ``Document``.\n\n0.1.0 (2017-05-19)\n------------------\n\nThe first ``aiomongodel`` release.\n\n\n", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/ilex/aiomongodel", "keywords": "", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "aiomongodel", "package_url": "https://pypi.org/project/aiomongodel/", "platform": "", "project_url": "https://pypi.org/project/aiomongodel/", "project_urls": { "Homepage": "https://github.com/ilex/aiomongodel" }, "release_url": "https://pypi.org/project/aiomongodel/0.2.0/", "requires_dist": [ "motor (<3.0,>=2.0)" ], "requires_python": "", "summary": "ODM to use with asynchronous MongoDB Motor driver.", "version": "0.2.0" }, "last_serial": 4262855, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "cf8f7389bb812cbdc4c8e403ed8d034c", "sha256": "539784d6e5ac9ffaab5f66d2b070eeabc03628f2035a470bf22d7f3b218cb501" }, "downloads": -1, "filename": "aiomongodel-0.1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "cf8f7389bb812cbdc4c8e403ed8d034c", "packagetype": "bdist_wheel", "python_version": "3.5", "requires_python": null, "size": 21573, "upload_time": "2017-05-19T14:13:47", "url": "https://files.pythonhosted.org/packages/89/88/d993270c9cda99ad235bc39177fe72461629434921a73d457a4a54e66d56/aiomongodel-0.1.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "77605f5ff66636ea27867f637ac5c28f", "sha256": "5be81d00b445769bb775d70ee95cb335702da9e6e20b53449ff5fb706e260b27" }, "downloads": -1, "filename": "aiomongodel-0.1.0.tar.gz", "has_sig": false, "md5_digest": "77605f5ff66636ea27867f637ac5c28f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19932, "upload_time": "2017-05-19T14:13:45", "url": "https://files.pythonhosted.org/packages/b0/13/fa50ccfa18c8be7b3a6c9511ee911b200ad83901f3235d0ae99d5ed9291c/aiomongodel-0.1.0.tar.gz" } ], "0.2.0": [ { "comment_text": "", "digests": { "md5": "d059d19fe814f79ae4843e896cee13a5", "sha256": "a4ec4f4138b7040793486764f3cbdefd579615b4b5b09f0b17a4698b912356d0" }, "downloads": -1, "filename": "aiomongodel-0.2.0-py3-none-any.whl", "has_sig": false, "md5_digest": "d059d19fe814f79ae4843e896cee13a5", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 18512, "upload_time": "2018-09-11T22:44:01", "url": "https://files.pythonhosted.org/packages/90/c9/b47345e8f6a980056a74a8db8912622e5605f8dd42e184bc0e361a65afd4/aiomongodel-0.2.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "aa3c0e75785fe4e475f6e243f1bbf2d6", "sha256": "662070047b40c93e946ba37661f7fc81cfc07e1529b527e95da291d1962b3c1a" }, "downloads": -1, "filename": "aiomongodel-0.2.0.tar.gz", "has_sig": false, "md5_digest": "aa3c0e75785fe4e475f6e243f1bbf2d6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21444, "upload_time": "2018-09-11T22:44:02", "url": "https://files.pythonhosted.org/packages/0e/17/1b5aa50ef413ec94b5501f9f4b9e8704a0be18dbbbf33ebb2d4cc660d521/aiomongodel-0.2.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "d059d19fe814f79ae4843e896cee13a5", "sha256": "a4ec4f4138b7040793486764f3cbdefd579615b4b5b09f0b17a4698b912356d0" }, "downloads": -1, "filename": "aiomongodel-0.2.0-py3-none-any.whl", "has_sig": false, "md5_digest": "d059d19fe814f79ae4843e896cee13a5", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 18512, "upload_time": "2018-09-11T22:44:01", "url": "https://files.pythonhosted.org/packages/90/c9/b47345e8f6a980056a74a8db8912622e5605f8dd42e184bc0e361a65afd4/aiomongodel-0.2.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "aa3c0e75785fe4e475f6e243f1bbf2d6", "sha256": "662070047b40c93e946ba37661f7fc81cfc07e1529b527e95da291d1962b3c1a" }, "downloads": -1, "filename": "aiomongodel-0.2.0.tar.gz", "has_sig": false, "md5_digest": "aa3c0e75785fe4e475f6e243f1bbf2d6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21444, "upload_time": "2018-09-11T22:44:02", "url": "https://files.pythonhosted.org/packages/0e/17/1b5aa50ef413ec94b5501f9f4b9e8704a0be18dbbbf33ebb2d4cc660d521/aiomongodel-0.2.0.tar.gz" } ] }