Skip to content

Commit 6363387

Browse files
committed
Add basic updates handler support.
1 parent 9690aa3 commit 6363387

File tree

3 files changed

+96
-6
lines changed

3 files changed

+96
-6
lines changed

couchdb/tests/view.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,57 @@ def test_reduce_empty(self):
102102
self.assertEqual(output.getvalue(),
103103
b'[true, [0]]\n')
104104

105+
def test_update(self):
106+
import json
107+
commands = [
108+
[
109+
'ddoc',
110+
'new',
111+
'_design/test_update',
112+
{
113+
'_id': '_design/test_update',
114+
'_rev': '8-d7379de23a751dc2a19e5638a7bbc5cc',
115+
'language': 'python',
116+
'updates': {
117+
'inc': {
118+
'map': '''\
119+
def fun(obj, req):
120+
if obj is not None:
121+
obj['field'] += 1
122+
return [obj, {"body": "."}]
123+
''',
124+
}
125+
}
126+
},
127+
],
128+
[
129+
'ddoc',
130+
'_design/test_update',
131+
['updates', 'inc'],
132+
[None, {}]
133+
],
134+
[
135+
'ddoc',
136+
'_design/test_update',
137+
['updates', 'inc'],
138+
[{'field': 41, 'other_field': 'x'}, {}]
139+
],
140+
]
141+
input = StringIO(b'\n'.join(json.dumps(c).encode('utf-8')
142+
for c in commands))
143+
output = StringIO()
144+
view.run(input=input, output=output)
145+
results = [
146+
json.loads(l.decode('utf-8'))
147+
for l in output.getvalue().strip().split(b'\n')
148+
]
149+
self.assertEqual(len(results), 3)
150+
self.assertEqual(results[0], True)
151+
self.assertEqual(results[1], ['up', None, {'body': '.'}])
152+
self.assertEqual(
153+
results[2],
154+
['up', {'field': 42, 'other_field': 'x'}, {'body': '.'}])
155+
105156

106157
def suite():
107158
suite = unittest.TestSuite()

couchdb/view.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
log = logging.getLogger('couchdb.view')
2525

26+
ddocs = {}
2627

2728
def run(input=sys.stdin, output=sys.stdout):
2829
r"""CouchDB view function handler implementation for Python.
@@ -135,8 +136,33 @@ def rereduce(*cmd):
135136
# Note: weird kwargs is for Python 2.5 compat
136137
return reduce(*cmd, **{'rereduce': True})
137138

138-
handlers = {'reset': reset, 'add_fun': add_fun, 'map_doc': map_doc,
139-
'reduce': reduce, 'rereduce': rereduce}
139+
def ddoc(*cmd):
140+
if cmd[0] == 'new':
141+
ddoc = cmd[2]
142+
ddoc['updates'] = dict(
143+
(name, {'map': compile_fun(value['map'])})
144+
for name, value in ddoc['updates'].items())
145+
ddocs[cmd[1]] = ddoc
146+
return True
147+
else:
148+
ddoc = ddocs[cmd[0]]
149+
action = cmd[1]
150+
if action[0] == 'updates':
151+
fun = ddoc['updates'][action[1]]['map']
152+
doc, body = fun(*cmd[2])
153+
res = ['up', doc, body]
154+
sys.stderr.flush()
155+
return res
156+
157+
158+
handlers = {
159+
'add_fun': add_fun,
160+
'ddoc': ddoc,
161+
'map_doc': map_doc,
162+
'reduce': reduce,
163+
'rereduce': rereduce,
164+
'reset': reset,
165+
}
140166

141167
try:
142168
while True:

doc/views.rst

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
Writing views in Python
22
=======================
33

4-
The couchdb-python package comes with a view server to allow you to write
5-
views in Python instead of JavaScript. When couchdb-python is installed, it
6-
will install a script called couchpy that runs the view server. To enable
7-
this for your CouchDB server, add the following section to local.ini::
4+
The couchdb-python package comes with a query server to allow you to
5+
write views or update handlers in Python instead of JavaScript. When
6+
couchdb-python is installed, it will install a script called couchpy
7+
that runs the view server. To enable this for your CouchDB server, add
8+
the following section to local.ini::
89

910
[query_servers]
1011
python=/usr/bin/couchpy
@@ -18,3 +19,15 @@ the language pull-down menu. Here's some sample view code to get you started::
1819

1920
Note that the ``map`` function uses the Python ``yield`` keyword to emit
2021
values, where JavaScript views use an ``emit()`` function.
22+
23+
Here's an example update handler code that will increment the
24+
``field`` member of an existing document. The name of the ``field`` to
25+
increment is passed in the query string::
26+
27+
def fun(obj, req):
28+
field = req.query['field']
29+
if obj is not None and field in obj:
30+
obj[field] += 1
31+
return [obj, {"body": "incremented"}]
32+
else:
33+
return [None, {"body": "no such document or field"}]

0 commit comments

Comments
 (0)