Repository: saleor/saleor

Issue:
Optimize queries for product list in the dashboard
```
{
  products(first: 50) {
    edges {
      node {
        id
        attributes {
          attribute {
            id
          }
          values {
            id
            name
          }
        }
      }
    }
  }
}
```

A query like this (which is performed by our product list in the dashboard) results in 131 DB queries (37 products in DB, 124 variants). I looked into the resolver and in seems that prefetching is added there, but its cache it's lost in this line:

https://github.com/mirumee/saleor/blob/5f2dde844f806219e385175c58366da1c157090b/saleor/graphql/product/types/products.py#L104

First of all, it seems that prefetching is lost when using custom queryset methods. We could drop this function but inside it, we anyway use `get_public_attributes` which does `filter()` which ignores prefetching as well.


Discussion:

Resolving patch:
diff --git a/saleor/graphql/product/types/products.py b/saleor/graphql/product/types/products.py
--- a/saleor/graphql/product/types/products.py
+++ b/saleor/graphql/product/types/products.py
@@ -86,6 +86,7 @@ def resolve_attribute_list(
         - product_type -> attribute[rel] -> attribute
     """
     resolved_attributes = []
+    attributes_qs = None
 
     # Retrieve the product type
     if isinstance(instance, models.Product):
@@ -93,6 +94,8 @@ def resolve_attribute_list(
         product_type_attributes_assoc_field = "attributeproduct"
         assigned_attribute_instance_field = "productassignments"
         assigned_attribute_instance_filters = {"product_id": instance.pk}
+        if hasattr(product_type, "storefront_attributes"):
+            attributes_qs = product_type.storefront_attributes
     elif isinstance(instance, models.ProductVariant):
         product_type = instance.product.product_type
         product_type_attributes_assoc_field = "attributevariant"
@@ -102,8 +105,9 @@ def resolve_attribute_list(
         raise AssertionError(f"{instance.__class__.__name__} is unsupported")
 
     # Retrieve all the product attributes assigned to this product type
-    attributes_qs = getattr(product_type, product_type_attributes_assoc_field)
-    attributes_qs = attributes_qs.get_visible_to_user(user)
+    if not attributes_qs:
+        attributes_qs = getattr(product_type, product_type_attributes_assoc_field)
+        attributes_qs = attributes_qs.get_visible_to_user(user)
 
     # An empty QuerySet for unresolved values
     empty_qs = models.AttributeValue.objects.none()
@@ -590,8 +594,13 @@ def resolve_price(root: models.Product, info):
     @staticmethod
     @gql_optimizer.resolver_hints(
         prefetch_related=[
-            "product_type__attributeproduct__productassignments__values",
-            "product_type__attributeproduct__attribute",
+            Prefetch(
+                "product_type__attributeproduct",
+                queryset=models.AttributeProduct.objects.filter(
+                    attribute__visible_in_storefront=True
+                ).prefetch_related("productassignments__values", "attribute"),
+                to_attr="storefront_attributes",
+            )
         ]
     )
     def resolve_attributes(root: models.Product, info):


Test patch: