Skip to content
This repository was archived by the owner on Aug 30, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Unreleased

- [NEW] Add custom JSON encoder/decoder option to `Document` constructor.
- [NEW] Add new view parameters, `stable` and `update`, as keyword arguments to `get_view_result`.
- [FIXED] Case where an exception was raised after successful retry when using `doc.update_field`.

Expand Down
9 changes: 6 additions & 3 deletions src/cloudant/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@ class Document(dict):
:param database: A database instance used by the Document. Can be
either a ``CouchDatabase`` or ``CloudantDatabase`` instance.
:param str document_id: Optional document id used to identify the document.
:param str encoder: Optional JSON encoder object.
:param str decoder: Optional JSON decoder object.
"""
def __init__(self, database, document_id=None):
def __init__(self, database, document_id=None, **kwargs):
super(Document, self).__init__()
self._client = database.client
self._database = database
Expand All @@ -63,7 +65,8 @@ def __init__(self, database, document_id=None):
self._document_id = document_id
if self._document_id is not None:
self['_id'] = self._document_id
self.encoder = self._client.encoder
self.encoder = kwargs.get('encoder') or self._client.encoder
self.decoder = kwargs.get('decoder') or json.JSONDecoder

@property
def r_session(self):
Expand Down Expand Up @@ -165,7 +168,7 @@ def fetch(self):
resp = self.r_session.get(self.document_url)
resp.raise_for_status()
self.clear()
self.update(resp.json())
self.update(resp.json(cls=self.decoder))

def save(self):
"""
Expand Down
44 changes: 44 additions & 0 deletions tests/unit/document_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
import uuid
import inspect

from datetime import datetime

from cloudant.document import Document
from cloudant.error import CloudantDocumentException

Expand Down Expand Up @@ -859,5 +861,47 @@ def test_document_request_fails_after_client_disconnects(self):
finally:
self.client.connect()

def test_document_custom_json_encoder_and_decoder(self):
dt_format = '%Y-%m-%dT%H:%M:%S'

class DTEncoder(json.JSONEncoder):

def default(self, obj):
if isinstance(obj, datetime):
return {
'_type': 'datetime',
'value': obj.strftime(dt_format)
}
return super(DTEncoder, self).default(obj)

class DTDecoder(json.JSONDecoder):

def __init__(self, *args, **kwargs):
json.JSONDecoder.__init__(self, object_hook=self.object_hook,
*args, **kwargs)

def object_hook(self, obj):
if '_type' not in obj:
return obj
if obj['_type'] == 'datetime':
return datetime.strptime(obj['value'], dt_format)
return obj

doc = Document(self.db, encoder=DTEncoder)
doc['name'] = 'julia'
doc['dt'] = datetime(2018, 7, 9, 15, 11, 10, 0)
doc.save()

raw_doc = self.db.all_docs(include_docs=True)['rows'][0]['doc']

self.assertEquals(raw_doc['name'], 'julia')
self.assertEquals(raw_doc['dt']['_type'], 'datetime')
self.assertEquals(raw_doc['dt']['value'], '2018-07-09T15:11:10')

doc2 = Document(self.db, doc['_id'], decoder=DTDecoder)
doc2.fetch()

self.assertEquals(doc2['dt'], doc['dt'])

if __name__ == '__main__':
unittest.main()