diff --git a/docs/guide/defining-documents.rst b/docs/guide/defining-documents.rst index 7683387e..0a12cb6d 100644 --- a/docs/guide/defining-documents.rst +++ b/docs/guide/defining-documents.rst @@ -44,7 +44,7 @@ are as follows: * :class:`~mongoengine.ReferenceField` List fields -^^^^^^^^^^^ +----------- MongoDB allows the storage of lists of items. To add a list of items to a :class:`~mongoengine.Document`, use the :class:`~mongoengine.ListField` field type. :class:`~mongoengine.ListField` takes another field object as its first @@ -54,7 +54,7 @@ argument, which specifies which type elements may be stored within the list:: tags = ListField(StringField(max_length=50)) Embedded documents -^^^^^^^^^^^^^^^^^^ +------------------ MongoDB has the ability to embed documents within other documents. Schemata may be defined for these embedded documents, just as they may be for regular documents. To create an embedded document, just define a document as usual, but @@ -76,7 +76,7 @@ document class as the first argument:: page = Page(comments=[comment1, comment2]) Reference fields -^^^^^^^^^^^^^^^^ +---------------- References may be stored to other documents in the database using the :class:`~mongoengine.ReferenceField`. Pass in another document class as the first argument to the constructor, then simply assign document objects to the @@ -100,7 +100,7 @@ The :class:`User` object is automatically turned into a reference behind the scenes, and dereferenced when the :class:`Page` object is retrieved. Uniqueness constraints -^^^^^^^^^^^^^^^^^^^^^^ +---------------------- MongoEngine allows you to specify that a field should be unique across a collection by providing ``unique=True`` to a :class:`~mongoengine.Field`\ 's constructor. If you try to save a document that has the same value for a unique @@ -130,7 +130,7 @@ document class to use:: meta = {'collection': 'cmsPage'} Capped collections -^^^^^^^^^^^^^^^^^^ +------------------ A :class:`~mongoengine.Document` may use a **Capped Collection** by specifying :attr:`max_documents` and :attr:`max_size` in the :attr:`meta` dictionary. :attr:`max_documents` is the maximum number of documents that is allowed to be @@ -179,13 +179,13 @@ subsequent calls to :meth:`~mongoengine.queryset.QuerySet.order_by`. :: } blog_post_1 = BlogPost(title="Blog Post #1") - blog_post_1.published_date = datetime(2010, 1, 5, 0, 0 ,0)) + blog_post_1.published_date = datetime(2010, 1, 5, 0, 0 ,0) blog_post_2 = BlogPost(title="Blog Post #2") - blog_post_2.published_date = datetime(2010, 1, 6, 0, 0 ,0)) + blog_post_2.published_date = datetime(2010, 1, 6, 0, 0 ,0) blog_post_3 = BlogPost(title="Blog Post #3") - blog_post_3.published_date = datetime(2010, 1, 7, 0, 0 ,0)) + blog_post_3.published_date = datetime(2010, 1, 7, 0, 0 ,0) blog_post_1.save() blog_post_2.save() @@ -194,11 +194,11 @@ subsequent calls to :meth:`~mongoengine.queryset.QuerySet.order_by`. :: # get the "first" BlogPost using default ordering # from BlogPost.meta.ordering latest_post = BlogPost.objects.first() - self.assertEqual(latest_post.title, "Blog Post #3") + assert latest_post.title == "Blog Post #3" # override default ordering, order BlogPosts by "published_date" first_post = BlogPost.objects.order_by("+published_date").first() - self.assertEqual(first_post.title, "Blog Post #1") + assert first_post.title == "Blog Post #1" Document inheritance ==================== @@ -218,7 +218,7 @@ convenient and efficient retrieval of related documents:: date = DateTimeField() Working with existing data -^^^^^^^^^^^^^^^^^^^^^^^^^^ +-------------------------- To enable correct retrieval of documents involved in this kind of heirarchy, two extra attributes are stored on each document in the database: :attr:`_cls` and :attr:`_types`. These are hidden from the user through the MongoEngine diff --git a/docs/guide/document-instances.rst b/docs/guide/document-instances.rst index 756bc3d5..b5a1f029 100644 --- a/docs/guide/document-instances.rst +++ b/docs/guide/document-instances.rst @@ -17,7 +17,7 @@ attribute syntax:: 'Example Page' Saving and deleting documents ------------------------------ +============================= To save the document to the database, call the :meth:`~mongoengine.Document.save` method. If the document does not exist in the database, it will be created. If it does already exist, it will be @@ -31,7 +31,7 @@ valide :attr:`id`. :ref:`guide-atomic-updates` Document IDs ------------- +============ Each document in the database has a unique id. This may be accessed through the :attr:`id` attribute on :class:`~mongoengine.Document` objects. Usually, the id will be generated automatically by the database server when the object is save, diff --git a/docs/guide/querying.rst b/docs/guide/querying.rst index 3b700322..10712047 100644 --- a/docs/guide/querying.rst +++ b/docs/guide/querying.rst @@ -14,7 +14,7 @@ fetch documents from the database:: print user.name Filtering queries ------------------ +================= The query may be filtered by calling the :class:`~mongoengine.queryset.QuerySet` object with field lookup keyword arguments. The keys in the keyword arguments correspond to fields on the @@ -33,7 +33,7 @@ syntax:: uk_pages = Page.objects(author__country='uk') Querying lists -^^^^^^^^^^^^^^ +-------------- On most fields, this syntax will look up documents where the field specified matches the given value exactly, but when the field refers to a :class:`~mongoengine.ListField`, a single item may be provided, in which case @@ -47,7 +47,7 @@ lists that contain that item will be matched:: Page.objects(tags='coding') Query operators ---------------- +=============== Operators other than equality may also be used in queries; just attach the operator name to a key with a double-underscore:: @@ -69,7 +69,7 @@ Available operators are as follows: * ``exists`` -- value for field exists Limiting and skipping results ------------------------------ +============================= Just as with traditional ORMs, you may limit the number of results returned, or skip a number or results in you query. :meth:`~mongoengine.queryset.QuerySet.limit` and @@ -86,15 +86,54 @@ achieving this is using array-slicing syntax:: # 5 users, starting from the 10th user found users = User.objects[10:15] +Default Document queries +======================== +By default, the objects :attr:`~mongoengine.Document.objects` attribute on a +document returns a :class:`~mongoengine.queryset.QuerySet` that doesn't filter +the collection -- it returns all objects. This may be changed by defining a +method on a document that modifies a queryset. The method should accept two +arguments -- :attr:`doc_cls` and :attr:`queryset`. The first argument is the +:class:`~mongoengine.Document` class that the method is defined on (in this +sense, the method is more like a :func:`classmethod` than a regular method), +and the second argument is the initial queryset. The method needs to be +decorated with :func:`~mongoengine.queryset.queryset_manager` in order for it +to be recognised. :: + + class BlogPost(Document): + title = StringField() + date = DateTimeField() + + @queryset_manager + def objects(doc_cls, queryset): + # This may actually also be done by defining a default ordering for + # the document, but this illustrates the use of manager methods + return queryset.order_by('-date') + +You don't need to call your method :attr:`objects` -- you may define as many +custom manager methods as you like:: + + class BlogPost(Document): + title = StringField() + published = BooleanField() + + @queryset_manager + def live_posts(doc_cls, queryset): + return queryset.order_by('-date') + + BlogPost(title='test1', published=False).save() + BlogPost(title='test2', published=True).save() + assert len(BlogPost.objects) == 2 + assert len(BlogPost.live_posts) == 1 + Aggregation ------------ +=========== MongoDB provides some aggregation methods out of the box, but there are not as many as you typically get with an RDBMS. MongoEngine provides a wrapper around the built-in methods and provides some of its own, which are implemented as Javascript code that is executed on the database server. Counting results -^^^^^^^^^^^^^^^^ +---------------- Just as with limiting and skipping results, there is a method on :class:`~mongoengine.queryset.QuerySet` objects -- :meth:`~mongoengine.queryset.QuerySet.count`, but there is also a more Pythonic @@ -103,7 +142,7 @@ way of achieving this:: num_users = len(User.objects) Further aggregation -^^^^^^^^^^^^^^^^^^^ +------------------- You may sum over the values of a specific field on documents using :meth:`~mongoengine.queryset.QuerySet.sum`:: @@ -133,7 +172,7 @@ would be generating "tag-clouds":: top_tags = sorted(tag_freqs.items(), key=itemgetter(1), reverse=True)[:10] Advanced queries ----------------- +================ Sometimes calling a :class:`~mongoengine.queryset.QuerySet` object with keyword arguments can't fully express the query you want to use -- for example if you need to combine a number of constraints using *and* and *or*. This is made @@ -161,7 +200,7 @@ calling it with keyword arguments:: .. _guide-atomic-updates: Atomic updates --------------- +============== Documents may be updated atomically by using the :meth:`~mongoengine.queryset.QuerySet.update_one` and :meth:`~mongoengine.queryset.QuerySet.update` methods on a diff --git a/mongoengine/queryset.py b/mongoengine/queryset.py index bb0090ea..6c8572c6 100644 --- a/mongoengine/queryset.py +++ b/mongoengine/queryset.py @@ -660,14 +660,15 @@ class QuerySetManager(object): # owner is the document that contains the QuerySetManager queryset = QuerySet(owner, self._collection) if self._manager_func: - queryset = self._manager_func(queryset) + queryset = self._manager_func(owner, queryset) return queryset def queryset_manager(func): - """Decorator that allows you to define custom QuerySet managers on + """Decorator that allows you to define custom QuerySet managers on :class:`~mongoengine.Document` classes. The manager must be a function that - accepts a :class:`~mongoengine.queryset.QuerySet` as its only argument, and - returns a :class:`~mongoengine.queryset.QuerySet`, probably the same one - but modified in some way. + accepts a :class:`~mongoengine.Document` class as its first argument, and a + :class:`~mongoengine.queryset.QuerySet` as its second argument. The method + function should return a :class:`~mongoengine.queryset.QuerySet`, probably + the same one that was passed in, but modified in some way. """ return QuerySetManager(func) diff --git a/tests/queryset.py b/tests/queryset.py index 10507e00..1e06615a 100644 --- a/tests/queryset.py +++ b/tests/queryset.py @@ -146,7 +146,7 @@ class QuerySetTest(unittest.TestCase): published_date = DateTimeField() @queryset_manager - def published(queryset): + def published(doc_cls, queryset): return queryset(is_published=True) blog_post_1 = BlogPost(title="Blog Post #1", @@ -444,7 +444,7 @@ class QuerySetTest(unittest.TestCase): tags = ListField(StringField()) @queryset_manager - def music_posts(queryset): + def music_posts(doc_cls, queryset): return queryset(tags='music') BlogPost.drop_collection()