Skip to content

Instantly share code, notes, and snippets.

@camd
Created July 20, 2017 22:15
Show Gist options
  • Save camd/cae123923ba8ce2442ebd6e1be34377e to your computer and use it in GitHub Desktop.
Save camd/cae123923ba8ce2442ebd6e1be34377e to your computer and use it in GitHub Desktop.
graphene_django select_related
from graphene_django.filter import DjangoFilterConnectionField
from graphql.utils.ast_to_dict import ast_to_dict
def collect_fields(node):
non_fields = ["edges", "node"]
fields = []
for leaf in node:
if leaf.get('kind', None) == "Field" and not leaf["name"]["value"] in non_fields:
fields.append(leaf["name"]["value"])
if leaf.get("selection_set", None):
fields = fields + collect_fields(leaf["selection_set"]["selections"])
return fields
def get_fields(info):
"""Return a nested dict of the fields requested by a graphene resolver"""
node = ast_to_dict(info.field_asts)
return collect_fields(node)
def optimize(qs, info, field_map):
fields = get_fields(info)
print(fields)
for field in fields:
if field in field_map:
field_name, opt = field_map[field]
if opt == "prefetch":
qs = qs.prefetch_related(field_name)
else:
qs = qs.select_related(field_name)
return qs
class OptimizableFilterConnectionField(DjangoFilterConnectionField):
@staticmethod
def merge_querysets(default_queryset, queryset):
# There could be the case where the default queryset (returned from
# the filterclass)
# and the resolver queryset have some limits on it.
# We only would be able to apply one of those, but not both
# at the same time.
# See related PR: https://github.com/graphql-python/graphene-django
# /pull/126
assert not (
default_queryset.query.low_mark and queryset.query.low_mark), (
'Received two sliced querysets (low mark) in the connection, '
'please slice only in one.'
)
assert not (
default_queryset.query.high_mark and queryset.query.high_mark), (
'Received two sliced querysets (high mark) in the connection, '
'please slice only in one.'
)
low = default_queryset.query.low_mark or queryset.query.low_mark
high = default_queryset.query.high_mark or queryset.query.high_mark
default_queryset.query.clear_limits()
# This eliminates select_related and prefetch_related, so disabling
# for now:
# queryset = default_queryset & queryset
queryset.query.set_limits(low, high)
return queryset
import graphene
from graphene_django.filter import DjangoFilterConnectionField
from graphene_django.types import DjangoObjectType
import helpers
from treeherder.model import error_summary
from treeherder.model.models import *
from treeherder.webapp.graphql.types import ObjectScalar
class JobDetailGraph(DjangoObjectType):
class Meta:
model = JobDetail
filter_fields = {
'url': ('exact', 'icontains', 'iendswith', 'endswith')
}
interfaces = (graphene.relay.Node, )
class TextLogErrorGraph(DjangoObjectType):
class Meta:
model = TextLogError
bug_suggestions = ObjectScalar()
def resolve_bug_suggestions(self, args, context, info):
return error_summary.bug_suggestions_line(self)
class TextLogStepGraph(DjangoObjectType):
class Meta:
model = TextLogStep
class JobGraph(DjangoObjectType):
class Meta:
model = Job
filter_fields = {
'id': ['exact'],
'guid': ['exact'],
'result': ['exact'],
'tier': ['exact', 'lt'],
}
interfaces = (graphene.relay.Node, )
job_details = DjangoFilterConnectionField(JobDetailGraph)
def resolve_job_details(self, args, context, info):
return JobDetail.objects.filter(job=self, **args)
class BuildPlatformGraph(DjangoObjectType):
class Meta:
model = BuildPlatform
class MachinePlatformGraph(DjangoObjectType):
class Meta:
model = MachinePlatform
class MachineGraph(DjangoObjectType):
class Meta:
model = Machine
class JobTypeGraph(DjangoObjectType):
class Meta:
model = JobType
class JobGroupGraph(DjangoObjectType):
class Meta:
model = JobGroup
class JobLogGraph(DjangoObjectType):
class Meta:
model = JobLog
class FailureLineGraph(DjangoObjectType):
class Meta:
model = FailureLine
class GroupGraph(DjangoObjectType):
class Meta:
model = Group
class ProductGraph(DjangoObjectType):
class Meta:
model = Product
class FailureClassificationGraph(DjangoObjectType):
class Meta:
model = FailureClassification
class RepositoryGraph(DjangoObjectType):
class Meta:
model = Repository
class OptionCollectionGraph(DjangoObjectType):
class Meta:
model = OptionCollection
class OptionGraph(DjangoObjectType):
class Meta:
model = Option
class PushGraph(DjangoObjectType):
class Meta:
model = Push
filter_fields = ('revision', )
interfaces = (graphene.relay.Node, )
field_map = {
"buildPlatform": ("build_platform", "select"),
"jobLog": ("job_log", "prefetch"),
"jobType": ("job_type", "select"),
"jobGroup": ("job_type__job_group", "select"),
"failureClassification": ("failure_classification", "prefetch"),
"failureLine": ("job_log__failure_line", "prefetch"),
"group": ("job_log__failure_line__group", "prefetch"),
}
jobs = helpers.OptimizableFilterConnectionField(JobGraph)
def resolve_jobs(self, args, context, info):
return helpers.optimize(Job.objects.filter(push=self, **args),
info,
self.field_map)
class Query(graphene.ObjectType):
all_option_collections = graphene.List(OptionCollectionGraph)
all_pushes = DjangoFilterConnectionField(PushGraph)
def resolve_all_option_collections(self, args, context, info):
field_map = {
"option": ("option", "select"),
}
return helpers.optimize(OptionCollection.objects.all(),
info,
field_map)
def resolve_all_pushes(self, args, context, info):
return Push.objects.filter(**args)
schema = graphene.Schema(query=Query)
@gotexis
Copy link

gotexis commented Apr 12, 2019

        # This eliminates select_related and prefetch_related, so disabling
        # for now:
        # queryset = default_queryset & queryset

It seems like greying this out also eliminates filtering for me...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment