Implementation and tests for exec_js field substitution

This commit is contained in:
Harry Marr 2010-02-25 17:20:52 +00:00
parent 200e9eca92
commit d0e0b291df
2 changed files with 70 additions and 0 deletions

View File

@ -1,6 +1,7 @@
from connection import _get_db from connection import _get_db
import pymongo import pymongo
import re
import copy import copy
@ -593,6 +594,21 @@ class QuerySet(object):
def __iter__(self): def __iter__(self):
return self return self
def _sub_js_fields(self, code):
"""When fields are specified with [~fieldname] syntax, where
*fieldname* is the Python name of a field, *fieldname* will be
substituted for the MongoDB name of the field (specified using the
:attr:`name` keyword argument in a field's constructor).
"""
def field_sub(match):
# Extract just the field name, and look up the field objects
field_name = match.group(1).split('.')
fields = QuerySet._lookup_field(self._document, field_name)
# Substitute the correct name for the field into the javascript
return '["%s"]' % fields[-1].name
return re.sub('\[\s*~([A-z_][A-z_0-9.]+?)\s*\]', field_sub, code)
def exec_js(self, code, *fields, **options): def exec_js(self, code, *fields, **options):
"""Execute a Javascript function on the server. A list of fields may be """Execute a Javascript function on the server. A list of fields may be
provided, which will be translated to their correct names and supplied provided, which will be translated to their correct names and supplied
@ -608,6 +624,8 @@ class QuerySet(object):
:param options: options that you want available to the function :param options: options that you want available to the function
(accessed in Javascript through the ``options`` object) (accessed in Javascript through the ``options`` object)
""" """
code = self._sub_js_fields(code)
fields = [QuerySet._translate_field_name(self._document, f) fields = [QuerySet._translate_field_name(self._document, f)
for f in fields] for f in fields]
collection = self._document._meta['collection'] collection = self._document._meta['collection']

View File

@ -387,6 +387,58 @@ class QuerySetTest(unittest.TestCase):
BlogPost.drop_collection() BlogPost.drop_collection()
def test_exec_js_field_sub(self):
"""Ensure that field substitutions occur properly in exec_js functions.
"""
class Comment(EmbeddedDocument):
content = StringField(name='body')
class BlogPost(Document):
name = StringField(name='doc-name')
comments = ListField(EmbeddedDocumentField(Comment), name='cmnts')
BlogPost.drop_collection()
comments1 = [Comment(content='cool'), Comment(content='yay')]
post1 = BlogPost(name='post1', comments=comments1)
post1.save()
comments2 = [Comment(content='nice stuff')]
post2 = BlogPost(name='post2', comments=comments2)
post2.save()
code = """
function getComments() {
var comments = [];
db[collection].find(query).forEach(function(doc) {
var docComments = doc[~comments];
for (var i = 0; i < docComments.length; i++) {
comments.push({
'document': doc[~name],
'comment': doc[~comments][i][~comments.content]
});
}
});
return comments;
}
"""
sub_code = BlogPost.objects._sub_js_fields(code)
code_chunks = ['doc["cmnts"];', 'doc["doc-name"],',
'doc["cmnts"][i]["body"]']
for chunk in code_chunks:
self.assertTrue(chunk in sub_code)
results = BlogPost.objects.exec_js(code)
expected_results = [
{u'comment': u'cool', u'document': u'post1'},
{u'comment': u'yay', u'document': u'post1'},
{u'comment': u'nice stuff', u'document': u'post2'},
]
self.assertEqual(results, expected_results)
BlogPost.drop_collection()
def test_delete(self): def test_delete(self):
"""Ensure that documents are properly deleted from the database. """Ensure that documents are properly deleted from the database.
""" """