2016-12-27 74 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引用尚未定義的節點。

相關問題