From 9d63e37051a20e8dfc583fb9f0781f3b2cad3100 Mon Sep 17 00:00:00 2001 From: Felix Meziere Date: Thu, 16 Sep 2021 12:17:27 +0100 Subject: [PATCH] Allow for hints with same value on multiple different resolvers of a given DjangoObjectType. --- graphene_django_optimizer/query.py | 2 +- tests/schema.py | 17 +++++++++++++++++ tests/test_query.py | 30 ++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/graphene_django_optimizer/query.py b/graphene_django_optimizer/query.py index e64f041..8ef4d6a 100644 --- a/graphene_django_optimizer/query.py +++ b/graphene_django_optimizer/query.py @@ -269,7 +269,7 @@ def _add_optimization_hints(self, source, target): if source: if not is_iterable(source): source = (source,) - target += source + target += [source_item for source_item in source if source_item not in target] def _get_name_from_resolver(self, resolver): optimization_hints = self._get_optimization_hints(resolver) diff --git a/tests/schema.py b/tests/schema.py index fc30203..42208df 100644 --- a/tests/schema.py +++ b/tests/schema.py @@ -52,6 +52,10 @@ class ItemInterface(graphene.Interface): "tests.schema.ItemType", name=graphene.String(required=True), ) + aux_filtered_children = graphene.List( + "tests.schema.ItemType", + name=graphene.String(required=True), + ) children_custom_filtered = gql_optimizer.field( ConnectionField("tests.schema.ItemConnection", filter_input=ItemFilterInput()), prefetch_related=_prefetch_children, @@ -82,6 +86,19 @@ def resolve_aux_children_names(root, info): def resolve_filtered_children(root, info, name): return getattr(root, "gql_filtered_children_" + name) + @gql_optimizer.resolver_hints( + prefetch_related=lambda info, name: Prefetch( + "children", + queryset=gql_optimizer.query(Item.objects.filter(name=f"some_prefix {name}"), info), + # Different queryset than resolve_filtered_children but same to_attr, on purpose + # to check equality of Prefetch is based only on to_attr attribute, as it is implemented in Django. + to_attr="gql_filtered_children_" + name, + ), + ) + def resolve_aux_filtered_children(root, info, name): + return getattr(root, "gql_filtered_children_" + name) + + def resolve_children_custom_filtered(root, info, *_args): return getattr(root, "gql_custom_filtered_children") diff --git a/tests/test_query.py b/tests/test_query.py index fa7339d..2a50b0c 100644 --- a/tests/test_query.py +++ b/tests/test_query.py @@ -585,3 +585,33 @@ def test_should_only_use_the_only_and_not_select_related(): items = gql_optimizer.query(qs, info) optimized_items = qs.only("id", "name") assert_query_equality(items, optimized_items) + +@pytest.mark.django_db +def test_should_accept_two_hints_with_same_prefetch_to_attr_and_keep_one_of_them(): + info = create_resolve_info( + schema, + """ + query { + items(name: "foo") { + filteredChildren(name: "bar") { + id + name + } + auxFilteredChildren(name: "bar") { # Same name to generate Prefetch with same to_attr + id + name + } + } + } + """, + ) + qs = Item.objects.filter(name="foo") + items = gql_optimizer.query(qs, info) + optimized_items = qs.prefetch_related( + Prefetch( + "children", + queryset=Item.objects.filter(name="bar").only('id','name'), + to_attr="gql_filtered_children_foo", + ) + ) + assert_query_equality(items, optimized_items)