tfoxy / graphene-django-optimizer

Optimize database access inside graphene queries
MIT License
427 stars 86 forks source link

TypeError("Cannot call select_related() after .values() or .values_list()") #80

Open jathanism opened 2 years ago

jathanism commented 2 years ago

Python version: 3.8 Django version: 3.1.14

I am a maintainer of Nautobot and we recently implemented the optimizer in our v1.2.0 release. We just got this bug report related to trying to query a related object and filtering only the id field for it, which results in this error:

{
  "errors": [
    {
      "message": "Cannot call only() after .values() or .values_list()",
      "locations": [
        {
          "line": 5,
          "column": 3
        }
      ],
      "path": [
        "device",
        "rel_cluster_to_device"
      ]
    }
  ],
  "data": {
    "device": {
      "name": "R10-S3",
      "rel_cluster_to_device": null
    }
  }
}

And this server-side traceback:

Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]: An error occurred while resolving field DeviceType.rel_device_to_vm
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]: Traceback (most recent call last):
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]:   File "/opt/nautobot/lib/python3.9/site-packages/graphql/execution/executor.py", line 452, in resolve_or_error
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]:     return executor.execute(resolve_fn, source, info, **args)
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]:   File "/opt/nautobot/lib/python3.9/site-packages/graphql/execution/executors/sync.py", line 16, in execute
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]:     return fn(*args, **kwargs)
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]:   File "/opt/nautobot/lib/python3.9/site-packages/nautobot/core/graphql/generators.py", line 139, in resolve_relationship
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]:     queryset_ids = gql_optimizer.query(
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]:   File "/opt/nautobot/lib/python3.9/site-packages/graphene_django_optimizer/query.py", line 40, in query
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]:     return QueryOptimizer(info, **options).optimize(queryset)
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]:   File "/opt/nautobot/lib/python3.9/site-packages/graphene_django_optimizer/query.py", line 60, in optimize
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]:     return store.optimize_queryset(queryset)
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]:   File "/opt/nautobot/lib/python3.9/site-packages/graphene_django_optimizer/query.py", line 378, in optimize_queryset
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]:     queryset = queryset.select_related(*self.select_list)
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]:   File "/opt/nautobot/lib/python3.9/site-packages/django/db/models/query.py", line 1047, in select_related
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]:     raise TypeError("Cannot call select_related() after .values() or .values_list()")
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]: TypeError: Cannot call select_related() after .values() or .values_list()
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]: Traceback (most recent call last):
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]:   File "/opt/nautobot/lib/python3.9/site-packages/graphql/execution/executor.py", line 452, in resolve_or_error
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]:     return executor.execute(resolve_fn, source, info, **args)
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]:   File "/opt/nautobot/lib/python3.9/site-packages/graphql/execution/executors/sync.py", line 16, in execute
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]:     return fn(*args, **kwargs)
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]:   File "/opt/nautobot/lib/python3.9/site-packages/nautobot/core/graphql/generators.py", line 139, in resolve_relationship
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]:     queryset_ids = gql_optimizer.query(
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]:   File "/opt/nautobot/lib/python3.9/site-packages/graphene_django_optimizer/query.py", line 40, in query
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]:     return QueryOptimizer(info, **options).optimize(queryset)
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]:   File "/opt/nautobot/lib/python3.9/site-packages/graphene_django_optimizer/query.py", line 60, in optimize
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]:     return store.optimize_queryset(queryset)
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]:   File "/opt/nautobot/lib/python3.9/site-packages/graphene_django_optimizer/query.py", line 378, in optimize_queryset
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]:     queryset = queryset.select_related(*self.select_list)
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]:   File "/opt/nautobot/lib/python3.9/site-packages/django/db/models/query.py", line 1047, in select_related
Jan 04 10:38:42 dh01-a-06-18 nautobot-server[2465943]:     raise TypeError("Cannot call select_related() after .values() or .values_list()")

This query reproduces the error:

query {
  device(id: "a7c1dcc8-80eb-4754-9981-e2f63a9ab11e") {
    id
    name
    rel_bgp_device_router_id {
      id
    }
  }
}

But the following variant query works just fine:

query {
  device(id: "a7c1dcc8-80eb-4754-9981-e2f63a9ab11e") {
    id
    name
    rel_bgp_device_router_id {
      id
      address
    }
  }
}

Unfortunately it's non-trivial to try it for yourself. We have a temporary workaround that doesn't fix anything in graphene-django-optimizer but just wraps the calls in a try...except block. See: https://github.com/nautobot/nautobot/pull/1230

We'd be happy to try to help fix this bug, but just in case you might be able to fix it quicker, I wanted to report it here. Thanks for this great project!

Summary

tfoxy commented 2 years ago

Hi @jathanism! This issue skipped my radar and I just noticed because of another one that was reported. If I find time in the future I will try to debug and fix this (I'm a little rusty on Python and this project in general so it can take me some time).