0

I have Flask app using MongoDB. I've had problems with encoding ObjectId, datetime and date objects, but solved them with this https://stackoverflow.com/a/51773611/9472066 answer - custom encoder works. I don't know, however, how to write a decoder for PyMongo: how can I know whether the string is ObjectId, or datetime, or anything else? Do I have to check format by hand in the decoder method, and then call the appropriate constructor? Or is there any smarter, more automatic way?

qalis
  • 1,314
  • 1
  • 16
  • 44
  • Where is your data coming from? – Belly Buster Apr 09 '20 at 07:58
  • @BellyBuster from my own serialization to the database. Precisely, frontend passes JSONs, I create objects from them, do a bunch of business logic operations on it, and then serialize them to database. Later I want to take them from the database. So I have both serialization and deserialization to JSON. Documents and objects are complex (embedded documents, lists of custom objects etc.), so I thing encoder and decoder are necessary, e. g. for ObjectID type. – qalis Apr 09 '20 at 08:35

1 Answers1

1

I'd strongly advise you to work with native python types because pymongo will do all the heavy lifting in terms of creating the appropriate types.

It's a mistake to think MongoDB is a JSON database, and a bigger mistake to think you have to work with JSON to get data in and out out it.

If you really want to do it try the bson.json_util module:

import pymongo
import bson.json_util

db = pymongo.MongoClient()["mydatabase"]

db.mycollection.insert_one({'a': 'a'})
json_str = bson.json_util.dumps(db.mycollection.find_one({'a': 'a'}))
json_dict = json.loads(json_str)
json_dict['a'] = 'b'
json_str = json.dumps(json_dict)
db.newcollection.insert_one(bson.json_util.loads(json_str))
print(list(db.newcollection.find({})))

But I would prefer to do this natively:

db.mycollection.insert_one({'a': 'a'})
record = db.mycollection.find_one({'a': 'a'})
record['a'] = 'b'
db.newcollection.insert_one(record)
print(list(db.newcollection.find({})))
Belly Buster
  • 8,224
  • 2
  • 7
  • 20
  • The problem with second approach in my case is that I have quite complex business logic to do with the data from the database. For example, I create User class objects with a bunch of methods and dependencies, and I have to save it to database and later load data and create User object from it. At the same time, I don't want (and with existing APIs probably can't) use PyMODM and it's Field types like StringField. Doing all work on straight dicts would be quite hard in my case, so I want to work with custom classes, which AFAIK require encoder and decoder. – qalis Apr 09 '20 at 13:25
  • You can use the class's built-in __dict__ property to get the dictionary representation of any class object. Believe me you will save a world of pain avoiding pickling and unpickling everything to JSON. – Belly Buster Apr 09 '20 at 13:32
  • What do you think about the slight workaround: classes holding JSONs, with methods for JSON access and for business logic? That would work with your second code, if I use that embedded JSON? – qalis Apr 09 '20 at 14:39
  • 1
    That sounds sensible. – Belly Buster Apr 09 '20 at 16:07
  • The `dict` property I referenced earlier should have been `__dict__`. The default formatter didn't display it properly. – Belly Buster Apr 10 '20 at 01:09