strawberry-graphql / strawberry-django

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

Strawberry Django Plus migration problem: get_queryset for nested fields #318

Open iamcrookedman opened 1 year ago

iamcrookedman commented 1 year ago

Request:

{
  standConnection{
    edges{
      node{
        planningAreas{
          id
        }
      }
    }
  }
}

Models:

class PlanningArea(models.Model):
    ...
    objects = PlanningAreaQuerySet.as_manager()

class Stand(models.Model):
    ...
    objects = StandManager()

Types:

class BaseAccessResolverNodeTypeMixin:
    @classmethod
    def get_queryset(cls, queryset: BaseModelAvailableQuerySet, info: Info, **kwargs) -> BaseModelAvailableQuerySet:
        return queryset.filter_available(user=info.context.request.user)

@strawberry.django.type(PlanningArea)
class PlanningAreaGqlType(BaseAccessResolverNodeTypeMixin, relay.Node):
    name: auto
    ...

@strawberry.django.type(Stand)
class StandGqlType(BaseAccessResolverNodeTypeMixin, relay.Node):
    planning_areas: list[Annotated["PlanningAreaGqlType", lazy("apps.planning_areas.gql.types")]]
    ...

For both models, the objects manager was redefined and it is inherited from BaseModelAvailableQuerySet

The problem is the following: when I make a request it starts calling get_queryset method for PlanningAreaGqlType (the old library did not have this behavior) and it fails with an error AttributeError: 'QuerySet' object has no attribute 'filter_available'

Everything is fine for the latest version of strawberry-django-plus lib

Upvote & Fund

Fund with Polar

bellini666 commented 1 year ago

Hey @iamcrookedman ,

What is happening here is that there was an issue at strawberr-graphql-django, which strawberry-django-plus used to use as well, and got fixed together with the merge.

When you have a nested related manager, django will use the _base_manager instead of the instead of the _default_manager, which is set to the one you define on objects. You can read at the django documentation why it does that.

Previously strawberry-graphql-django used to use .objects.all() instead of ._base_manager.all(), which goes against og what should be used based on the django documentation, and it is not what users would expect.

Having said that, if you want your model to always use your custom manager, you can override its _base_manager, but ideally you will probably want to consider if you really want nested manager to use your custom manager, due to the reasons mentioned on the django documentation.

bellini666 commented 1 year ago

Marking this as documentation since it would be nice to add some documentation about how this works

Eraldo commented 5 months ago

I ran into this as well today. :) And luckily (and thanks to @bellini666 for pointing me to it), this is already known and solved.

I just wanted to confirm that I set the base_manager_name on my model's Meta class it now it works fine.

Example:

class Tag(models.Model):
    class Meta:
        base_manager_name = "objects"

    objects = TagQuerySet.as_manager()

Now it also works when used as a nested type (as a reverse relationship of another model). Example:

query FruitTags {
  fruit(id: "VGFnOjE=") {
    id
    tags {
      edges {
        node {
          id
          name
        }
      }
    }
  }
}

👍