GraphQL Design with Circular Dependency

Viewed 1243

In my structure I would like to introduce circular dependency like below to avoid submiting two separate queries to backend. Can someone advise how this can be done in Python.

Below is the sample code:

parent.py

import graphene

class Parent(graphene.ObjectType):
    id = graphene.ID()
    name = graphene.String()
    child= graphene.Field(Child)

child.py

import graphene

class Child(graphene.ObjectType):
    id = graphene.ID()
    name = graphene.String()
    parent = graphene.Field(Parent)

test.py

from parent import Parent

print("TEST")

Error

ImportError: cannot import name 'Parent' from partially initialized module 'parent' (most likely due to a circular import) 

Update The following also doesn't work (circular import error)

import graphene

class Child(graphene.ObjectType):
    import app.parent as P
    id = graphene.ID()
    name = graphene.String()
    parent = graphene.Field(P.Parent)

...
import graphene

class Parent(graphene.ObjectType):
    import app.child as C
    id = graphene.ID()
    name = graphene.String()
    child = graphene.Field(C.Child)
...

from app.parent import Parent

print("TEST")

AttributeError: partially initialized module 'app.parent' has no attribute 'Parent' (most likely due to a circular import)
2 Answers

TLDR - graphene.Field('<class-loc>.<class-name>'). In your case, graphene.Field('parent.Parent') and graphene.Field('child.Child') should do the job.

Ran into the exact same issue and thought there must be some way to define the schema just using string representations instead. While going through the code I found the import_string function which is used internally that helped me understand how that can be done -

https://github.com/graphql-python/graphene/blob/a53b782bf8ec5612d5cceb582fbde68eeba859aa/graphene/utils/module_loading.py#L5

Just use "lambda". In your case:

child = graphene.Field(lambda:C.Child)

instead of:

child = graphene.Field(C.Child)
Related