Closed Mradr closed 3 years ago
Please consider, that the document
variable in your sample code has type cloudant.document.Document
from cloudant
package. This dict could have multiple fields and it is not equivalent with the datetime
typed object from datetime
module. The strftime
function can be applied only on a datetime.datetime
typed object.
An object type can be checked with type()
function.
In your sample code, a custom decoder: DateTimeDecoder
is used. This is not part of cloudant
nor json
packages. So please review the decoder operation in this custom class.
Furthermore, I am really not suggesting to store only 1 date in a document, better to use a field for this purpose, for example:
val = document['date'].strftime("%Y-%m-%d %H:%M:%S")
But in this case also the custom decoder should be able to assign the datetime
type to the field date
.
I found an example datetime
decoder implementation for this type conversion: https://gist.github.com/abhinav-upadhyay/5300137.
But this is just a suggestion, that you can use as a base point to extend the custom decoder implementation.
The example url is what I am using. It does not work with the above code. Performing: val = document['date'].strftime("%Y-%m-%d %H:%M:%S") still would result in the same error.
Printing document['date'] displays a dict of the endcoded timedate object. Decode doesnt seem to be applying. Reading over the base code quick like, I dont see anything about decoding the object only encoding. Is this a bug then?
I assume this should read something like self._client.decoder and not whatever that is? self.encoder = kwargs.get('encoder') or self._client.encoder self.decoder = kwargs.get('decoder') or json.JSONDecoder
client.py has a gobal encoder that looks to be spread out for the whole program, but not one for decoding. self.encoder = kwargs.get('encoder') or json.JSONEncoder
Yea just tested, seem to fix the issue when I make the changes and then when calling first time passing the decoder along with the encoder seems to work across the board?
Printing document['date'] displays a dict of the encoded timedate object. Decode doesnt seem to be applying. Reading over the base code quick like, I dont see anything about decoding the object only encoding. Is this a bug then?
No, there is no bug, a custom decoder can be attached to the document handling and it will be applied on the JSON content after the content is fetched: https://github.com/cloudant/python-cloudant/blob/33781463ff85c8bb535bd6ede2049e4d3bec6c4c/src/cloudant/document.py#L167
Printing document['date'] displays a dict
The DateTimeEncoder encodes Python datetime
object as dict
. If you don't want to encode as a dict
then use a different encoder.
If you want to decode the dict
back to a Python datetime
object then you need to extend the decoder to make a type conversion from dict
to datetime
and produce a datetime
typed return value.
doc = Document(db, 'docid', encoder=DateTimeEncoder, decoder=DateTimeDecoder)
doc.fetch() # decoder makes the data manipulation and should convert `dict` to `datetime`
print(doc['date']) # prints a date string 2021-04-29 11:24:42.812545
print(type(doc['date'])) # <class 'datetime.datetime'> because the `dict` has been converted to `datetime` by the decoder
db['docid']['date'] = datetime.now()
db['docid'].save() # encode makes data manipulation and `datetime` should be converted to `dict` format
If you are accessing the document via the database dict
then you need to take care to ensure that the entry for the document in your dict is a Document
that has been initialized with the encoder/decoder.
db['docid'] = Document(db, 'docid', encoder=DateTimeEncoder, decoder=DateTimeDecoder)
db['docid'].fetch()
db['docid']['date'] = datetime.now()
db['docid'].save()
print(db['docid']['date']) # prints a date string 2021-04-29 11:24:42.812545
print(type(db['docid']['date'])) # <class 'datetime.datetime'>
db['docid'].fetch() # overwrites the local entry content with the remote
print(db['docid']['date']) # still prints a date string 2021-04-29 11:24:42.812545
print(type(db['docid']['date'])) # still a <class 'datetime.datetime'>
If you use the dict
approach, but don't create a Document
level encoder/decoder then you will not get decoded objects.
I assume this should read something like self._client.decoder and not whatever that is? self.encoder = kwargs.get('encoder') or self._client.encoder self.decoder = kwargs.get('decoder') or json.JSONDecoder
The self._client.encoder
is coming from the database
object, in your now deleted example you used custom encoder/decoder for Document handling.
There is an asymmetry here because the client level encoder existed before Document
encoder/decoder pairs. There has never been a client level decoder. The client encoder was retained for backwards compatibility, but the Document
level one is more flexible since different documents may require different encoding/decoding schemes.
How to deal with time objects?
with Document( self.app.master_database["something"], id, encoder=DateTimeEncoder, decoder=DateTimeDecoder ) as document: val = document["created_on"].strftime("%Y-%m-%d %H:%M:%S")
AttributeError: 'dict' object has no attribute 'strftime'
So it looks like it is not decoding correctly on the object? As for the decoder I am just using the basic json one and looks like it is encoding correctly on the timedate object, but not decoding at all? What am I missing for this step?
Also, trying to wrap my head around the way this works because something is a bit off...
Setting the database to this object self.client_user_contact_db = self.master_database.create_database( "client_user_contacts" )
Then calling self.client_user_contact_db[id]["test"] = False
vs
for entry in self.client_user_contact_db: print(entry)
Results in test (1) to be False, but the entry test to be still True
While I know I need to call .save() I would assume still that they are the same object and I should be able to call .save any time to update the database then while I am working with the local cache version. Also, why is self.client_user_contact_db giving me back whole documents vs just the keys? I can check for keys eg if key in self.client_user_contact_db, but I would think it too would just give me back keys for the for loop - keys being the ids.