Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django model objects and Graphene get_node

So far I was able to play around with Graphene without needing DjangoObjectType. I try to avoid it as I'm not planning to stay too close to my Django model classes. However I'm having issues when implementing Relay with Graphene:

class HouseholdNode(graphene.ObjectType):
  class Meta:
    interfaces = (graphene.relay.Node,)

  name = graphene.String()

  @classmethod
  def get_node(cls, info, id):
    return Household.objects.get(pk=id)

This fails with the following error:

Abstract type Node must resolve to an Object type at runtime for field Query.node with value "Test", received "None".

"Test" comes straight from Household's __str__ function.

Next try:

  @classmethod
  def get_node(cls, info, id):
    return cls(Household.objects.get(pk=id))

cls is HouseholdNode. However this yields the wrong result:

"node": {
  "id": "SG91c2Vob2xkOlRlc3Q=",
  "name": null
}

The ID is actually "Test".

Solution that works:

  @classmethod
  def get_node(cls, info, id):
    household = Household.objects.get(pk=id)
    return cls(name=household.name)

However I highly doubt that this is all Graphene can do for me. Do I really have to wrap the real data object into HouseholdNode? I already have resolve functions, can't those simply be used instead?

The documentation is highly lacking on those edges, please enlighten me.

like image 477
stschindler Avatar asked May 12 '26 07:05

stschindler


1 Answers

Abstract Types (like graphene.relay.node.Node) are resolved by the executor using graphql.execution.executor.complete_abstract_value.

Practically speaking, your ObjectTypes with Node as an interface are passed up from graphene to the graphql layer, each wrapped as a GrapheneInterfaceType. The resolve_type of each of those objects (which ultimately provides the source of your error) calls graphql.execution.executor.get_default_resolve_type_fn.

This function narrows down the possible types which could be returned (possible_types) and then iterates over those types checking if the is_type_of attribute is callable and whether it returns True. It's important to note that the possible_types are user-defined subclasses of Node, inheriting from graphene.types.objecttype.ObjectType which has is_type_of = None. Ergo, you get the GraphQLError as no type is resolved.

The solution is to define a is_type_of method on your object types (or create an abstract ObjectType you can subclass with this already implemented). For example, here is the code in graphene-sqlalchemy that implements the is_type_of logic, and for graphene-django that code is here.

@classmethod
def is_type_of(cls, root, info):
    if isinstance(root, SimpleLazyObject):
        root._setup()
        root = root._wrapped
    if isinstance(root, cls):
        return True
    if not is_valid_django_model(type(root)):
        raise Exception((
            'Received incompatible instance "{}".'
        ).format(root))

    model = root._meta.model._meta.concrete_model
    return model == cls._meta.model
like image 112
Devin Avatar answered May 14 '26 19:05

Devin



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!