From 90409624c6d94aeabb82e39cf8205b69e1ff0259 Mon Sep 17 00:00:00 2001 From: Alexander Zelenyak Date: Tue, 7 Nov 2017 19:11:44 +0300 Subject: [PATCH] try --- README.rst | 38 +++++---- doc/index.rst | 226 +++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 208 insertions(+), 56 deletions(-) diff --git a/README.rst b/README.rst index f4754e9..f6ad51a 100644 --- a/README.rst +++ b/README.rst @@ -56,6 +56,7 @@ Create the models __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)) @@ -218,39 +219,40 @@ Is equal of this: print(item) -======= +------- CHANGES -======= +------- 1.4.14 (2017-11-06) -------------------- +=================== * Add ``EnumField`` for save ``enum.Enum``; * Add ``EnumStateField`` for simple state machines based on ``enum.Enum``. 1.4.13 (2017-10-31) -------------------- +=================== * Add ``QuerySet.batch_size`` method for setup batch size for cursor; * Some minor fixes. + 1.4.10 (2017-07-07) ------------------- +================== * ``ReferenceField.from_mongo`` try to get document from primary if not found by default. 1.4.9 (2017-07-06) ------------------- +================== * Add ``QuerySet.read_primary`` method for simple setup ``read_preference.Primary``. 1.4.4 (2017-05-17) ------------------- +================== * Add ``TimedeltaField`` for stores durations; * Add ``SimpleEmbeddedDocumentField`` for simply create embedded documents. @@ -265,20 +267,20 @@ CHANGES 1.4.3 (2017-05-14) ------------------- +================== * Add ``StaticField`` for static data. 1.4.2 (2017-04-09) ------------------- +================== * Additional arguments (like ``write_concern``) for write operations; * ``create_fake`` save the documents with write concern "majority" by default. 1.4.0 (2017-04-05) ------------------- +================== * Drop pymongo 2 support; * Additional options for databases and collections; @@ -289,13 +291,13 @@ CHANGES 1.3.1 (2017-02-21) ------------------- +================== * Change raw data for ``Money``; 1.3.0 (2017-02-19) ------------------- +================== * Add currency support to ``Money``: - Totaly rewrite ``Money`` type. Now it is not subclass of ``Decimal``; @@ -303,13 +305,13 @@ CHANGES 1.2.1 (2017-01-19) ------------------- +================== * Add ``QuerySet.find_in`` for ``$in`` queries with specified order; 1.2.0 (2016-12-27) ------------------- +================== * Drop MongoDB 2.X suport; * Objects for update and remove results; @@ -317,7 +319,7 @@ CHANGES 1.1.4 (2016-08-20) ------------------- +================== * Add some features to ``Bulk``: - ``Bulk.update_one(document, **kw)``: method for add update one document in bulk; @@ -327,7 +329,7 @@ CHANGES 1.1.3 (2016-07-23) ------------------- +================== * Add ``QuerySet.ids`` method for get only documents id's from queryset; @@ -335,7 +337,7 @@ CHANGES 1.1 (2016-04-26) ----------------- +================ * Add cacheing on queryset level and use it for ``ReferenceField``; @@ -353,7 +355,7 @@ CHANGES 1.0 (2015-11-14) ----------------- +================ * Change document structure. No more bad `BaseDocument.__data__` attribute: - `BaseDocument.__raw__`: raw data from mongo; diff --git a/doc/index.rst b/doc/index.rst index 47614f9..9e47c6a 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -21,54 +21,204 @@ Minimal version of python is 3.4. Quick start ----------- -.. code-block:: python +Create the models +================= + +.. code:: python import pymongo - from yadm import Database, Document, fields + from yadm import Database + from yadm import Document, EmbeddedDocument + from yadm import fields + from yadm.serialize import to_mongo + + + class User(Document): + __collection__ = 'users' + + name = fields.StringField() + email = fields.EmailField() - # Create model - class BlogPost(Document): - __collection__ = 'blog_posts' + class PostLogItem(EmbeddedDocument): + op = fields.StringField(choices=['created', 'comment_added']) + at = fields.DatetimeField() + data = fields.MongoMapField() + + class Post(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)) + + + class Comment(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 class ``Document``. You can use multiple inheritance. + +``__collection__`` magic attribute setups the collection name for documents of model. + + +Connect to database +=================== + +.. code:: python + + client = pymongo.MongoClient('mongodb://localhost:27017') + db = Database(client, 'blog') + +``Database`` object is a wrapper about ``pymongo`` or ``motor`` ``Database``. + +Create documents +================ - # Create post - post = BlogPost() +User +---- + +.. code:: python + + user = User(name='Bill', email='hero_of@the.galaxy') + db.insert(user) + +Just insert document to database. + + +Post +---- + +.. code:: python + + post = Post() + post.user = user post.title = 'Small post' post.body = 'Bla-bla-bla...' + post.log = [PostLogItem(op='created', at=datetime.utcnow())] # first log item + db.insert(post) - # Connect to database - client = pymongo.MongoClient("localhost", 27017) - db = Database(client, 'test') +You can fill documents as below. - # Insert post to database - db.insert(post) - # Query posts - qs = db.get_queryset(BlogPost).find({'title': {'$regex': '^s.*'}}) +Comment the post +---------------- + +.. code:: python + + comment = Comment() + comment.user = user + comment.post = post + comment.text = "RE: Bla-bla-bla..." + db.insert(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. + + +Queries +======= + +find +---- + +.. code:: python + + qs = db(Post).find({'title': {'$regex': '^S'}}) assert qs.count() > 0 +1. ``db(Post)`` creates the ``QuerySet`` object; +2. ``find`` method get the raw-query and return new ``QuerySet`` object with updated criteria; +3. ``count`` method make the query to database and return value. + +.. code:: python + for post in qs: - assert post.title.startswith('s') + assert post.title.startswith('S') + +``__iter__`` method make the ``find``-query and returns the generator of documents. - # Query one post - post = db.get_queryset(BlogPost).find_one({'title': 'Small post'}) - # Change post - post.title = 'Bla-bla-bla title' +find_one +-------- + +.. code:: python - # Save changed post - db.save(post) + post = db(Post).find_one({'user': user.id}) +``find_one`` get the first finded document. + +.. code:: python + + user = post.user + +Get attribute with reference makes the query to referred collection. Warning: N+1 problem! +We have a cache in ``QuerySet`` object and get one referred document only once for one queryset. + + +join +---- + +Yes! We have a joins! In userspace... + +.. code:: python + comments = db(Comment).find({'post': post.id}).sort(('created_at', 1)) + for comment in comments.join('user'): + print(comment.user.name, comment.text) + +1. Create the queryset for comments; +2. In ``join`` we get all documents for qs, get all users with one ``$in``-query and bind to documents. + + +aggregations +------------ + +.. code:: python + + agg = (db.aggregate(Comment) + .match(user=user.id}) + .group(_id='post', count={'$sum:' 1}) + .sort(count=-1)) + + for item in agg: + print(item) + +Is equal of this: + +.. code:: python + + agg = db.aggregate(Comment, [ + {'match': {'user': user.id}}, + {'group': {'_id': 'post', 'count': {'$sum:' 1}}}, + {'sort': {'count': -1}}, + ]) + + for item in agg: + print(item) + + +------- CHANGES -======= +------- 1.4.14 (2017-11-06) ------------------- +=================== * Add :py:meth:`EnumField ` for save ``enum.Enum``; @@ -77,7 +227,7 @@ CHANGES 1.4.13 (2017-10-31) ------------------- +=================== * Add :py:meth:`QuerySet.batch_size ` method for setup batch size for cursor; @@ -85,21 +235,21 @@ CHANGES 1.4.10 (2017-07-07) ------------------- +=================== * :py:meth:`ReferenceField.from_mongo ` try to get document from primary if not found by default. 1.4.9 (2017-07-06) ------------------- +=================== * Add :py:meth:`QuerySet.read_primary ` method for simple setup ``pymongo.read_preference.Primary``. 1.4.4 (2017-05-17) ------------------- +=================== * Add :py:class:`TimedeltaField ` for stores durations; * Add :py:class:`SimpleEmbeddedDocumentField ` @@ -115,20 +265,20 @@ CHANGES 1.4.3 (2017-05-14) ------------------- +=================== * Add :py:class:`StaticField ` for static data. 1.4.2 (2017-04-09) ------------------- +=================== * Additional arguments (like ``write_concern``) for write operations; * :py:func:`create_fake ` save the documents with write concern "majority" by default. 1.4.0 (2017-04-05) ------------------- +=================== * Drop pymongo 2 support; * Additional options for databases and collections; @@ -139,13 +289,13 @@ CHANGES 1.3.1 (2017-02-21) ------------------- +=================== * Change raw data for :py:class:`Money `; 1.3.0 (2017-02-19) ------------------- +=================== * Add currency support to :py:class:`Money `: - Totaly rewrite :py:class:`Money ` type. Now it is not subclass of :py:class:`Decimal`; @@ -153,13 +303,13 @@ CHANGES 1.2.1 (2017-01-19) ------------------- +=================== * Add :py:meth:`QuerySet.find_in ` for ``$in`` queries with specified order; 1.2.0 (2016-12-27) ------------------- +=================== * Drop MongoDB 2.X support; * Objects for update and remove results; @@ -167,7 +317,7 @@ CHANGES 1.1.4 (2016-08-20) ------------------- +=================== * Add some features to :py:module:`Bulk `: - :py:meth:`Bulk.update_one(document, **kw) `: method for add update one document in bulk; @@ -177,7 +327,7 @@ CHANGES 1.1.3 (2016-07-23) ------------------- +=================== * Add :py:meth:`QuerySet.ids ` method for get only documents id's from queryset; @@ -188,7 +338,7 @@ CHANGES 1.1 (2016-04-26) ----------------- +================ * Add cacheing on queryset level and use it for :py:class:`ReferenceField `; @@ -208,7 +358,7 @@ CHANGES 1.0 (2015-11-14) ----------------- +================ * Change document structure. No more bad :py:attr:`BaseDocument.__data__ ` attribute: - :py:attr:`BaseDocument.__raw__ `: raw data from mongo;