kilokahn
kilokahn

Reputation: 1211

PyMongo - Name must be an instance of Str

I'm trying to read and write from a database on MongoDB Atlas and while I can read data from my collections just fine, any attempt to write to a collection causes PyMongo to raise an exception 'name must be an instance of str'.

I'm guessing this is in reference to the MongoClient object but the thing is I am using a connection string. Can anyone help me with what I'm doing wrong?

My code is as follows: (I've got a ton of comments to help me understand better, so please excuse the lack of brevity)

def setattributes(self, rowdict):
        """ a function to create a user. Assumes that only a data
        dict is provided. strips everything else and updates.
         what the data dict contains is your problem.
        """
        with UseDatabase(self.dbconfig) as db:
            collection = db.database[self.tablename]
            locationdict = {    #create a corresponding location entry
            'email' : rowdict['email'],
            'devstate' : 0,
            'location' : {
            'type': 'Point',
            'coordinates' : [ 0, 0 ]
            },
            'lastseen' : datetime.now()
            }
            try:
                res = db.insertdata(collection, rowdict) #insert user data
            except Exception as e:
                print("Error adding user to DB : %s" % e)
                return False  # if you cant insert, return False
            try:  
                loccollection = db.database[self.locationtable]
                resloc = db.insertdata(loccollection, locationdict)
            except Exception as e: # if the status update failed
                db.collection.remove({'email' : rowdict['email']}) 
                #rollback the user insert - atomicity
                return False
        return True

My Database code is as follows:

class ConnectionError(Exception):
    pass

class CredentialsError(Exception):
    pass

class UseDatabase:
    def __init__(self, config: dict):
        self.config = config

    def __enter__(self, config = atlas_conn_str):
        try:
            self.client = MongoClient(config)
            self.database = self.client['reviv']
            return self

        except:
            print("Check connection settings")
            raise ConnectionError

    def __exit__(self, exc_type, exc_value, exc_traceback):
        self.client.close()

    def insertdata(self, collection, data):
        post = data
        post_id = self.database[collection].insert_one(post).inserted_id
        return post_id

    def getdetails(self, collection, emailid):
        user = collection.find_one({'email' : emailid}, {'_id' : 0})
        return user

Upvotes: 2

Views: 12004

Answers (1)

A. Jesse Jiryu Davis
A. Jesse Jiryu Davis

Reputation: 24017

In your "setattributes()", you access a pymongo Collection instance by name:

collection = db.database[self.tablename]

Then in "insertdata()" you attempt to do the same thing again, but now "collection" is not a string, it's a Collection instance:

post_id = self.database[collection].insert_one(post).inserted_id

Instead, simply do:

post_id = collection.insert_one(post).inserted_id

By the way, I see that you've written some code to ensure you create and close a MongoClient for each operation. This unnecessarily complicated and it will slow down your application dramatically by requiring a new connection for each operation. As the FAQ says, "Create this client once for each process, and reuse it for all operations. It is a common mistake to create a new client for each request, which is very inefficient."

I suggest you delete your UseDatabase class, make the MongoClient a module global variable, and use the MongoClient directly:

client = MongoClient(atlas_conn_str)
db = client[locationtable]

class C:
    def setattributes(self, rowdict):
        collection = db[self.tablename]
        # ... make rowdict as usual, and then:
        res = collection.insert_one(rowdict)

This code is simpler and will run much faster.

Upvotes: 4

Related Questions