Implementation and tests for exec_js field substitution
This commit is contained in:
parent
200e9eca92
commit
d0e0b291df
@ -1,6 +1,7 @@
|
||||
from connection import _get_db
|
||||
|
||||
import pymongo
|
||||
import re
|
||||
import copy
|
||||
|
||||
|
||||
@ -593,6 +594,21 @@ class QuerySet(object):
|
||||
def __iter__(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):
|
||||
"""Execute a Javascript function on the server. A list of fields may be
|
||||
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
|
||||
(accessed in Javascript through the ``options`` object)
|
||||
"""
|
||||
code = self._sub_js_fields(code)
|
||||
|
||||
fields = [QuerySet._translate_field_name(self._document, f)
|
||||
for f in fields]
|
||||
collection = self._document._meta['collection']
|
||||
|
@ -387,6 +387,58 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
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):
|
||||
"""Ensure that documents are properly deleted from the database.
|
||||
"""
|
||||
|
Loading…
x
Reference in New Issue
Block a user