MrThearMan / graphene-django-query-optimizer

Automatically optimize SQL queries in Graphene-Django schemas.
https://pypi.org/project/graphene-django-query-optimizer/
MIT License
13 stars 4 forks source link

Relay doesn't seem to work with Django's `ContentType` #155

Closed rsomani95 closed 1 month ago

rsomani95 commented 1 month ago

I have read the docs thoroughly before making this bug report.

I have read through other open issues, and my issue is not a duplicate.

What version of the library you are using?

0.10.1

Which python version are you using?

3.12

What operating system are you on?

Mac

Description

There seems to be a bug where Relay doesn't play nice with Django's ContentType framework. I've documented steps below to reproduce this behavior.

First, modify the test project like so:

class TagType(DjangoObjectType):
    ...

    class Meta:
        interfaces = (relay.Node,)
        ...

class PostalCodeType(DjangoObjectType):
    # tags = DjangoListField(lambda: TagType)
    tags = DjangoConnectionField(lambda: TagType)
    ...

and run this query:

{
  allPostalCodes {
    pk
    tags {
      edges {
        node {
          id
        }
      }
    }
  }
}

I'm met with this error:

      "message": "'GenericRel' object has no attribute 'attname'"

Here's the full trace:

Bad Request: /graphql/
2024-10-06T23:48:54+0000 | WARNING | django.utils.log.log_response:241 | Bad Request: /graphql/
[06/Oct/2024 23:48:54] "POST /graphql/ HTTP/1.1" 400 970
2024-10-06T23:48:58+0000 | INFO | config.logging.resolve:41 | Traceback (most recent call last):
  File "/Users/rahulsomani/git/graphene-django-query-optimizer/example_project/config/logging.py", line 39, in resolve
    return next_func(root, info, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/rahulsomani/git/graphene-django-query-optimizer/query_optimizer/fields.py", line 193, in list_resolver
    return optimize(queryset, info, max_complexity=max_complexity)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/rahulsomani/git/graphene-django-query-optimizer/query_optimizer/compiler.py", line 43, in optimize
    queryset = optimizer.optimize_queryset(queryset)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/rahulsomani/git/graphene-django-query-optimizer/query_optimizer/optimizer.py", line 106, in optimize_queryset
    results = self.process(queryset, filter_info)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/rahulsomani/git/graphene-django-query-optimizer/query_optimizer/optimizer.py", line 153, in process
    prefetch = optimizer.process_prefetch(name, nested_results, nested_filter_info)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/rahulsomani/git/graphene-django-query-optimizer/query_optimizer/optimizer.py", line 181, in process_prefetch
    queryset = self.paginate_prefetch_queryset(queryset, filter_info)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/rahulsomani/git/graphene-django-query-optimizer/query_optimizer/optimizer.py", line 200, in paginate_prefetch_queryset
    field_name = remote_field.name if isinstance(field, models.ManyToManyField) else remote_field.attname
                                                                                     ^^^^^^^^^^^^^^^^^^^^
AttributeError: 'GenericRel' object has no attribute 'attname'

[06/Oct/2024 23:48:58] "POST /graphql/ HTTP/1.1" 200 1025
2024-10-06T23:48:59+0000 | INFO | config.logging.resolve:41 | Traceback (most recent call last):
  File "/Users/rahulsomani/git/graphene-django-query-optimizer/example_project/config/logging.py", line 39, in resolve
    return next_func(root, info, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/rahulsomani/git/graphene-django-query-optimizer/query_optimizer/fields.py", line 193, in list_resolver
    return optimize(queryset, info, max_complexity=max_complexity)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/rahulsomani/git/graphene-django-query-optimizer/query_optimizer/compiler.py", line 43, in optimize
    queryset = optimizer.optimize_queryset(queryset)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/rahulsomani/git/graphene-django-query-optimizer/query_optimizer/optimizer.py", line 106, in optimize_queryset
    results = self.process(queryset, filter_info)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/rahulsomani/git/graphene-django-query-optimizer/query_optimizer/optimizer.py", line 153, in process
    prefetch = optimizer.process_prefetch(name, nested_results, nested_filter_info)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/rahulsomani/git/graphene-django-query-optimizer/query_optimizer/optimizer.py", line 181, in process_prefetch
    queryset = self.paginate_prefetch_queryset(queryset, filter_info)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/rahulsomani/git/graphene-django-query-optimizer/query_optimizer/optimizer.py", line 200, in paginate_prefetch_queryset
    field_name = remote_field.name if isinstance(field, models.ManyToManyField) else remote_field.attname
                                                                                     ^^^^^^^^^^^^^^^^^^^^
AttributeError: 'GenericRel' object has no attribute 'attname'
MrThearMan commented 1 month ago

Yup, this is an issue with how the field name for nested relay connection fields in case of Generic Relations is determined. Thankfully a small fix. Fixed in 0.10.2.

rsomani95 commented 1 month ago

Thanks!