即使在 django 和discord.py 中使用sync_to_async 后也出现 SynchronousOnlyOperation 错误

Iqb*_*wan 1 python django django-orm python-asyncio discord.py

我正在尝试为 Django 网站制作一个不和谐的机器人。在实现的时候我发现Django数据库不允许异步操作,我们必须使用线程或sync_to_async.

我使用过sync_to_async并且似乎有效。但后来我遇到了一个问题,我什至无法弄清楚它出了什么问题以及那里发生了什么。

谁能解释我在那里做错了什么以及那里发生了什么?

@sync_to_async
def get_customer(id):
    try:
        return Customer.objects.get(discord_id=id)
    except:
        return None
#------

@bot.command()
async def categories(ctx):
    customer = await get_customer(ctx.author.id) #this line of code works well
    categories = await sync_to_async(Category.objects.all().filter)(customer=customer)
    #categories = await sync_to_async(Category.objects.filter)(customer=customer)#I tried this also, didn't work
    print(categories) #problem is in the line
    embed = Embed(title='', description="{}".format('\n'.join([ x.name for x in categories])))
    await ctx.send(embed=embed)
Run Code Online (Sandbox Code Playgroud)

我收到的回溯错误是:

Running bot
Ignoring exception in command categories:
Traceback (most recent call last):
  File "/Users/rhidwan/Desktop/personal_transaction/venv/lib/python3.7/site-packages/discord/ext/commands/core.py", line 83, in wrapped
    ret = await coro(*args, **kwargs)
  File "discord_bot.py", line 76, in categories
    print(categories)
  File "/Users/rhidwan/Desktop/personal_transaction/venv/lib/python3.7/site-packages/django/db/models/query.py", line 252, in __repr__
    data = list(self[:REPR_OUTPUT_SIZE + 1])
  File "/Users/rhidwan/Desktop/personal_transaction/venv/lib/python3.7/site-packages/django/db/models/query.py", line 276, in __iter__
    self._fetch_all()
  File "/Users/rhidwan/Desktop/personal_transaction/venv/lib/python3.7/site-packages/django/db/models/query.py", line 1261, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
  File "/Users/rhidwan/Desktop/personal_transaction/venv/lib/python3.7/site-packages/django/db/models/query.py", line 57, in __iter__
    results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
  File "/Users/rhidwan/Desktop/personal_transaction/venv/lib/python3.7/site-packages/django/db/models/sql/compiler.py", line 1150, in execute_sql
    cursor = self.connection.cursor()
  File "/Users/rhidwan/Desktop/personal_transaction/venv/lib/python3.7/site-packages/django/utils/asyncio.py", line 24, in inner
    raise SynchronousOnlyOperation(message)
django.core.exceptions.SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async.

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/rhidwan/Desktop/personal_transaction/venv/lib/python3.7/site-packages/discord/ext/commands/bot.py", line 892, in invoke
    await ctx.command.invoke(ctx)
  File "/Users/rhidwan/Desktop/personal_transaction/venv/lib/python3.7/site-packages/discord/ext/commands/core.py", line 797, in invoke
    await injected(*ctx.args, **ctx.kwargs)
  File "/Users/rhidwan/Desktop/personal_transaction/venv/lib/python3.7/site-packages/discord/ext/commands/core.py", line 92, in wrapped
    raise CommandInvokeError(exc) from exc
discord.ext.commands.errors.CommandInvokeError: Command raised an exception: SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async.
Run Code Online (Sandbox Code Playgroud)

Ozg*_*ali 6

如果您仍然需要帮助解决该问题;

似乎这个问题与 Django 中查询集的惰性求值有关。基本上,当你打电话时

MyModel.objects.filter(key=value) 
Run Code Online (Sandbox Code Playgroud)

查询集不会立即评估,它仅在您使用它时评估,这可能是迭代它,将其转换为列表,或获取它的字符串表示形式。

在您的示例中,即使您使用sync_to_async包装您的Django调用,当您想要使用以下方式打印查询集时,也会评估查询集:

print(categories)
Run Code Online (Sandbox Code Playgroud)

由于它处于异步上下文中,因此您会收到错误。为了避免这种情况,您可以强制在sync_to_async内评估查询集,例如

@sync_to_async
def get_categories_value(customer):
    return list(Category.objects.all().filter(customer=customer))

...

categories = await get_categories_value()
print(categories)
Run Code Online (Sandbox Code Playgroud)