djipidi / graphene_django_crud

Turns the django ORM into a graphql API
MIT License
31 stars 6 forks source link

Field `Model.field` cannot be both deferred and traversed using select_related at the same time #5

Closed HellWolf93 closed 3 years ago

HellWolf93 commented 3 years ago

With this models:

class Order(models.Model):
    # fields...

class Payment(models.Model):
    order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name='payments')

And its types:

class OrderType(DjangoCRUDObjectType):
    class Meta:
        model = Order
        interfaces = (relay.Node,)

    total_price = graphene.Decimal()

    def resolve_total_price(parent, info, **kwargs):
        return 0.0

class PaymentType(DjangoCRUDObjectType):
    class Meta:
        model = Payment
        interfaces = (relay.Node,)

When executing this query:

{   
  payments {
    edges {
      node {
        order {
          totalPrice
        }
      }
    }
  }
}

It fails with Field Payment.order cannot be both deferred and traversed using select_related at the same time. It happens because on https://github.com/djipidi/graphene_django_crud/blob/7e54d9a21e7c7cdcef7dd196dfc8312d0d3b810c/graphene_django_crud/types.py#L384 when the field is of type OneToOneField, OneToOneRel, ForeignKey it is added to related_fields, but if I don't query at least one non-relation field in Order then the order field is never added to only, resulting in a queryset like Payment.object.select_related('order').only('pk') which is not correct in Django.

This can be solved if I query for at least one non-relation field (i.e, id) in Order, but this forces to query for fields that maybe the user does not need.

I don't know if I explained well, I would be happy to make more clarifications.

djipidi commented 3 years ago

Thank you for your feedback and for the precision of the diagnosis.

To resolve I think the queryset should be like this even though no fields are needed. Payment.object.select_related('order').only('id', 'order__id')

While waiting for a corrective you can work around the problem using the decorator @resolver_hints(only=["id"]).

Best regard