Как можно использовать pymongo для асинхронной работы?

Всем привет! Я планирую заменить pymongo на motor (асинхронную версию pymongo) и у меня есть следующий код. <br/> <br/> database.py <br/> <pre><code class="python">import motor.motor_asyncio
import os
from dotenv import load_dotenv
import asyncio

load_dotenv()

cluster = motor.motor_asyncio.AsyncIOMotorClient(os.environ['MONGO_URL'])
db = cluster[os.environ['DB_NAME']]

async def get_db(server_id, plugin):
	data = await db[plugin].find({'server_id': str(server_id)})
	#print(data)

	if not len(list(data)) > 1:
		return db[plugin].find_one({'server_id': str(server_id)})
	else:
		return db[plugin].find({'server_id': str(server_id)})</code></pre> <br/> <br/> В других файлах есть похожий код <br/> <pre><code class="python">async def update_rewards(self, server):
		plugin = database.get_db(server.id, 'levels')
		server_rewards = database.get_db(server.id, 'role_rewards')

		rewards = []
		if database.get_db(server.id, 'role_rewards'):
			if not type(database.get_db(server.id, 'role_rewards')) is dict:
				rewards_ = database.get_db(server.id, 'role_rewards')
				for reward in rewards_:
					print(reward)
					await rewards.append(reward)
			else:
				rewards.append(database.get_db(server.id, 'role_rewards'))</code></pre> <br/> <br/> Возникла ошибка <pre><code class="python">for reward in rewards_:
                  ^^^^^^^^
TypeError: 'coroutine' object is not iterable</code></pre> <br/> <br/> Я не до конца понимаю, как работает asyncio. Можете помочь мне разобраться?
  • 14 апреля 2025 г. 8:55
Ответы на вопрос 2
Да, конечно! Давайте разберём ваш код и исправим несколько ошибок.

Проблема, с которой вы столкнулись, связана с тем, что вы забыли использовать `await` при вызове асинхронных функций. Ваша функция `get_db()` является асинхронной, и когда вы вызываете её, вы должны использовать `await`, чтобы дождаться результата выполнения.

Вот как можно изменить ваш код для корректного асинхронного использования:

1. В функции `get_db` необходимо использовать `await` при выполнении запросов к базе данных.
2. В функции `update_rewards` также нужно использовать `await` при получении данных из базы.

Вот как можно исправить ваш код:

### database.py:

```python
import motor.motor_asyncio
import os
from dotenv import load_dotenv
import asyncio

load_dotenv()

cluster = motor.motor_asyncio.AsyncIOMotorClient(os.environ['MONGO_URL'])
db = cluster[os.environ['DB_NAME']]

async def get_db(server_id, plugin):
    data = await db[plugin].find({'server_id': str(server_id)}).to_list(None)  # Изменено

    if len(data) == 0:
        return await db[plugin].find_one({'server_id': str(server_id)})  # Используем await
    else:
        return data  # Возвращаем полученные данные
```

### Другие файлы:

```python
async def update_rewards(self, server):
    # Используем await для получения данных
    plugin_data = await database.get_db(server.id, 'levels')
    server_rewards = await database.get_db(server.id, 'role_rewards')

    rewards = []
    
    if server_rewards:
        # Если server_rewards - это не пустой список или None
        if isinstance(server_rewards, list):  # Убедимся, что это список
            for reward in server_rewards:
                print(reward)
                rewards.append(reward)  # Добавляем в список
        else:
            rewards.append(server_rewards)  # Добавляем напрямую, если это не список
```

Изменения, которые я внёс:
1. В функции `get_db`, запросы теперь используют `.to_list(None)`, чтобы получить список результатов из `find()`, и вы должны использовать `await` при вызове `find_one()`.
2. В функции `update_rewards` используется `await` для получения данных из базы, а также добавлен проверка с помощью `isinstance()` для определения типа `server_rewards`.

Теперь всё должно работать корректно! Не забудьте запускать ваш асинхронный код в окружении, поддерживающем `asyncio`, например, в функции `async def main()`.

Вот пример такой функции:

```python
async def main():
    # Здесь вы можете вызвать ваши асинхронные функции
    await update_rewards(server)

# Запуск основной асинхронной функции
if __name__ == "__main__":
    asyncio.run(main())
``` 

Пусть ваш код будет работать эффективно! Если останутся вопросы, не стесняйтесь спрашивать.
Вызов корутины осуществляется с указанием ключевого слова await перед ней 

https://docs.python.org/3/library/asyncio-task.html
Похожие вопросы