Почему при выполнении запроса я получаю ошибку "Cannot return null for non-nullable field"?
** СТРУКТУРА ПАПКИ **
models.py
import graphene
from graphene import ObjectType, relay
from graphene_django import DjangoObjectType
from general.models import Character, Director, Episode
class CharacterType(DjangoObjectType):
pk = graphene.Int(source="pk", required=True)
class Meta:
model = Character
filter_fields = {
"name": ["exact", "icontains", "istartswith"],
"character_species": ["exact", "icontains"],
"location": ["exact"],
"status": ["exact"],
}
fields = "__all__"
interfaces = (relay.Node,)
class EpisodeType(DjangoObjectType):
pk = graphene.Int(source="pk")
class Meta:
model = Episode
filter_fields = {
"name": ["exact", "icontains", "istartswith"],
"directed_by__name": ["exact", "icontains"],
"aired_date": ["exact"],
}
fields = "__all__"
interfaces = (relay.Node,)
class DirectorType(DjangoObjectType):
pk = graphene.Int(source="pk", required=True)
class Meta:
model = Director
filter_fields = {
"name": ["exact", "icontains", "istartswith"],
"first_directed_episode__name": ["exact", "icontains"],
"last_directed_episode__name": ["exact", "icontains"],
"age": ["exact"],
}
fields = "__all__"
interfaces = (relay.Node,)
types.py
import graphene
from graphene import ObjectType, relay
from graphene_django import DjangoObjectType
from general.models import Character, Director, Episode
class CharacterType(DjangoObjectType):
pk = graphene.Int(source="pk", required=True)
class Meta:
model = Character
filter_fields = {
"name": ["exact", "icontains", "istartswith"],
"character_species": ["exact", "icontains"],
"location": ["exact"],
"status": ["exact"],
}
fields = "__all__"
interfaces = (relay.Node,)
class EpisodeType(DjangoObjectType):
pk = graphene.Int(source="pk")
class Meta:
model = Episode
filter_fields = {
"name": ["exact", "icontains", "istartswith"],
"directed_by__name": ["exact", "icontains"],
"aired_date": ["exact"],
}
fields = "__all__"
interfaces = (relay.Node,)
class DirectorType(DjangoObjectType):
pk = graphene.Int(source="pk", required=True)
class Meta:
model = Director
filter_fields = {
"name": ["exact", "icontains", "istartswith"],
"first_directed_episode__name": ["exact", "icontains"],
"last_directed_episode__name": ["exact", "icontains"],
"age": ["exact"],
}
fields = "__all__"
interfaces = (relay.Node,)
loaders.py
from collections import defaultdict
from promise import Promise
from promise.dataloader import DataLoader
from general.models import Episode
class EpisodesByDirectorIdLoader(DataLoader):
def batch_load_fn(self, director_ids):
episodes_by_director_ids = defaultdict(list)
all_episodes_of_directors = Episode.objects.filter(
directed_by__pk__in=director_ids
)
for episode in all_episodes_of_directors.iterator():
episodes_by_director_ids[episode.directed_by.pk].append(episode)
return Promise.resolve(
[
episodes_by_director_ids.get(director_id, [])
for director_id in director_ids
]
)
queries.py
import graphene
from graphene_django.filter import DjangoFilterConnectionField
from general.models import Character, Director, Episode
from .types import EpisodeType
class Query(graphene.ObjectType):
all_episodes = DjangoFilterConnectionField(EpisodeType)
@staticmethod
def resolve_all_episodes(root, info):
# return Episode.objects.all() # RETURNS QUERYSET
return info.context.episodes_by_director_id_loader.load(1) # RETURNS PROMISE OBJECT
QUERY
query{
allEpisodes{
edges{
node{
name
}
}
}
}
OUTPUT
{
"errors": [
{
"message": "Cannot return null for non-nullable field EpisodeTypeConnection.edges.",
"locations": [
{
"line": 26,
"column": 5
}
],
"path": [
"allEpisodes",
"edges"
]
}
],
"data": {
"allEpisodes": null
}
}
Создал модели с именами Episode, Character и Director. Написал типы, запросы и мутации, которые прекрасно работали до использования dataloader, все еще прекрасно работают при возврате набора запросов в resolver, но выдают какую-то ошибку.
Ожидаемым результатом было использование dataloader для повышения производительности, метод resole возвращает объект Promise, а запрос после выполнения показывает вышеприведенный вывод
ИСПРАВЛЕНО ПУТЕМ РЕДАКТИРОВАНИЯ RESOLVER И LOADER И ИЗМЕНЕНИЯ ВЕРСИИ GRAPHENE И DJANGO
loaders.py
from collections import defaultdict
from promise import Promise
from promise.dataloader import DataLoader
from general.models import Episode, Director
class EpisodesByDirectorIdLoader(DataLoader):
def batch_load_fn(self, director_ids):
episodes_directed_by_director_ids = defaultdict(list)
episodes = Episode.objects.filter(directed_by_id__in=director_ids)
for episode in episodes.iterator():episodes_directed_by_director_ids[episode.directed_by_id].append(episode)
return Promise.resolve(
[episodes_directed_by_director_ids.get(director_id, []) for director_id in director_ids]
)
Разрешитель
class DirectorType(DjangoObjectType):
pk = graphene.Int(source="pk", required=True)
episodes_directed = graphene.List(EpisodeType)
class Meta:
model = Director
filter_fields = {
"name": ["exact", "icontains", "istartswith"],
"first_directed_episode__name": ["exact", "icontains"],
"last_directed_episode__name": ["exact", "icontains"],
"age": ["exact"],
}
only_fields = (
"id",
"name",
"age",
"first_directed_episode",
"last_directed_episode",
)
interfaces = (relay.Node,)
use_connection = True
@staticmethod
def resolve_episodes_directed(root, info, **kwargs):
# return Episode.objects.filter(directed_by_id=root.id)
return info.context.episodes_by_director_id_loader.load(root.id)
requirements.txt
aiodataloader==0.2.1
aniso8601==7.0.0
asgiref==3.5.2
backports.zoneinfo==0.2.1
black==22.10.0
click==8.1.3
Cython==0.29.32
Django==3.2.16
django-debug-toolbar==3.7.0
django-filter==22.1
django-graphiql-debug-toolbar==0.2.0
django-utils-six==2.0
factory-boy==3.2.1
Faker==15.3.2
flake8==5.0.4
graphene==2.1.9
graphene-django==2.2.0
graphql-core==2.3.2
graphql-relay==2.0.1
isort==5.10.1
mccabe==0.7.0
mypy-extensions==0.4.3
pathspec==0.10.1
platformdirs==2.5.2
promise==2.3
psycopg2-binary==2.9.5
pycodestyle==2.9.1
pyflakes==2.5.0
python-dateutil==2.8.2
pytz==2022.6
Rx==1.6.1
singledispatch==3.7.0
six==1.16.0
sqlparse==0.4.3
text-unidecode==1.3
tomli==2.0.1
typing-extensions==4.4.0