如何在 Telegram 机器人内异步进行数据库调用?

问题描述 投票:0回答:1

我有一个 Django 应用程序,它运行 Telegram 聊天机器人脚本作为命令。

我使用

python manage.py runserver
启动 Django 应用程序。 我用
python manage.py bot
启动电报客户端。

我想在用户在电报聊天中键入“/animals”时调用的异步方法中列出 Animal 表中的条目。如果我使用硬编码列表或字典作为数据源,我的代码就可以工作。但是,我无法让 ORM 调用在异步模式下工作。

文件结构:

|Accounts---------------------
|------| models.py------------
|Main-------------------------
|------| Management-----------
|---------------| Commands----
|-----------------------bot.py

动物模型:

class Animal(models.Model):
    id = models.AutoField(primary_key=True)
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    name = models.CharField(max_length=255)

我从文件中删除了很多内容,只留下了相关的部分。
机器人.py

# removed unrelated imports
from asgiref.sync import sync_to_async
from accounts.models import Animal

class Command(BaseCommand):
    help = "Starts the telegram bot."
    # assume that the token is correct
    TOKEN = "abc123"

    def handle(self, *args, **options):

        async def get_animals():
            await Animal.objects.all()

        async def animals_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:

            async_db_results = get_animals()

            message = ""
            counter = 0
            for animal in async_db_results:
                message += animal.name + "\n"
                counter += 1

            await update.message.reply_text(message)

        application = Application.builder().token(TOKEN).build()
        application.add_handler(CommandHandler("animals", animals_command))
        application.run_polling(allowed_updates=Update.ALL_TYPES)

此代码的错误消息:
类型错误:“协程”对象不可迭代

最初我用

Animal.objects.all()
代替
async_db_results
。 ORM 调用不是异步的,所以我收到此错误消息:

django.core.exceptions.SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async.

这是一个原型应用程序,我知道我不应该使用

runserver
。我还应该使用 webhook 而不是长轮询,但我不认为这些问题与我的异步问题有关。
我要尝试的下一件事是使用 asyncio,但我已经花了很多时间,我想我会问这个问题。

我已经查看了这些资源(以及许多其他资源):
docs.djangoproject.com:asgiref.sync.sync_to_async
stackoverflow:Django:SynchronousOnlyOperation:您无法从异步上下文中调用它 - 使用线程或sync_to_async
stackoverflow:同步到异步 Django ORM 查询集外键属性

python django asynchronous async-await python-telegram-bot
1个回答
0
投票

我想通了。正确使用

sync_to_async
await
就可以了。

@sync_to_async
def get_animals():
    return list(Habit.objects.all())

async def animals_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    """Send a message when the command /habit is issued."""
    async_db_results = await get_animals()

    message = "Animal list:"
    for h in async_db_results:
        message += "{}\n".format(h.name)

    await update.message.reply_text(message)
© www.soinside.com 2019 - 2024. All rights reserved.