2016-12-27 72 views
0

使用下面的人为的例子:使用graphene-django,可以定义两个节点之间的循环关系吗?

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) 

如何可以定义RecipeNodeIngredientNode使得我可以运行以下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 
      } 
      } 
     } 
     } 
    } 
    } 
} 

,我不能引用IngredientNodeRecipeNode,因为它尚未定义。如果我尝试使用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的过滤器0.15.3,石墨烯的Django 1.2.0

回答

2

为子孙后代,我们解决此问题的工作方式是重新定义DjangoFilterConnectionField使得filterset_class参数是必须的,我们删除引用节点的元属性的代码。不利的一面是我们无法再利用filter_fields快捷方式。对我们来说,这不是问题,因为我们从一开始就一直在使用FilterSet。

整个最终的解决方案/解决方法:

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引用尚未定义的节点。

相关问题