queryset_manager funcs now accept doc as arg

This commit is contained in:
Harry Marr 2010-01-23 17:16:01 +00:00
parent 470e08f616
commit 2585f1b724
5 changed files with 69 additions and 29 deletions

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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)

View File

@ -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()