Using the following contrived example:
from django.db import models
from django_filters import FilterSet, OrderingFilter
from graphene import ObjectType, Schema, relay
from graphene_django import DjangoObjectType
from graphene_django.filter import DjangoFilterConnectionField
class Recipe(models.Model):
name = models.CharField(max_length=50)
ingredients = models.ManyToManyField('Ingredient', related_name='recipes')
class Ingredient(models.Model):
name = models.CharField(max_length=50)
class RecipeFilter(FilterSet):
order_by = OrderingFilter(fields=[('name', 'name')])
class Meta:
fields = {'name': ['icontains']}
model = Recipe
class IngredientFilter(FilterSet):
order_by = OrderingFilter(fields=[('name', 'name')])
class Meta:
fields = {'name': ['icontains']}
model = Ingredient
class RecipeNode(DjangoObjectType):
ingredients = DjangoFilterConnectionField(IngredientNode, filterset_class=IngredientFilter)
class Meta:
interfaces = [relay.Node]
model = Recipe
only_fields = ['name']
class IngredientNode(DjangoObjectType):
recipes = DjangoFilterConnectionField(RecipeNode, filterset_class=RecipeFilter)
class Meta:
interfaces = [relay.Node]
model = Ingredient
only_fields = ['name']
class Queries(ObjectType):
all_recipes = DjangoFilterConnectionField(RecipeNode, filterset_class=RecipeFilter)
all_ingredients = DjangoFilterConnectionField(IngredientNode, filterset_class=IngredientFilter)
schema = Schema(query=Queries)
How can I define the circular relationship between RecipeNode
and IngredientNode
such that I can run the following GraphQL query:
{
allRecipes(name_Icontains: "gg") {
edges {
node {
name
ingredients(name_Icontains: "gg") {
edges {
node {
name
}
}
}
}
}
}
allIngredients(name_Icontains: "gg") {
edges {
node {
name
recipes(name_Icontains: "gg") {
edges {
node {
name
}
}
}
}
}
}
}
As it stands, I cannot reference IngredientNode
from RecipeNode
since it is not yet defined. If I attempt to use a lambda as I've seen recommended elsewhere, I receive AttributeError: 'function' object has no attribute '_meta'
.
class IngredientNode(DjangoObjectType):
recipes = DjangoFilterConnectionField(lambda: RecipeNode, filterset_class=RecipeFilter)
class Meta:
interfaces = [relay.Node]
model = Ingredient
only_fields = ['name']
If I attempt to set the attribute after the fact, I am unable to query for ingredients
from within a recipe. There is no error, Graphiql just behaves as though ingredients
was never defined.
class RecipeNode(DjangoObjectType):
class Meta:
interfaces = [relay.Node]
model = Recipe
only_fields = ['name']
class IngredientNode(DjangoObjectType):
recipes = DjangoFilterConnectionField(RecipeNode, filterset_class=RecipeFilter)
class Meta:
interfaces = [relay.Node]
model = Ingredient
only_fields = ['name']
RecipeNode.ingredients = DjangoFilterConnectionField(IngredientNode, filterset_class=IngredientFilter)
I have to think there's a simple solution to this that I'm just not seeing. Any help would be appreciated. Thanks!
Django 1.8.17, django-filter 0.15.3, graphene-django 1.2.0
For posterity, the way we worked around this issue was to redefine the DjangoFilterConnectionField such that the filterset_class argument is required and we removed code that referenced the node's Meta attributes. The down side is that we can no longer take advantage of the filter_fields shortcut. For us, this is not an issue since we have been using FilterSets from the start.
The entire final solution/workaround:
Redefining DjangoFilterConnectionField in this way allows us to use a lambda to reference nodes that have not yet been defined.