mirror of
https://github.com/graphql-python/graphene-django.git
synced 2025-07-13 09:42:32 +03:00
Adding query optimization
This commit is contained in:
parent
0588f89b89
commit
2d2c5ee889
|
@ -4,6 +4,7 @@ from functools import partial
|
|||
# from graphene.relay import is_node
|
||||
from graphene.types.argument import to_arguments
|
||||
from ..fields import DjangoConnectionField
|
||||
from ..optimization import optimize_queryset
|
||||
from .utils import get_filtering_args_from_filterset, get_filterset_class
|
||||
|
||||
|
||||
|
@ -75,6 +76,7 @@ class DjangoFilterConnectionField(DjangoConnectionField):
|
|||
data=filter_kwargs,
|
||||
queryset=default_manager.get_queryset()
|
||||
).qs
|
||||
qs = optimize_queryset(default_manager.model, qs, info.field_asts[0])
|
||||
|
||||
return super(DjangoFilterConnectionField, cls).connection_resolver(
|
||||
resolver,
|
||||
|
|
82
graphene_django/optimization.py
Normal file
82
graphene_django/optimization.py
Normal file
|
@ -0,0 +1,82 @@
|
|||
from collections import namedtuple
|
||||
|
||||
from django.db.models import ForeignKey
|
||||
from django.db.models.fields.reverse_related import ForeignObjectRel
|
||||
from graphene.utils.str_converters import to_snake_case
|
||||
|
||||
from .registry import get_global_registry
|
||||
from .utils import get_related_model
|
||||
|
||||
REGISTRY = get_global_registry()
|
||||
SELECT = 'select'
|
||||
PREFETCH = 'prefetch'
|
||||
RelatedSelection = namedtuple('RelatedSelection', ['name', 'fetch_type'])
|
||||
|
||||
|
||||
def model_fields_as_dict(model):
|
||||
return dict((f.name, f) for f in model._meta.get_fields())
|
||||
|
||||
|
||||
def get_related_fetches_for_model(model, graphql_ast):
|
||||
model_fields = model_fields_as_dict(model)
|
||||
selections = graphql_ast.selection_set.selections
|
||||
|
||||
graphene_obj_type = REGISTRY.get_type_for_model(model)
|
||||
optimizations = {}
|
||||
if graphene_obj_type and graphene_obj_type._meta.optimizations:
|
||||
optimizations = graphene_obj_type._meta.optimizations
|
||||
|
||||
relateds = []
|
||||
|
||||
for selection in selections:
|
||||
selection_name = to_snake_case(selection.name.value)
|
||||
selection_field = model_fields.get(selection_name, None)
|
||||
|
||||
try:
|
||||
related_model = get_related_model(selection_field)
|
||||
except:
|
||||
# This is not a ForeignKey or Relation, check manual optimizations
|
||||
manual_optimizations = optimizations.get(selection_name)
|
||||
if manual_optimizations:
|
||||
for manual_select in manual_optimizations.get(SELECT, []):
|
||||
relateds.append(RelatedSelection(manual_select, SELECT))
|
||||
for manual_prefetch in manual_optimizations.get(PREFETCH, []):
|
||||
relateds.append(RelatedSelection(manual_prefetch, PREFETCH))
|
||||
|
||||
continue
|
||||
|
||||
query_name = selection_field.name
|
||||
if isinstance(selection_field, ForeignObjectRel):
|
||||
query_name = selection_field.field.related_query_name()
|
||||
|
||||
nested_relateds = get_related_fetches_for_model(related_model, selection)
|
||||
|
||||
related_type = PREFETCH # default to prefetch, it's safer
|
||||
if isinstance(selection_field, ForeignKey):
|
||||
related_type = SELECT # we can only select for ForeignKeys
|
||||
|
||||
if nested_relateds:
|
||||
for related in nested_relateds:
|
||||
full_name = '{0}__{1}'.format(query_name, related.name)
|
||||
|
||||
nested_related_type = PREFETCH
|
||||
if related_type == SELECT and related.fetch_type == SELECT:
|
||||
nested_related_type = related_type
|
||||
|
||||
relateds.append(RelatedSelection(full_name, nested_related_type))
|
||||
else:
|
||||
relateds.append(RelatedSelection(query_name, related_type))
|
||||
|
||||
return relateds
|
||||
|
||||
|
||||
def optimize_queryset(model, queryset, graphql_ast):
|
||||
relateds = get_related_fetches_for_model(model, graphql_ast)
|
||||
|
||||
for related in relateds:
|
||||
if related.fetch_type == SELECT:
|
||||
queryset = queryset.select_related(related.name)
|
||||
else:
|
||||
queryset = queryset.prefetch_related(related.name)
|
||||
|
||||
return queryset
|
|
@ -10,6 +10,7 @@ from graphene.types.utils import merge, yank_fields_from_attrs
|
|||
from graphene.utils.is_base_type import is_base_type
|
||||
|
||||
from .converter import convert_django_field_with_choices
|
||||
from .optimization import optimize_queryset
|
||||
from .registry import Registry, get_global_registry
|
||||
from .utils import (DJANGO_FILTER_INSTALLED, get_model_fields,
|
||||
is_valid_django_model)
|
||||
|
@ -55,6 +56,7 @@ class DjangoObjectTypeMeta(ObjectTypeMeta):
|
|||
only_fields=(),
|
||||
exclude_fields=(),
|
||||
interfaces=(),
|
||||
optimizations=None,
|
||||
skip_registry=False,
|
||||
registry=None
|
||||
)
|
||||
|
@ -118,7 +120,9 @@ class DjangoObjectType(six.with_metaclass(DjangoObjectTypeMeta, ObjectType)):
|
|||
|
||||
@classmethod
|
||||
def get_node(cls, id, context, info):
|
||||
query = cls._meta.model._meta.default_manager
|
||||
query = optimize_queryset(cls._meta.model, query, info.field_asts[0])
|
||||
try:
|
||||
return cls._meta.model.objects.get(pk=id)
|
||||
return query.get(pk=id)
|
||||
except cls._meta.model.DoesNotExist:
|
||||
return None
|
||||
|
|
Loading…
Reference in New Issue
Block a user