From 76465dc39118c1cec010a231f2cbd99d157cfcff Mon Sep 17 00:00:00 2001
From: Felix Meziere <felix.meziere@gmail.com>
Date: Fri, 15 Oct 2021 01:29:16 +0100
Subject: [PATCH] Allow for hints with same value on multiple different
 resolvers of a given DjangoObjectType. (#76)

---
 graphene_django_optimizer/query.py |  4 +++-
 tests/schema.py                    | 18 +++++++++++++++++
 tests/test_query.py                | 31 ++++++++++++++++++++++++++++++
 3 files changed, 52 insertions(+), 1 deletion(-)

diff --git a/graphene_django_optimizer/query.py b/graphene_django_optimizer/query.py
index e64f041..78652a7 100644
--- a/graphene_django_optimizer/query.py
+++ b/graphene_django_optimizer/query.py
@@ -269,7 +269,9 @@ class QueryOptimizer(object):
         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..f8124c7 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,20 @@ class ItemInterface(graphene.Interface):
     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..6dedac0 100644
--- a/tests/test_query.py
+++ b/tests/test_query.py
@@ -585,3 +585,34 @@ 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)
-- 
GitLab