Пример реализации асинхронных запросов API¶
Примечание
Этот материал находится в состоянии наполнения информацией.
Общие сведения¶
Асинхронные запросы - это запросы с отложенным ответом от сервера. Т.е. можно совершить несколько запросов друг за другом не дожидаясь ответов на них сразу после совершения запроса. В Web для реализации такого способа обмена обычно используется технология websocket. Для реализации асинхронных запросов API по протоколу websocket у нас применён компонент Django-channels-graphql-ws.
Программная реализация¶
Реализация асинхронности через websocket совершенно идентична реализации для синхронных запросов.
Программная реализация подписки¶
Подписка (subscription - запрос с отложенным ответом при возникновении какого-либо события) очень похожа на реализацию схем и мутаций для GraphQL API. Рассмотрим для примера создание подписки на действия в чате. Для начала создадим файл mesagxilo/api/[subscription.py](http://subscription.py) с описанием объектов подписки с подобным содержимым:
import channels_graphql_ws
import graphene
from graphql import GraphQLError
from uzantoj.api.schema import UzantoNode
from .schema import MesagxiloBabilejoNode, MesagxiloMesagxoNode, MesagxiloInvestojNode
from ..models import MesagxiloBabilejo, MesagxiloMesagxo, MesagxiloPartoprenanto
from uzantoj.models import Uzanto
# Реализация подписки для чатов
# Объединение возвращаемых объектов действий
class MesagxiloEventojUnion(graphene.Union):
class Meta:
# Типы объединения
types = (
UzantoNode,
MesagxiloMesagxoNode,
MesagxiloInvestojNode,
)
# Подписки для WebSocket
class MesagxiloEventoj(channels_graphql_ws.Subscription):
"""Подписка на действия в чатах"""
evento = graphene.String(required=True)
babilejo = graphene.relay.node.Field(MesagxiloBabilejoNode)
objekto = graphene.Field(MesagxiloEventojUnion)
class Arguments:
"""Тут аргументы, передающиеся для подписки на события чатов"""
# Список ID чатов
babilejoj = graphene.List(graphene.Int, required=True)
def subscribe(self, info, babilejoj):
"""В этом методе определятся список чатов для подписки на их действия"""
if info.context.user.is_authenticated:
babilejoj_res = MesagxiloPartoprenanto.objects.filter(
partoprenanto=info.context.user, forigo=False, arkivo=False, publikigo=True,
babilejo__id__in=babilejoj
)
babilej_set = set(
babilejoj_res.values_list('babilejo__id', flat=True)
)
if not babilejoj_res:
raise GraphQLError(
'Невозможно отслеживать чаты c ID {}, они не сущствуют или не доступны'.format(
set(babilejoj) - babilej_set
)
)
# Тут можно сделать проверку на количество заявленных и фактически найденных чатов,
# в которых пользователь участвует
return list(map(str, babilej_set)) or None
raise GraphQLError('Необходима авторизация')
[@staticmethod](profile/staticmethod)
def publish(payload, info, babilejoj):
evento = payload.get('evento')
try:
babilejo = MesagxiloBabilejo.objects.get(
uuid=payload.get('babilejo'),
publikigo=True,
forigo=False,
arkivo=False
)
if evento in ('nova_mesagxo', 'redaktita_mesagxo'):
objekto = MesagxiloMesagxo.objects.get(
uuid=payload.get('mesagxo'),
publikigo=True,
arkivo=False,
forigo=False
)
autoro = objekto.posedanto
else:
objekto = Uzanto.objects.get(
id=payload.get('uzanto')
)
autoro = objekto
if autoro.id == info.context.user.id:
# Пропускаем публикацию события для автора события :)
return MesagxiloEventoj.SKIP
return MesagxiloEventoj(evento=evento, babilejo=babilejo, objekto=objekto)
except (MesagxiloBabilejo.DoesNotExist, Uzanto.DoesNotExist, MesagxiloMesagxo.DoesNotExist):
pass
return MesagxiloEventoj.SKIP
[@classmethod](profile/classmethod)
def publikigi(cls, babilejo, mesagxo):
"""Этот метод должен вызываться при публикации нового сообщения в чате"""
cls.broadcast(
group='{}'.format(babilejo.id),
payload={
'evento': 'nova_mesagxo',
'babilejo': str(babilejo.uuid),
'mesagxo': str(mesagxo.uuid)
}
)
[@classmethod](profile/classmethod)
def redakti(cls, babilejo, mesagxo):
"""Этот метод должен вызываться при редактировании сообщения в чате"""
cls.broadcast(
group=babilejo.id,
payload={
'evento': 'redaktita_mesagxo',
'babilejo': babilejo.uuid,
'mesagxo': mesagxo.uuid
}
)
[@classmethod](profile/classmethod)
def presajoj(cls, babilejo, uzanto):
"""Этот метод должен вызываться при редактировании сообщения в чате"""
cls.broadcast(
group=babilejo.id,
payload={
'evento': 'presajoj',
'babilejo': babilejo.uuid,
'uzanto': uzanto.id
}
)
class MesagxiloSubscription(graphene.ObjectType):
mesagxilo_eventoj = MesagxiloEventoj.Field()