Implementation and tests for exec_js field substitution
This commit is contained in:
		@@ -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.
 | 
			
		||||
        """
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user