**FOLDER STRUCTURE **
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
}
}
Created models named Episode, Character and Director. Wrote types, queries and mutations which worked perfectly before using dataloader, still works perfectly when returning queryset in resolver but shows some kind of error.
The expected result was to use dataloader to improve the performance, the resole method returns Promise object and the query after execution shows the above output