Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prefetching for generic relations does not seem to work #75

Open
crazyscientist opened this issue Sep 2, 2021 · 3 comments
Open

Prefetching for generic relations does not seem to work #75

crazyscientist opened this issue Sep 2, 2021 · 3 comments

Comments

@crazyscientist
Copy link

Hi there,

first of all, thanks for the project, and I'd like to point out that prefetching works for ForeignKey and ManyToMany relations. Somehow only GenericForeignKey is not recognized as optimizable.

Versions

Package Version
Python 3.8
Django 2.2
graphene 2.1.9
graphene-django 2.15.0
graphene-django-optimizer 0.8.0
graphql-core 2.3.2
graphql-relay 2.0.1

Models

class Comment(models.Model):
    text = models.TextField(null=True, blank=True)
    when = models.DateTimeField(null=True)
    who = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
    remote_id = models.PositiveIntegerField(null=True)
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')


class CommentMixin(models.Model):
    comments = GenericRelation(Comment)

    class Meta:
        abstract = True


class Incident(CommentMixin, models.Model):
    incident_id = models.IntegerField(unique=True)

Schema

class CommentType(OptimizedDjangoObjectType):
    class Meta:
        model = models.Comment
        interfaces = (graphene.relay.Node,)


class IncidentType(OptimizedDjangoObjectType):
    comments = DjangoConnectionField(CommentType)

    @resolver_hints(model_field=models.Comment)
    def resolve_comments(self, info, **kwargs):
        return self.comments.all()

Query

{
  incidents(first: 20) {
    edges {
      node {
        id
        comments {
          edges {
            node {
              text
              when
              who {
                username
              }
            }
          }
        }
      }
    }
  }
}

Problem

For non-generic relations the optimization works as expected, but with a generic relation (in the above example: comments) it does not. Based on the raw SQL queries executed, it looks like for every incident the comments are queried and for each comment the related User model is queried.

Expected

Generic relations are tough. Django allows prefetching of the model that is a member of the generic relation, but nothing further, e.g.:

Incident.objects.all().prefetch_related("comments")  # works
Incident.objects.all().prefetch_related("comments", "comments__who")  # does not work

It would be unfair to expect that the optimizer overcomes this limitation of Django. It would be really great, if the optimizer could prefetch the comments in this example.

@crazyscientist
Copy link
Author

crazyscientist commented Sep 2, 2021

Maybe this is related to #45?

@tfoxy
Copy link
Owner

tfoxy commented Sep 19, 2021

Hi @crazyscientist ! The detection of foreign key id is done manually by using isinstance (

def _is_foreign_key_id(self, model_field, name):
return (
isinstance(model_field, ForeignKey)
and model_field.name != name
and model_field.get_attname() == name
)
). You can try to add GenericForeignKey to the detection. If that works, feel free to create a PR together with a test for the GenericForeignKey case.

@tfoxy
Copy link
Owner

tfoxy commented Sep 19, 2021

Looking into this a little bit deeper, I think it would require some changes to the code to support this properly. At the time I made this, I only had a few generic relations in the codebase I was applying this library. But if a codebase has many fields like that, I see the value that solving this issue would provide.

If you have the time, feel free to create a PR. Will gladly review it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants