使用graphene-django,是否可以定义两个节点之间的循环关系?
Using graphene-django, is it possible to define a circular relationship between two nodes?
使用以下人为的示例:
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)
如何定义 RecipeNode
和 IngredientNode
之间的循环关系,以便我可以 运行 以下 GraphQL 查询:
{
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
}
}
}
}
}
}
}
就目前而言,我无法从 RecipeNode
引用 IngredientNode
,因为它尚未定义。如果我尝试使用其他地方推荐的 lambda,我会收到 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']
如果我尝试在事后设置属性,我将无法从食谱中查询 ingredients
。没有错误,Graphiql 的行为就像从未定义 ingredients
一样。
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)
我不得不认为有一个我只是没有看到的简单解决方案。任何帮助,将不胜感激。谢谢!
Django 1.8.17,django-filter 0.15.3,石墨烯-django 1.2.0
为了后代,我们解决这个问题的方法是重新定义 DjangoFilterConnectionField 以便 filterset_class 参数是必需的,我们删除了引用节点的 Meta 属性的代码。缺点是我们不能再利用 filter_fields 快捷方式。对于我们来说,这不是问题,因为我们从一开始就一直在使用 FilterSets。
整个决赛solution/workaround:
from django.db import models
from django_filters import FilterSet, OrderingFilter
from functools import partial
from graphene import ObjectType, Schema, relay
from graphene_django import DjangoObjectType, DjangoConnectionField
from graphene_django.filter.utils import get_filtering_args_from_filterset, get_filterset_class
class DjangoFilterConnectionField(DjangoConnectionField):
def __init__(self, type, filterset_class, *args, **kwargs):
self.filterset_class = get_filterset_class(filterset_class)
self.filtering_args = get_filtering_args_from_filterset(self.filterset_class, type)
kwargs.setdefault('args', {})
kwargs['args'].update(self.filtering_args)
super(DjangoFilterConnectionField, self).__init__(type, *args, **kwargs)
@staticmethod
def connection_resolver(resolver, connection, default_manager, filterset_class, filtering_args,
root, args, context, info):
filter_kwargs = {k: v for k, v in args.items() if k in filtering_args}
qs = default_manager.get_queryset()
qs = filterset_class(data=filter_kwargs, queryset=qs).qs
return DjangoConnectionField.connection_resolver(resolver, connection, qs, root, args, context, info)
def get_resolver(self, parent_resolver):
return partial(self.connection_resolver, parent_resolver, self.type, self.get_manager(),
self.filterset_class, self.filtering_args)
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(lambda: 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)
以这种方式重新定义 DjangoFilterConnectionField 允许我们使用 lambda 来引用尚未定义的节点。
使用以下人为的示例:
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)
如何定义 RecipeNode
和 IngredientNode
之间的循环关系,以便我可以 运行 以下 GraphQL 查询:
{
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
}
}
}
}
}
}
}
就目前而言,我无法从 RecipeNode
引用 IngredientNode
,因为它尚未定义。如果我尝试使用其他地方推荐的 lambda,我会收到 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']
如果我尝试在事后设置属性,我将无法从食谱中查询 ingredients
。没有错误,Graphiql 的行为就像从未定义 ingredients
一样。
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)
我不得不认为有一个我只是没有看到的简单解决方案。任何帮助,将不胜感激。谢谢!
Django 1.8.17,django-filter 0.15.3,石墨烯-django 1.2.0
为了后代,我们解决这个问题的方法是重新定义 DjangoFilterConnectionField 以便 filterset_class 参数是必需的,我们删除了引用节点的 Meta 属性的代码。缺点是我们不能再利用 filter_fields 快捷方式。对于我们来说,这不是问题,因为我们从一开始就一直在使用 FilterSets。
整个决赛solution/workaround:
from django.db import models
from django_filters import FilterSet, OrderingFilter
from functools import partial
from graphene import ObjectType, Schema, relay
from graphene_django import DjangoObjectType, DjangoConnectionField
from graphene_django.filter.utils import get_filtering_args_from_filterset, get_filterset_class
class DjangoFilterConnectionField(DjangoConnectionField):
def __init__(self, type, filterset_class, *args, **kwargs):
self.filterset_class = get_filterset_class(filterset_class)
self.filtering_args = get_filtering_args_from_filterset(self.filterset_class, type)
kwargs.setdefault('args', {})
kwargs['args'].update(self.filtering_args)
super(DjangoFilterConnectionField, self).__init__(type, *args, **kwargs)
@staticmethod
def connection_resolver(resolver, connection, default_manager, filterset_class, filtering_args,
root, args, context, info):
filter_kwargs = {k: v for k, v in args.items() if k in filtering_args}
qs = default_manager.get_queryset()
qs = filterset_class(data=filter_kwargs, queryset=qs).qs
return DjangoConnectionField.connection_resolver(resolver, connection, qs, root, args, context, info)
def get_resolver(self, parent_resolver):
return partial(self.connection_resolver, parent_resolver, self.type, self.get_manager(),
self.filterset_class, self.filtering_args)
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(lambda: 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)
以这种方式重新定义 DjangoFilterConnectionField 允许我们使用 lambda 来引用尚未定义的节点。