strawberry-graphql / strawberry-django

Strawberry GraphQL Django extension
https://strawberry.rocks/docs/django
MIT License
421 stars 122 forks source link

Nested annotations and optimizations break when using strawberry_django.auth.current_user #594

Open pfcodes opened 4 months ago

pfcodes commented 4 months ago

Describe the Bug

Annotations and optimizations don't work on fields created with strawberry_django.auth.current_user()

With the following code:

@strawberry.type
class Query:
    user: User = strawberry_django.node(extensions=[IsAuthenticated()])
    current_user: User = strawberry_django.auth.current_user()
@strawberry_django.type(UserModel)
class User(Actor, relay.Node):
    friend_count: int = strawberry_django.field(annotate=Count("friends"))

friend_count will work on the user field just fine, but attempting to access it on current_user and you get this error:

 {
  "data": null,
  "errors": [
    {
      "message": "'User' object has no attribute 'friend_count'",
      "locations": [
        {
          "line": 3,
          "column": 5
        }
      ],
      "path": [
        "currentUser",
        "friendCount"
      ]
    }
  ]
}

It's not just annotations, it happens for all the optimizations too.

System Information

Additional Context

There is another similar issue here. They might be related.

Upvote & Fund

Fund with Polar

bellini666 commented 4 months ago

annotate support still have some issues, like this case

The issue here is similar: The current_user resolver will return a user instead of a queryset containing the user, meaning the optimizer can't "annotate" it anymore.

I'm going to try to see if we can change current_user to return a queryset instead to support those cases, but for now what you can do is to define your own custom resolver like this:

from strawberry_django.auth.queries import resolve_current_user

@strawberry.type
class Query:
    @strawberry_django.field
    def current_user(self, info: Info):
        user = resolve_current_user(info)
        return User.objects.filter(pk=user.pk)

This is going to cause an extra DB query, but that one can be annotated.

Annoter option would be to not use annotate and instead rely on a data loader for that friend_count field (which IMO is better)

erwinfeser commented 1 month ago

annotate support still have some issues, like this case

The issue here is similar: The current_user resolver will return a user instead of a queryset containing the user, meaning the optimizer can't "annotate" it anymore.

I'm going to try to see if we can change current_user to return a queryset instead to support those cases, but for now what you can do is to define your own custom resolver like this:

from strawberry_django.auth.queries import resolve_current_user

@strawberry.type
class Query:
    @strawberry_django.field
    def current_user(self, info: Info):
        user = resolve_current_user(info)
        return User.objects.filter(pk=user.pk)

This is going to cause an extra DB query, but that one can be annotated.

Annoter option would be to not use annotate and instead rely on a data loader for that friend_count field (which IMO is better)

Could you show an example about relying on a data loader for that friend_count?

bellini666 commented 1 month ago

Hey @erwinfeser ,

Sure, there's an example in this comment here: https://github.com/strawberry-graphql/strawberry-django/issues/549#issuecomment-2178761605