PyMongo - имя должно быть экземпляром Str

Я пытаюсь читать и писать из базы данных в MongoDB Atlas, и хотя я могу нормально читать данные из моих коллекций, любая попытка записи в коллекцию приводит к тому, что PyMongo вызывает исключение «имя должно быть экземпляром str».

Я предполагаю, что это относится к объекту MongoClient, но дело в том, что я использую строку подключения. Может ли кто-нибудь помочь мне с тем, что я делаю не так?

Мой код выглядит следующим образом: (У меня есть масса комментариев, которые помогут мне лучше понять, поэтому, пожалуйста, извините за недостаточную краткость)

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

Код моей базы данных выглядит следующим образом:

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

person kilokahn    schedule 01.10.2017    source источник


Ответы (1)


В вашем "setattributes ()" вы получаете доступ к экземпляру коллекции pymongo по имени:

collection = db.database[self.tablename]

Затем в «insertdata ()» вы снова пытаетесь сделать то же самое, но теперь «collection» - это не строка, это экземпляр Collection:

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

Вместо этого просто выполните:

post_id = collection.insert_one(post).inserted_id

Кстати, я вижу, что вы написали код, чтобы гарантировать создание и закрытие MongoClient для каждой операции. Это излишне сложно и резко замедлит работу вашего приложения, требуя нового соединения для каждой операции. Как сказано в FAQ, «Создайте этого клиента один раз для каждого процесса и повторно используйте его для всех операций. Распространенной ошибкой является создание нового клиента для каждого запроса, что очень неэффективно».

Я предлагаю вам удалить свой класс UseDatabase, сделать MongoClient глобальной переменной модуля и напрямую использовать MongoClient:

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)

Этот код проще и будет работать намного быстрее.

person A. Jesse Jiryu Davis    schedule 01.10.2017
comment
Большое спасибо, ваше предложение помогло. Я также прислушиваюсь к вашему совету по перемещению класса базы данных и реализации соединения в самом основном потоке - этот класс является остатком более ранней версии моего приложения, где я использовал MariaDB с пулом соединений. Еще раз большое спасибо. :) - person kilokahn; 01.10.2017
comment
Является ли хорошей практикой глобальное соединение? Разве это не усложнит тестирование, а также не ухудшит его, если у вас будут транзакции? - person badc0re; 26.12.2018
comment
@ badc0re, как говорится в документации, pyMongo реализует объединение коллекций на уровне драйвера. Чем больше раз вы открываете / закрываете соединение, тем медленнее становится ваше приложение и количество соединений, открытых с БД в любой момент времени. - person kilokahn; 26.02.2019