Package: yadm
It’s small and simple ODM for use with MongoDB.RequirementsYADM support MongoDB version 3.x only. MongoDB 2.x is not supported.Minimal version of python — 3.6.Quick startCreate the modelsfromdatetimeimportdatetimeimportpymongofromyadmimportDatabasefromyadmimportDocument,EmbeddedDocumentfromyadmimportfieldsfromyadm.serializeimportto_mongoclassUser(Document):__collection__='users'name=fields.StringField()email=fields.EmailField()classPostLogItem(EmbeddedDocument):op=fields.StringField(choices=['created','comment_added'])at=fields.DatetimeField()data=fields.MongoMapField()classPost(Document):__collection__='posts'user=fields.ReferenceField(User)created_at=fields.DatetimeField(auto_now=True)title=fields.StringField()body=fields.StringField()log=fields.ListField(fields.EmbeddedDocumentField(PostLogItem))classComment(Document):__collection__='comments'user=fields.ReferenceField(User)created_at=fields.DatetimeField(auto_now=True)post=fields.ReferenceField(Post)text=fields.StringField()All documents creates from classDocument. You can use multiple inheritance.__collection__magic attribute setups the collection name for documents of model.Connect to databaseclient=pymongo.MongoClient('mongodb://localhost:27017')db=Database(client,'blog')Databaseobject is a wrapper aboutpymongoormotorDatabase.Create documentsUseruser=User(name='Bill',email='bill@galactic.hero')db.insert_one(user)Just insert document to database.Postpost=Post()post.user=userpost.title='Small post'post.body='Bla-bla-bla...'post.log=[PostLogItem(op='created',at=datetime.utcnow())]db.insert_one(post)You can fill documents as above.Comment the postcomment=Comment()comment.user=usercomment.post=postcomment.text="RE: Bla-bla-bla..."db.insert_one(comment)db.update_one(post,push={'log':to_mongo(PostLogItem(op='comment_added',at=comment.created_at,data={'comment':comment.id,'user':comment.user.id,}))})We add log item to post’s log. This is very usefull case.Queriesfindqs=db(Post).find({'title':{'$regex':'^S'}})assertqs.count()>0db(Post)creates theQuerySetobject;findmethod get the raw-query and return newQuerySetobject with updated criteria;countmethod make the query to database and return value.forpostinqs:assertpost.title.startswith('S')__iter__method make thefind-query and returns the generator of documents.find_oneGet the first finded document.post=db(Post).find_one({'user':user.id})get_documentGet the document by id from primary.user=db.get_document(User,user.id)Referencesuser=post.userGet attribute with reference makes the query to referred collection. Warning: N+1 problem!
We have a cache inQuerySetobject and get one referred document only once for one queryset.Lookupscomments=db(Comment).find({'post':post.id}).sort(('created_at',1))forcommentincomments.lookup('user'):print(comment.user.name,comment.text)This code create the aggregate query with$lookupstatement for resolve the references.Aggregationsagg=(db.aggregate(Comment).match(user=user.id).group(_id='post',count={'$sum':1}).sort(count=-1))foriteminagg:print(item)Or traditional MongoDB syntax:agg=db.aggregate(Comment,pipeline=[{'match':{'user':user.id}},{'group':{'_id':'post','count':{'$sum':1}}},{'sort':{'count':-1}},])CHANGES2.0.9 (2023-08-23)Addcommentmethods forQuerySetandAggregationto specify comment feature of MongoDB.2.0.8 (2021-09-23)Asyncio support for testing.2.0.7 (2021-04-21)Some bugfixes.2.0.5 (2019-02-25)AddAggregation.hintmethod.2.0.4 (2019-02-20)AddDatabase.estimated_document_countmethod for quickly count documents in the collection.2.0.1 (2018-11-04)AddQuerySet.hintfor specify index for query.2.0.0 (2018-10-25)ABig Rewritedocument logic:Document.__raw__now contains only data from pymongo, without anyAttributeNotSetorNotLoaded;Document.__changed__is removed: all changes reflects toDocument.__cache__;Document.__not_loaded__frozenset of fields whitch not loaded by projection;Document.__new_document__flag isTruefor document’s objects whitch created directly in your code;Document.__log__list-like container with log of document changes (unstable API at now);Document.__data__is removed as deprecated;Now is not allow to set fields as classes;Defaults is not lazy and creates with document instance;Update for minimal versions of pymongo (3.7) and motor (2.0):AddDatabase.bulk_write;AddDatabase.insert_one,Database.insert_manyandDatabase.delete_one;DeprecateDatabase.insert,Database.remove;RemoveDatabase.bulk(without deprecation period, sorry);AddQuerySet.count_documents;AddQuerySet.update_oneandQuerySet.update_many;AddQuerySet.delete_oneandQuerySet.delete_many;AddQuerySet.find_one_and_update,QuerySet.find_one_and_replaceandQuerySet.find_one_and_delete;DeprecateQuerySet.count;DeprecateQuerySet.update,QuerySet.removeandQuerySet.find_and_modify;Remove deprecatedQuerySet.with_id;Simple interface for build lookups:QuerySet.lookup;Removebccargument fromMoneyField;AddDecimal128Field.1.5.0 (2017-12-31)Experimentalasynciosupport;AddReferencesListFieldfor lists of references.1.4.15 (2017-12-27)Addprojectionargument toDatabase.get_documentandDatabase.reload;AddDocument.__default_projection__attribute.1.4.14 (2017-11-06)AddEnumFieldfor saveenum.Enum;AddEnumStateFieldfor simple state machines based onenum.Enum.1.4.13 (2017-10-31)AddQuerySet.batch_sizemethod for setup batch size for cursor;Some minor fixes.1.4.10 (2017-07-07)ReferenceField.from_mongotry to get document from primaryif not found by default.1.4.9 (2017-07-06)AddQuerySet.read_primarymethod for simple setupread_preference.Primary.1.4.4 (2017-05-17)AddTimedeltaFieldfor stores durations;AddSimpleEmbeddedDocumentFieldfor simply create embedded documents.classDoc(Document):embedded=SimpleEmbeddedDocumentField({'i':IntegerField(),'s':StringField(),})1.4.3 (2017-05-14)AddStaticFieldfor static data.1.4.2 (2017-04-09)Additional arguments (likewrite_concern) for write operations;create_fakesave the documents with write concern “majority” by default.1.4.0 (2017-04-05)Drop pymongo 2 support;Additional options for databases and collections;AddDatabase.get_document;AddTypedEmbeddedDocumentField;reloadargument ofDatabase.update_onemust be keyword(may be backward incompotable).1.3.1 (2017-02-21)Change raw data forMoney;1.3.0 (2017-02-19)Add currency support toMoney:Totaly rewriteMoneytype. Now it is not subclass ofDecimal;Add storage for currencies:yadm.fields.money.currency.DEFAULT_CURRENCY_STORAGE;1.2.1 (2017-01-19)AddQuerySet.find_infor$inqueries with specified order;1.2.0 (2016-12-27)Drop MongoDB 2.X suport;Objects for update and remove results;Use Faker instead fake-factory.1.1.4 (2016-08-20)Add some features toBulk:Bulk.update_one(document, **kw): method for add update one document in bulk;Bulk.find(query).update(**kw): update many documents by query;Bulk.find(query).upsert().update(**kw): upsert document;Bulk.find(query).remove(**kw): remove documents;1.1.3 (2016-07-23)AddQuerySet.idsmethod for get only documents id’s from queryset;AddMoney.total_centsmethod andMoney.from_centsclassmethod;1.1 (2016-04-26)Add cacheing on queryset level and use it forReferenceField;Add mongo aggregation framework support;Addread_preferencesetting;Addexcargument toQuerySet.find_onefor raise exception if not found;Addmultiargument toQuerySet.remove;DeprecateQuerySet.with_id;Refactoring.1.0 (2015-11-14)Change document structure. No more badBaseDocument.__data__attribute:BaseDocument.__raw__: raw data from mongo;BaseDocument.__cache__: cached objects, casted with fields;BaseDocument.__changed__: changed objects.Changes api for custom fields:Not more need create field descriptors for every field;prepare_valuecalled only for setattr;to_mongocalled only for save objects to mongo;from_mongocalled only for load values fromBaseDocument.__raw__;RemoveField.defaultattribute. UseField.get_defaultmethod;AddField.get_if_not_loadedandField.get_if_attribute_not_setmethod;By default raiseNotLoadedErrorif field not loaded from projection;Changes inReferenceField:RaiseBrokenReferenceif link is bloken;RaiseNotBindingToDatabaseif document not saved to database;smart_nullkeyword forField;Fields in document must be instances (not classes!);RemoveArrayContainerandArrayContainerField;Remove oldMapIntKeysFieldandMapObjectIdKeysField. Use newMapCustomKeysField;AddDatabase.update_onemethod for run simple update query with specified document;AddQuerySet.distinct;serialize.from_mongonow acceptnot_loadedsequence with filed names who must mark as not loaded,parentandname;serialize.to_mongodo not callFieldDescriptor.__set__;Fakers! Subsystem for generate test objects;Tests now use pytest;And more, and more…