79
79
80
80
class Field (object ):
81
81
"""Basic unit for mapping a piece of data between Python and JSON.
82
-
82
+
83
83
Instances of this class can be added to subclasses of `Document` to describe
84
84
the mapping of a document.
85
85
"""
@@ -191,7 +191,7 @@ def _to_json(self, value):
191
191
class ViewField (object ):
192
192
r"""Descriptor that can be used to bind a view definition to a property of
193
193
a `Document` class.
194
-
194
+
195
195
>>> class Person(Document):
196
196
... name = TextField()
197
197
... age = IntegerField()
@@ -201,51 +201,73 @@ class ViewField(object):
201
201
... }''')
202
202
>>> Person.by_name
203
203
<ViewDefinition '_design/people/_view/by_name'>
204
-
204
+
205
205
>>> print(Person.by_name.map_fun)
206
206
function(doc) {
207
207
emit(doc.name, doc);
208
208
}
209
-
209
+
210
210
That property can be used as a function, which will execute the view.
211
-
211
+
212
212
>>> from couchdb import Database
213
213
>>> db = Database('python-tests')
214
-
214
+
215
215
>>> Person.by_name(db, count=3)
216
216
<ViewResults <PermanentView '_design/people/_view/by_name'> {'count': 3}>
217
-
217
+
218
218
The results produced by the view are automatically wrapped in the
219
219
`Document` subclass the descriptor is bound to. In this example, it would
220
220
return instances of the `Person` class. But please note that this requires
221
221
the values of the view results to be dictionaries that can be mapped to the
222
222
mapping defined by the containing `Document` class. Alternatively, the
223
223
``include_docs`` query option can be used to inline the actual documents in
224
224
the view results, which will then be used instead of the values.
225
-
225
+
226
226
If you use Python view functions, this class can also be used as a
227
227
decorator:
228
-
228
+
229
229
>>> class Person(Document):
230
230
... name = TextField()
231
231
... age = IntegerField()
232
232
...
233
233
... @ViewField.define('people')
234
234
... def by_name(doc):
235
235
... yield doc['name'], doc
236
-
236
+
237
237
>>> Person.by_name
238
238
<ViewDefinition '_design/people/_view/by_name'>
239
239
240
240
>>> print(Person.by_name.map_fun)
241
241
def by_name(doc):
242
242
yield doc['name'], doc
243
+
244
+ Alternatively, a map and reduce function can be used explicitly. The results
245
+ with a reduce function will always return the actual documents rather than
246
+ the results mapped by the containing `Document` class.
247
+
248
+ >>> class Item(Document):
249
+ ... sku = TextField()
250
+ ... color = IntegerField()
251
+ ...
252
+ ... stock = ViewField('people')
253
+ ...
254
+ ... @stock.map
255
+ ... def map(doc):
256
+ ... yield doc['sku'], doc)
257
+ ...
258
+ ... @stock.reduce
259
+ ... def reduce(keys, values, rereduce):
260
+ ... if rereduce:
261
+ ... yield sum(values)
262
+ ... else:
263
+ ... yield len(values)
264
+
243
265
"""
244
266
245
- def __init__ (self , design , map_fun , reduce_fun = None , name = None ,
267
+ def __init__ (self , design , map_fun = None , reduce_fun = None , name = None ,
246
268
language = 'javascript' , wrapper = DEFAULT , ** defaults ):
247
269
"""Initialize the view descriptor.
248
-
270
+
249
271
:param design: the name of the design document
250
272
:param map_fun: the map function code
251
273
:param reduce_fun: the reduce function code (optional)
@@ -271,8 +293,33 @@ def define(cls, design, name=None, language='python', wrapper=DEFAULT,
271
293
view code).
272
294
"""
273
295
def view_wrapped (fun ):
274
- return cls (design , fun , language = language , wrapper = wrapper ,
275
- ** defaults )
296
+ return cls (design , fun , name = name , language = language ,
297
+ wrapper = wrapper , ** defaults )
298
+ return view_wrapped
299
+
300
+ @property
301
+ def map (self ):
302
+ """Property method for use as a decorator to set a reduce function
303
+ """
304
+ if self .map_fun is not None :
305
+ raise AttributeError ("ViewField already has a map function" )
306
+
307
+ def view_wrapped (fun ):
308
+ self .map_fun = fun
309
+ return self
310
+ return view_wrapped
311
+
312
+ @property
313
+ def reduce (self ):
314
+ """Property method for use as a decorator to set a reduce function
315
+ """
316
+ if self .reduce_fun is not None :
317
+ raise AttributeError ("ViewField already has a reduce function" )
318
+
319
+ def view_wrapped (fun ):
320
+ self .reduce_fun = fun
321
+ self .defaults ['include_docs' ] = True
322
+ return self
276
323
return view_wrapped
277
324
278
325
def __get__ (self , instance , cls = None ):
@@ -322,7 +369,7 @@ def _set_id(self, value):
322
369
@property
323
370
def rev (self ):
324
371
"""The document revision.
325
-
372
+
326
373
:rtype: basestring
327
374
"""
328
375
if hasattr (self ._data , 'rev' ): # When data is client.Document
@@ -331,18 +378,18 @@ def rev(self):
331
378
332
379
def items (self ):
333
380
"""Return the fields as a list of ``(name, value)`` tuples.
334
-
381
+
335
382
This method is provided to enable easy conversion to native dictionary
336
383
objects, for example to allow use of `mapping.Document` instances with
337
384
`client.Database.update`.
338
-
385
+
339
386
>>> class Post(Document):
340
387
... title = TextField()
341
388
... author = TextField()
342
389
>>> post = Post(id='foo-bar', title='Foo bar', author='Joe')
343
390
>>> sorted(post.items())
344
391
[('_id', 'foo-bar'), ('author', u'Joe'), ('title', u'Foo bar')]
345
-
392
+
346
393
:return: a list of ``(name, value)`` tuples
347
394
"""
348
395
retval = []
@@ -358,7 +405,7 @@ def items(self):
358
405
@classmethod
359
406
def load (cls , db , id ):
360
407
"""Load a specific document from the given database.
361
-
408
+
362
409
:param db: the `Database` object to retrieve the document from
363
410
:param id: the document ID
364
411
:return: the `Document` instance, or `None` if no document with the
@@ -378,7 +425,7 @@ def store(self, db):
378
425
def query (cls , db , map_fun , reduce_fun , language = 'javascript' , ** options ):
379
426
"""Execute a CouchDB temporary view and map the result values back to
380
427
objects of this mapping.
381
-
428
+
382
429
Note that by default, any properties of the document that are not
383
430
included in the values of the view will be treated as if they were
384
431
missing from the document. If you want to load the full document for
@@ -391,7 +438,7 @@ def query(cls, db, map_fun, reduce_fun, language='javascript', **options):
391
438
def view (cls , db , viewname , ** options ):
392
439
"""Execute a CouchDB named view and map the result values back to
393
440
objects of this mapping.
394
-
441
+
395
442
Note that by default, any properties of the document that are not
396
443
included in the values of the view will be treated as if they were
397
444
missing from the document. If you want to load the full document for
@@ -448,7 +495,7 @@ def _to_json(self, value):
448
495
449
496
class DateField (Field ):
450
497
"""Mapping field for storing dates.
451
-
498
+
452
499
>>> field = DateField()
453
500
>>> field._to_python('2007-04-01')
454
501
datetime.date(2007, 4, 1)
@@ -541,7 +588,7 @@ def _to_json(self, value):
541
588
542
589
class DictField (Field ):
543
590
"""Field type for nested dictionaries.
544
-
591
+
545
592
>>> from couchdb import Server
546
593
>>> server = Server()
547
594
>>> db = server.create('python-tests')
0 commit comments