Squashed commit of the following:

commit 48f988acd728f1193b57df8cf6b0154d69c15099
Merge: 6526923 1304f27
Author: Ross Lawley <ross.lawley@gmail.com>
Date:   Thu Jul 26 08:17:45 2012 -0700

    Merge pull request #44 from faulkner/fix-notes

    Proper syntax for RST notes (so they actually render).

commit 6526923345efd768044c4fba770a8a3ada161a40
Author: Ross Lawley <ross.lawley@gmail.com>
Date:   Thu Jul 26 16:00:32 2012 +0100

    Fixed recursion loading bug in _get_changed_fields

    fixes hmarr/mongoengine#548

commit 24fd1acce68341361331e033d1692d61a936f871
Author: Ross Lawley <ross.lawley@gmail.com>
Date:   Thu Jul 26 14:14:10 2012 +0100

    Version bump

commit cbb9235dc5d863ee0bb8a315c976581e71b6a641
Merge: 6459d4c 19ec2c9
Author: Ross Lawley <ross.lawley@gmail.com>
Date:   Wed Jul 25 15:12:34 2012 +0100

    Merge branch 'master' of github.com:hmarr/mongoengine

commit 19ec2c9bc98db454680e681373f3fcd3b0f79a6c
Merge: 3070e0b 601f0eb
Author: Ross Lawley <ross.lawley@gmail.com>
Date:   Wed Jul 25 07:12:07 2012 -0700

    Merge pull request #545 from maxcountryman/patch-1

    Correcting typo in DynamicField docstring

commit 6459d4c0b60229edcd4d562898833f221d00ebf4
Author: Ross Lawley <ross.lawley@gmail.com>
Date:   Wed Jul 25 14:55:10 2012 +0100

    Fixed issue with custom queryset manager expecting explict variable names

    If using / expecting kwargs you have to call the queryset manager
    explicitly.

commit 1304f2721f7850b223950edb85ec7c141255176c
Author: Chris Faulkner <thefaulkner@gmail.com>
Date:   Tue Jul 24 14:06:43 2012 -0700

    Proper syntax for RST notes (so they actually render).

commit 598ffd3e5c107990dd41995d0bbb21bf13f67fb5
Author: Ross Lawley <ross.lawley@gmail.com>
Date:   Mon Jul 23 15:32:02 2012 +0100

    Fixed documentation

commit 601f0eb1682ebd9c9eb67f1b68b76bdd2cf3d8c7
Author: Max Countryman <maxc@me.com>
Date:   Fri Jul 20 19:12:43 2012 -0700

    Correcting typo in DynamicField docstring
This commit is contained in:
Ross Lawley 2012-07-26 22:50:39 +01:00
parent 1a4533a9cf
commit 3628a7653c
9 changed files with 130 additions and 32 deletions

View File

@ -3,6 +3,14 @@ Changelog
========= =========
Changes in 0.6.18
=================
- Fixed recursion loading bug in _get_changed_fields
Changes in 0.6.17
=================
- Fixed issue with custom queryset manager expecting explict variable names
Changes in 0.6.16 Changes in 0.6.16
================= =================
- Fixed issue where db_alias wasn't inherited - Fixed issue where db_alias wasn't inherited
@ -27,7 +35,7 @@ Changes in 0.6.14
- Added support for add_to_set and each - Added support for add_to_set and each
Changes in 0.6.13 Changes in 0.6.13
================ =================
- Fixed EmbeddedDocument db_field validation issue - Fixed EmbeddedDocument db_field validation issue
- Fixed StringField unicode issue - Fixed StringField unicode issue
- Fixes __repr__ modifying the cursor - Fixes __repr__ modifying the cursor

View File

