Python is not able to resolve circular dependencies for schemas with recursive types.
e.g
# category_type.py
from .post_type import Post
class Category(graphene.ObjectType):
Post = graphene.List(Post)
# post_type.py
from .category_type import Category
class Post(graphene.ObjectType):
category = graphene.Field(Category)
At runtime, the above fails as the types haven't yet loaded before being used in field definitions. A better option would be to wrap the field definitions in a lambda to be called at resolve time and after the types have been declared.
e.g
# category_type.py
- from .post_type import Post
class Category(graphene.ObjectType):
- Post = graphene.List(Post)
+ Post = lambda: graphene.List(Post)
+ from .post_type import Post
# post_type.py
- from .category_type import Category
class Post(graphene.ObjectType):
- category = graphene.Field(Category)
+ category = lambda: graphene.Field(Category)
+ from .category_type import Category
cc @Globegitter
Or looking at Graphql-core another syntax option could be
class Post(graphene.ObjectType):
fields = lambda: dict(
category = graphene.Field(Category)
)
But of course that would mean that graphene would either have to break backwards-compatibility to a quite substantial degree, or support two different syntaxes.
Hi @imranolas,
You can achieve this by using String references (ala Django):
# category_type.py
@schema.register
class Category(graphene.ObjectType):
Post = graphene.List('Post')
# post_type.py
@schema.register
class Post(graphene.ObjectType):
category = graphene.Field('Category')
As posted in the pull request #111, there is already another "undocumented" way for doing solving this if you don't want to use string references.
class Category(graphene.ObjectType):
Post = graphene.LazyType(lambda _: Post)
from .post_type import Post
# post_type.py
class Post(graphene.ObjectType):
category = graphene.LazyType(lambda _: Category)
Please feel free to reopen if you have any other suggestion of how to achieve it! :)
Hey guys– I've seen a lot of similar issues and pull requests, all of which are closed as being fixed; is any of this documented anywhere? I can't find any reference to it in the main page documentation.
I agree, what's the way to do this in graphene 2.0? Neither the string or the LazyType seem to work for me...
I think, based on reading through the source code, that graphene.Dynamic is intended to be the solution. It looks like this:
graph.List(graph.Dynamic(lambda: CircularType))
But there's sadly no documentation for this class at all. For instance, would this be more appropriate?
graph.Dynamic(lambda: graph.List(CircularType))
Looking at some unrelated docs here it seems like you can do this:
graphene.List(lambda: MyCircularType)
graphene.Field(lambda: MyCircularType)
I just tested it and it works.
Confirmed that the lambda thing works pretty much everywhere. Seems like that's the thing to do. Should definitely be documented explicitly somewhere.
This whole project is woefully under-documented. But great finds!
@syrusakbary
anyway to do this just using graphql-core? thanks
@sanfilippopablo How did you actually import the types? I'm still struggling to get this working without an error
the lambda option doesn't solve the problem of circular imports, just circular dependencies. Is there a supported solution for the problem of circular imports? specifying it as a dotted package string 'mymodule.gqlobjects.Foobar' seems to work, I think.
Hi @imranolas,
You can achieve this by using String references (ala Django):
# category_type.py @schema.register class Category(graphene.ObjectType): Post = graphene.List('Post') # post_type.py @schema.register class Post(graphene.ObjectType): category = graphene.Field('Category')
This does not work anymore. Just tested like this:
class Webhook(ModelObjectType):
triggered_by = graphene.Field('WebhookEventType')
class WebhookEventType(ModelObjectType):
...
This raises ImportError from here: https://github.com/graphql-python/graphene/blame/master/graphene/utils/module_loading.py#L39
If I change string to mymodule.WebhookEventType then it works but this is not ideal.
Using string references does not appear to be support in Connection.Meta.Node
class A(graphene.Connection):
class Meta:
node = 'B'
Any solutions. Ive also tried lazy_import and import_string, but it doesnt seem to be finding the class in the module.
Using string references does not appear to be support in Connection.Meta.Node
class A(graphene.Connection): class Meta: node = 'B'Any solutions. Ive also tried lazy_import and import_string, but it doesnt seem to be finding the class in the module.
I faced the same problem. Did you find any solutions?
@McPo @amaksymov did you manage to solve this?
Most helpful comment
Looking at some unrelated docs here it seems like you can do this:
I just tested it and it works.