首页 > 解决方案 > 将 Django 的 GenericForeignKey 与异步石墨烯订阅一起使用

问题描述

我已经实现了 graphql_ws来订阅来自Notification使用多个GenericForeignKeys.

我的设置运行良好,除非我尝试从这些异物中查询信息。然后我收到以下错误:

graphql.error.located_error.GraphQLLocatedError: You cannot call this from an async context - use a thread or sync_to_async.

据我所知,这是因为一些数据库查询操作是在异步上下文之外get_notifications(即在resolve_actor函数中)完成的。但我真的不清楚如何将操作拉resolve_actor
入异步上下文。

我没有成功尝试使用prefetch_related(另外我不确定它是否适用于每个 Django 文档的多种内容类型)。

这是代码

模型.py


class Notification(TimeStampedModel):
    # ...
    actor_content_type = models.ForeignKey(ContentType, related_name='notify_actor', on_delete=models.CASCADE)
    actor_object_id = models.CharField(max_length=255)
    actor = GenericForeignKey('actor_content_type', 'actor_object_id')
    # ...

架构.py

class ActorTypeUnion(graphene.Union):
    """
    All possible types for Actors
    (The object that performed the activity.)
    """
    class Meta:
        types = (UserType,) # here's there's only one type, but other fields have multiple


class NotificationType(DjangoObjectType):
    actor = graphene.Field(ActorTypeUnion)
    
    def resolve_actor(self, args):
        if self.actor   is not None:
            model_name = self.actor._meta.model_name
            app_label = self.actor._meta.app_label
            model = ContentType.objects.get(app_label=app_label, model=model_name)
            return model.get_object_for_this_type(pk=self.actor_object_id)
        return None
    # ...
    class Meta:
        model = Notification

class Subscription(graphene.ObjectType):
    unread_notifications = graphene.List(NotificationType)

    async def resolve_unread_notifications(self, info, **kwargs):
        user = info.context['user']
        if user.is_anonymous:
            raise Exception('Not logged in!')
        
        @database_sync_to_async
        def get_notifications(user):
            notifications = Notification.objects.filter(
                recipient=user,
                read=False,
                organization=user.active_organization,
            )
            return [notifications]

        while True:
            await asyncio.sleep(1)
            yield await get_notifications(user)

查询(一切正常,除非我在 上查询字段actor

subscription {
   unreadNotifications {
      id,
      read,
      actor {
        ... on UserType {
          __typename,
          id

        }
      }
    }
  }

完整回溯

Traceback (most recent call last):
  File "/Users/benjaminsoukiassian/.pyenv/versions/logbook/lib/python3.8/site-packages/graphql/execution/executor.py", line 452, in resolve_or_error
    return executor.execute(resolve_fn, source, info, **args)
  File "/Users/benjaminsoukiassian/.pyenv/versions/logbook/lib/python3.8/site-packages/graphql/execution/executors/asyncio.py", line 74, in execute
    result = fn(*args, **kwargs)
  File "/Users/benjaminsoukiassian/Projects/logbook-back/logbook/notifications/schema.py", line 52, in resolve_actor
    model = ContentType.objects.get(app_label=app_label, model=model_name)
  File "/Users/benjaminsoukiassian/.pyenv/versions/logbook/lib/python3.8/site-packages/django/db/models/manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/Users/benjaminsoukiassian/.pyenv/versions/logbook/lib/python3.8/site-packages/django/db/models/query.py", line 431, in get
    num = len(clone)
  File "/Users/benjaminsoukiassian/.pyenv/versions/logbook/lib/python3.8/site-packages/django/db/models/query.py", line 262, in __len__
    self._fetch_all()
  File "/Users/benjaminsoukiassian/.pyenv/versions/logbook/lib/python3.8/site-packages/django/db/models/query.py", line 1324, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
  File "/Users/benjaminsoukiassian/.pyenv/versions/logbook/lib/python3.8/site-packages/django/db/models/query.py", line 51, in __iter__
    results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
  File "/Users/benjaminsoukiassian/.pyenv/versions/logbook/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 1173, in execute_sql
    cursor = self.connection.cursor()
  File "/Users/benjaminsoukiassian/.pyenv/versions/logbook/lib/python3.8/site-packages/django/utils/asyncio.py", line 24, in inner
    raise SynchronousOnlyOperation(message)
graphql.error.located_error.GraphQLLocatedError: You cannot call this from an async context - use a thread or sync_to_async.

标签: djangographqlpython-asynciographene-pythongraphene-django

解决方案


推荐阅读