@ -259,6 +259,35 @@ as the constructor's argument::
content = StringField() content = StringField()
.. _one-to-many-with-listfields:
One to Many with ListFields
'''''''''''''''''''''''''''
If you are implementing a one to many relationship via a list of references,
then the references are stored as DBRefs and to query you need to pass an
instance of the object to the query::
class User(Document):
name = StringField()
class Page(Document):
content = StringField()
authors = ListField(ReferenceField(User))
bob = User(name="Bob Jones").save()
john = User(name="John Smith").save()
Page(content="Test Page", authors=[bob, john]).save()
Page(content="Another Page", authors=[john]).save()
# Find all pages Bob authored
Page.objects(authors__in=[bob])
# Find all pages that both Bob and John have authored
Page.objects(authors__all=[bob, john])
Dealing with deletion of referred documents Dealing with deletion of referred documents
''''''''''''''''''''''''''''''''''''''''''' '''''''''''''''''''''''''''''''''''''''''''
By default, MongoDB doesn't check the integrity of your data, so deleting By default, MongoDB doesn't check the integrity of your data, so deleting

View File

@ -12,7 +12,7 @@ from signals import *
__all__ = (document.__all__ + fields.__all__ + connection.__all__ + __all__ = (document.__all__ + fields.__all__ + connection.__all__ +
queryset.__all__ + signals.__all__) queryset.__all__ + signals.__all__)
VERSION = (0, 6, 16) VERSION = (0, 6, 18)
def get_version(): def get_version():

View File

@ -1017,9 +1017,10 @@ Invalid data to create a `%s` instance.\n%s""".strip() % (cls._class_name, error
field_list.update(self._dynamic_fields) field_list.update(self._dynamic_fields)
for field_name in field_list: for field_name in field_list:
db_field_name = self._db_field_map.get(field_name, field_name) db_field_name = self._db_field_map.get(field_name, field_name)
key = '%s.' % db_field_name key = '%s.' % db_field_name
field = getattr(self, field_name, None) field = self._data.get(field_name, None)
if hasattr(field, 'id'): if hasattr(field, 'id'):
if field.id in inspected: if field.id in inspected:
continue continue

View File

@ -375,7 +375,7 @@ class DynamicDocument(Document):
:class:`~mongoengine.DynamicField` and data can be attributed to that :class:`~mongoengine.DynamicField` and data can be attributed to that
field. field.
..note:: .. note::
There is one caveat on Dynamic Documents: fields cannot start with `_` There is one caveat on Dynamic Documents: fields cannot start with `_`
""" """

View File

@ -451,7 +451,7 @@ class GenericEmbeddedDocumentField(BaseField):
Only valid values are subclasses of :class:`~mongoengine.EmbeddedDocument`. Only valid values are subclasses of :class:`~mongoengine.EmbeddedDocument`.
..note :: You can use the choices param to limit the acceptable .. note:: You can use the choices param to limit the acceptable
EmbeddedDocument types EmbeddedDocument types
""" """
@ -483,7 +483,7 @@ class GenericEmbeddedDocumentField(BaseField):
class DynamicField(BaseField): class DynamicField(BaseField):
"""A tryly dynamic field type capable of handling different and varying """A truly dynamic field type capable of handling different and varying
types of data. types of data.
Used by :class:`~mongoengine.DynamicDocument` to handle dynamic data""" Used by :class:`~mongoengine.DynamicDocument` to handle dynamic data"""
@ -530,6 +530,8 @@ class ListField(ComplexBaseField):
"""A list field that wraps a standard field, allowing multiple instances """A list field that wraps a standard field, allowing multiple instances
of the field to be used as a list in the database. of the field to be used as a list in the database.
If using with ReferenceFields see: :ref:`one-to-many-with-listfields`
.. note:: .. note::
Required means it cannot be empty - as the default for ListFields is [] Required means it cannot be empty - as the default for ListFields is []
""" """
@ -766,10 +768,10 @@ class GenericReferenceField(BaseField):
"""A reference to *any* :class:`~mongoengine.document.Document` subclass """A reference to *any* :class:`~mongoengine.document.Document` subclass
that will be automatically dereferenced on access (lazily). that will be automatically dereferenced on access (lazily).
..note :: Any documents used as a generic reference must be registered in the .. note:: Any documents used as a generic reference must be registered in the
document registry. Importing the model will automatically register it. document registry. Importing the model will automatically register it.
..note :: You can use the choices param to limit the acceptable Document types .. note:: You can use the choices param to limit the acceptable Document types
.. versionadded:: 0.3 .. versionadded:: 0.3
""" """

View File

@ -806,9 +806,9 @@ class QuerySet(object):
keyword argument called :attr:`defaults`. keyword argument called :attr:`defaults`.
.. note:: This requires two separate operations and therefore a .. note:: This requires two separate operations and therefore a
race condition exists. Because there are no transactions in mongoDB race condition exists. Because there are no transactions in mongoDB
other approaches should be investigated, to ensure you don't other approaches should be investigated, to ensure you don't
accidently duplicate data when using this method. accidently duplicate data when using this method.
:param write_options: optional extra keyword arguments used if we :param write_options: optional extra keyword arguments used if we
have to create a new document. have to create a new document.
@ -816,8 +816,8 @@ class QuerySet(object):
:param auto_save: if the object is to be saved automatically if not found. :param auto_save: if the object is to be saved automatically if not found.
.. versionchanged:: 0.6 - added `auto_save`
.. versionadded:: 0.3 .. versionadded:: 0.3
.. versionupdated:: 0.6 - added `auto_save`
""" """
defaults = query.get('defaults', {}) defaults = query.get('defaults', {})
if 'defaults' in query: if 'defaults' in query:
@ -1882,9 +1882,9 @@ class QuerySetManager(object):
queryset = queryset_class(owner, owner._get_collection()) queryset = queryset_class(owner, owner._get_collection())
if self.get_queryset: if self.get_queryset:
var_names = self.get_queryset.func_code.co_varnames var_names = self.get_queryset.func_code.co_varnames
if var_names == ('queryset',): if len(var_names) == 1:
queryset = self.get_queryset(queryset) queryset = self.get_queryset(queryset)
elif var_names == ('doc_cls', 'queryset',): elif len(var_names) == 2:
queryset = self.get_queryset(owner, queryset) queryset = self.get_queryset(owner, queryset)
else: else:
queryset = partial(self.get_queryset, owner, queryset) queryset = partial(self.get_queryset, owner, queryset)

View File

@ -5,7 +5,7 @@
%define srcname mongoengine %define srcname mongoengine
Name: python-%{srcname} Name: python-%{srcname}
Version: 0.6.16 Version: 0.6.18
Release: 1%{?dist} Release: 1%{?dist}
Summary: A Python Document-Object Mapper for working with MongoDB Summary: A Python Document-Object Mapper for working with MongoDB

View File

@ -579,6 +579,64 @@ class QuerySetTest(unittest.TestCase):
Blog.objects.insert([blog2, blog3], write_options={'continue_on_error': True}) Blog.objects.insert([blog2, blog3], write_options={'continue_on_error': True})
self.assertEqual(Blog.objects.count(), 3) self.assertEqual(Blog.objects.count(), 3)
def test_get_changed_fields_query_count(self):
class Person(Document):
name = StringField()
owns = ListField(ReferenceField('Organization'))
projects = ListField(ReferenceField('Project'))
class Organization(Document):
name = StringField()
owner = ReferenceField('Person')
employees = ListField(ReferenceField('Person'))
class Project(Document):
name = StringField()
Person.drop_collection()
Organization.drop_collection()
Project.drop_collection()
r1 = Project(name="r1").save()
r2 = Project(name="r2").save()
r3 = Project(name="r3").save()
p1 = Person(name="p1", projects=[r1, r2]).save()
p2 = Person(name="p2", projects=[r2]).save()
o1 = Organization(name="o1", employees=[p1]).save()
with query_counter() as q:
self.assertEqual(q, 0)
fresh_o1 = Organization.objects.get(id=o1.id)
self.assertEqual(1, q)
fresh_o1._get_changed_fields()
self.assertEqual(1, q)
with query_counter() as q:
self.assertEqual(q, 0)
fresh_o1 = Organization.objects.get(id=o1.id)
fresh_o1.save()
self.assertEquals(q, 2)
with query_counter() as q:
self.assertEqual(q, 0)
fresh_o1 = Organization.objects.get(id=o1.id)
fresh_o1.save(cascade=False)
self.assertEquals(q, 2)
with query_counter() as q:
self.assertEqual(q, 0)
fresh_o1 = Organization.objects.get(id=o1.id)
fresh_o1.employees.append(p2)
fresh_o1.save(cascade=False)
self.assertEquals(q, 3)
def test_slave_okay(self): def test_slave_okay(self):
"""Ensures that a query can take slave_okay syntax """Ensures that a query can take slave_okay syntax
@ -2228,28 +2286,28 @@ class QuerySetTest(unittest.TestCase):
date = DateTimeField(default=datetime.now) date = DateTimeField(default=datetime.now)
@queryset_manager @queryset_manager
def objects(doc_cls, queryset): def objects(cls, qryset):
return queryset(deleted=False) return qryset(deleted=False)
@queryset_manager @queryset_manager
def music_posts(doc_cls, queryset): def music_posts(doc_cls, queryset, deleted=False):
return queryset(tags='music', deleted=False).order_by('-date') return queryset(tags='music',
deleted=deleted).order_by('date')
BlogPost.drop_collection() BlogPost.drop_collection()
post1 = BlogPost(tags=['music', 'film']) post1 = BlogPost(tags=['music', 'film']).save()
post1.save() post2 = BlogPost(tags=['music']).save()
post2 = BlogPost(tags=['music']) post3 = BlogPost(tags=['film', 'actors']).save()
post2.save() post4 = BlogPost(tags=['film', 'actors', 'music'], deleted=True).save()
post3 = BlogPost(tags=['film', 'actors'])
post3.save()
post4 = BlogPost(tags=['film', 'actors'], deleted=True)
post4.save()
self.assertEqual([p.id for p in BlogPost.objects], self.assertEqual([p.id for p in BlogPost.objects()],
[post1.id, post2.id, post3.id]) [post1.id, post2.id, post3.id])
self.assertEqual([p.id for p in BlogPost.music_posts], self.assertEqual([p.id for p in BlogPost.music_posts()],
[post2.id, post1.id]) [post1.id, post2.id])
self.assertEqual([p.id for p in BlogPost.music_posts(True)],
[post4.id])
BlogPost.drop_collection() BlogPost.drop_collection()