{"id":13771545,"url":"https://github.com/ticketscloud/yadm","last_synced_at":"2025-05-11T04:30:45.440Z","repository":{"id":15177637,"uuid":"17905472","full_name":"ticketscloud/yadm","owner":"ticketscloud","description":"Yet Another Document Mapper (ODM) for MongoDB","archived":false,"fork":false,"pushed_at":"2023-08-23T11:01:19.000Z","size":566,"stargazers_count":30,"open_issues_count":1,"forks_count":10,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-05-21T05:36:10.210Z","etag":null,"topics":["mongo","mongodb","mongodb-document","odm","orm","python"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ticketscloud.png","metadata":{"files":{"readme":"README.rst","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2014-03-19T13:39:50.000Z","updated_at":"2024-05-15T19:09:17.000Z","dependencies_parsed_at":"2023-01-13T18:17:33.131Z","dependency_job_id":"3ce841b3-5cb4-4d30-8ac2-cac20f93dfaa","html_url":"https://github.com/ticketscloud/yadm","commit_stats":null,"previous_names":["zzzsochi/yadm"],"tags_count":55,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ticketscloud%2Fyadm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ticketscloud%2Fyadm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ticketscloud%2Fyadm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ticketscloud%2Fyadm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ticketscloud","download_url":"https://codeload.github.com/ticketscloud/yadm/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225012247,"owners_count":17406984,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["mongo","mongodb","mongodb-document","odm","orm","python"],"created_at":"2024-08-03T17:00:52.576Z","updated_at":"2024-11-17T07:30:48.901Z","avatar_url":"https://github.com/ticketscloud.png","language":"Python","readme":"===========================\nYet Another Document Mapper\n===========================\n\n.. image:: https://travis-ci.org/zzzsochi/yadm.svg?branch=master\n    :target: https://travis-ci.org/zzzsochi/yadm\n\n.. image:: https://coveralls.io/repos/github/zzzsochi/yadm/badge.svg?branch=master\n    :target: https://coveralls.io/github/zzzsochi/yadm?branch=master\n\n\n\nIt's small and simple ODM for use with MongoDB.\n\n.. Full documentation: http://yadm.readthedocs.org\n\n\n------------\nRequirements\n------------\n\nYADM support MongoDB version 3.x only. MongoDB 2.x is not supported.\n\nMinimal version of python — 3.6.\n\n\n-----------\nQuick start\n-----------\n\nCreate the models\n=================\n\n.. code:: python\n\n    from datetime import datetime\n\n    import pymongo\n\n    from yadm import Database\n    from yadm import Document, EmbeddedDocument\n    from yadm import fields\n    from yadm.serialize import to_mongo\n\n\n    class User(Document):\n        __collection__ = 'users'\n\n        name = fields.StringField()\n        email = fields.EmailField()\n\n\n    class PostLogItem(EmbeddedDocument):\n        op = fields.StringField(choices=['created', 'comment_added'])\n        at = fields.DatetimeField()\n        data = fields.MongoMapField()\n\n\n    class Post(Document):\n        __collection__ = 'posts'\n\n        user = fields.ReferenceField(User)\n        created_at = fields.DatetimeField(auto_now=True)\n        title = fields.StringField()\n        body = fields.StringField()\n        log = fields.ListField(fields.EmbeddedDocumentField(PostLogItem))\n\n\n    class Comment(Document):\n        __collection__ = 'comments'\n\n        user = fields.ReferenceField(User)\n        created_at = fields.DatetimeField(auto_now=True)\n        post = fields.ReferenceField(Post)\n        text = fields.StringField()\n\nAll documents creates from class ``Document``. You can use multiple inheritance.\n\n``__collection__`` magic attribute setups the collection name for documents of model.\n\n\nConnect to database\n===================\n\n.. code:: python\n\n    client = pymongo.MongoClient('mongodb://localhost:27017')\n    db = Database(client, 'blog')\n\n``Database`` object is a wrapper about ``pymongo`` or ``motor`` ``Database``.\n\n\nCreate documents\n================\n\nUser\n----\n\n.. code:: python\n\n    user = User(name='Bill', email='bill@galactic.hero')\n    db.insert_one(user)\n\nJust insert document to database.\n\n\nPost\n----\n\n.. code:: python\n\n    post = Post()\n    post.user = user\n    post.title = 'Small post'\n    post.body = 'Bla-bla-bla...'\n    post.log = [PostLogItem(op='created', at=datetime.utcnow())]\n    db.insert_one(post)\n\nYou can fill documents as above.\n\n\nComment the post\n----------------\n\n.. code:: python\n\n    comment = Comment()\n    comment.user = user\n    comment.post = post\n    comment.text = \"RE: Bla-bla-bla...\"\n    db.insert_one(comment)\n    db.update_one(post, push={\n        'log': to_mongo(PostLogItem(op='comment_added',\n                                    at=comment.created_at,\n                                    data={\n                                      'comment': comment.id,\n                                      'user': comment.user.id,\n                                    }))\n    })\n\nWe add log item to post's log. This is very usefull case.\n\n\nQueries\n=======\n\nfind\n----\n\n.. code:: python\n\n    qs = db(Post).find({'title': {'$regex': '^S'}})\n    assert qs.count() \u003e 0\n\n1. ``db(Post)`` creates the ``QuerySet`` object;\n2. ``find`` method get the raw-query and return new ``QuerySet`` object with updated criteria;\n3. ``count`` method make the query to database and return value.\n\n.. code:: python\n\n    for post in qs:\n        assert post.title.startswith('S')\n\n``__iter__`` method make the ``find``-query and returns the generator of documents.\n\n\nfind_one\n--------\n\nGet the first finded document.\n\n.. code:: python\n\n    post = db(Post).find_one({'user': user.id})\n\n\nget_document\n------------\n\nGet the document by id from primary.\n\n.. code:: python\n\n    user = db.get_document(User, user.id)\n\n\nReferences\n----------\n\n.. code:: python\n\n    user = post.user\n\nGet attribute with reference makes the query to referred collection. Warning: N+1 problem!\nWe have a cache in ``QuerySet`` object and get one referred document only once for one queryset.\n\n\nLookups\n-------\n\n.. code:: python\n\n    comments = db(Comment).find({'post': post.id}).sort(('created_at', 1))\n    for comment in comments.lookup('user'):\n        print(comment.user.name, comment.text)\n\nThis code create the aggregate query with ``$lookup`` statement for resolve the references.\n\n\nAggregations\n------------\n\n.. code:: python\n\n    agg = (db.aggregate(Comment)\n           .match(user=user.id)\n           .group(_id='post', count={'$sum': 1})\n           .sort(count=-1))\n\n    for item in agg:\n        print(item)\n\nOr traditional MongoDB syntax:\n\n.. code:: python\n\n    agg = db.aggregate(Comment, pipeline=[\n        {'match': {'user': user.id}},\n        {'group': {'_id': 'post', 'count': {'$sum': 1}}},\n        {'sort': {'count': -1}},\n    ])\n\n\n-------\nCHANGES\n-------\n\n2.0.9 (2023-08-23)\n==================\n\n* Add ``comment`` methods for ``QuerySet`` and ``Aggregation`` to specify comment feature of MongoDB.\n\n2.0.8 (2021-09-23)\n==================\n\n* Asyncio support for testing.\n\n\n2.0.7 (2021-04-21)\n==================\n\n* Some bugfixes.\n\n\n2.0.5 (2019-02-25)\n==================\n\n* Add ``Aggregation.hint`` method.\n\n\n2.0.4 (2019-02-20)\n==================\n\n* Add ``Database.estimated_document_count`` method for quickly count documents in the collection.\n\n\n2.0.1 (2018-11-04)\n==================\n\n* Add ``QuerySet.hint`` for specify index for query.\n\n\n2.0.0 (2018-10-25)\n==================\n\n* A `Big Rewrite \u003chttps://www.youtube.com/watch?v=xCGu5Z_vaps\u003e`_ document logic:\n    - ``Document.__raw__`` now contains only data from pymongo, without any ``AttributeNotSet`` or ``NotLoaded``;\n    - ``Document.__changed__`` is removed: all changes reflects to ``Document.__cache__``;\n    - ``Document.__not_loaded__`` frozenset of fields whitch not loaded by projection;\n    - ``Document.__new_document__`` flag is ``True`` for document's objects whitch created directly in your code;\n    - ``Document.__log__`` list-like container with log of document changes (unstable API at now);\n    - ``Document.__data__`` is removed as deprecated;\n    - Now is not allow to set fields as classes;\n    - Defaults is not lazy and creates with document instance;\n\n* Update for minimal versions of pymongo (3.7) and motor (2.0):\n    - Add ``Database.bulk_write``;\n    - Add ``Database.insert_one``, ``Database.insert_many`` and ``Database.delete_one``;\n    - Deprecate ``Database.insert``, ``Database.remove``;\n    - Remove ``Database.bulk`` (without deprecation period, sorry);\n    - Add ``QuerySet.count_documents``;\n    - Add ``QuerySet.update_one`` and ``QuerySet.update_many``;\n    - Add ``QuerySet.delete_one`` and ``QuerySet.delete_many``;\n    - Add ``QuerySet.find_one_and_update``, ``QuerySet.find_one_and_replace`` and ``QuerySet.find_one_and_delete``;\n    - Deprecate ``QuerySet.count``;\n    - Deprecate ``QuerySet.update``, ``QuerySet.remove`` and ``QuerySet.find_and_modify``;\n    - Remove deprecated ``QuerySet.with_id``;\n\n* Simple interface for build lookups: ``QuerySet.lookup``;\n* Remove ``bcc`` argument from ``MoneyField``;\n* Add ``Decimal128Field``.\n\n\n1.5.0 (2017-12-31)\n==================\n\n* Experimental ``asyncio`` support;\n* Add ``ReferencesListField`` for lists of references.\n\n\n1.4.15 (2017-12-27)\n===================\n\n* Add ``projection`` argument to ``Database.get_document`` and ``Database.reload``;\n* Add ``Document.__default_projection__`` attribute.\n\n\n1.4.14 (2017-11-06)\n===================\n\n* Add ``EnumField`` for save ``enum.Enum``;\n* Add ``EnumStateField`` for simple state machines based on ``enum.Enum``.\n\n\n1.4.13 (2017-10-31)\n===================\n\n* Add ``QuerySet.batch_size`` method for setup batch size for cursor;\n* Some minor fixes.\n\n\n\n1.4.10 (2017-07-07)\n===================\n\n* ``ReferenceField.from_mongo`` try to get document from primary\n    if not found by default.\n\n\n1.4.9 (2017-07-06)\n==================\n\n* Add ``QuerySet.read_primary`` method for simple setup ``read_preference.Primary``.\n\n\n1.4.4 (2017-05-17)\n==================\n\n* Add ``TimedeltaField`` for stores durations;\n* Add ``SimpleEmbeddedDocumentField`` for simply create embedded documents.\n\n.. code:: python\n\n    class Doc(Document):\n        embedded = SimpleEmbeddedDocumentField({\n            'i': IntegerField(),\n            's': StringField(),\n        })\n\n\n1.4.3 (2017-05-14)\n==================\n\n* Add ``StaticField`` for static data.\n\n\n1.4.2 (2017-04-09)\n==================\n\n* Additional arguments (like ``write_concern``) for write operations;\n* ``create_fake`` save the documents with write concern \"majority\" by default.\n\n\n1.4.0 (2017-04-05)\n==================\n\n* Drop pymongo 2 support;\n* Additional options for databases and collections;\n* Add ``Database.get_document``;\n* Add ``TypedEmbeddedDocumentField``;\n* ``reload`` argument of ``Database.update_one`` must be keyword\n    (may be backward incompotable).\n\n\n1.3.1 (2017-02-21)\n==================\n\n* Change raw data for ``Money``;\n\n\n1.3.0 (2017-02-19)\n==================\n\n* Add currency support to ``Money``:\n    - Totaly rewrite ``Money`` type. Now it is not subclass of ``Decimal``;\n    - Add storage for currencies: ``yadm.fields.money.currency.DEFAULT_CURRENCY_STORAGE``;\n\n\n1.2.1 (2017-01-19)\n==================\n\n* Add ``QuerySet.find_in`` for ``$in`` queries with specified order;\n\n\n1.2.0 (2016-12-27)\n==================\n\n* Drop MongoDB 2.X suport;\n* Objects for update and remove results;\n* Use Faker instead fake-factory.\n\n\n1.1.4 (2016-08-20)\n==================\n\n* Add some features to ``Bulk``:\n    - ``Bulk.update_one(document, **kw)``: method for add update one document in bulk;\n    - ``Bulk.find(query).update(**kw)``: update many documents by query;\n    - ``Bulk.find(query).upsert().update(**kw)``: upsert document;\n    - ``Bulk.find(query).remove(**kw)``: remove documents;\n\n\n1.1.3 (2016-07-23)\n==================\n\n* Add ``QuerySet.ids`` method for get only documents id's from queryset;\n\n* Add ``Money.total_cents`` method and ``Money.from_cents`` classmethod;\n\n\n1.1 (2016-04-26)\n================\n\n* Add cacheing on queryset level and use it for ``ReferenceField``;\n\n* Add mongo aggregation framework support;\n\n* Add ``read_preference`` setting;\n\n* Add ``exc`` argument to ``QuerySet.find_one`` for raise exception if not found;\n\n* Add ``multi`` argument to ``QuerySet.remove``;\n\n* Deprecate ``QuerySet.with_id``;\n\n* Refactoring.\n\n\n1.0 (2015-11-14)\n================\n\n* Change document structure. No more bad `BaseDocument.__data__` attribute:\n    - `BaseDocument.__raw__`: raw data from mongo;\n    - `BaseDocument.__cache__`: cached objects, casted with fields;\n    - `BaseDocument.__changed__`: changed objects.\n\n* Changes api for custom fields:\n    - Not more need create field descriptors for every field;\n    - `prepare_value` called only for setattr;\n    - `to_mongo` called only for save objects to mongo;\n    - `from_mongo` called only for load values from `BaseDocument.__raw__`;\n    - Remove `Field.default` attribute. Use `Field.get_default` method;\n    - Add `Field.get_if_not_loaded` and `Field.get_if_attribute_not_set` method;\n    - By default raise `NotLoadedError` if field not loaded from projection;\n\n* Changes in `ReferenceField`:\n    - Raise `BrokenReference` if link is bloken;\n    - Raise `NotBindingToDatabase` if document not saved to database;\n\n* `smart_null` keyword for `Field`;\n\n* Fields in document must be instances (not classes!);\n\n* Remove `ArrayContainer` and `ArrayContainerField`;\n\n* Remove old `MapIntKeysField` and `MapObjectIdKeysField`. Use new `MapCustomKeysField`;\n\n* Add `Database.update_one` method for run simple update query with specified document;\n\n* Add `QuerySet.distinct`;\n\n* `serialize.from_mongo` now accept `not_loaded` sequence with filed names who must mark as not loaded, `parent` and `name`;\n\n* `serialize.to_mongo` do not call `FieldDescriptor.__set__`;\n\n* Fakers! Subsystem for generate test objects;\n\n* Tests now use pytest;\n\n* And more, and more...\n","funding_links":[],"categories":["ODM, ORM, Active Record"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fticketscloud%2Fyadm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fticketscloud%2Fyadm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fticketscloud%2Fyadm/lists"}