2016-12-27 3 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) 

Как определить круговую связь между 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 
      } 
      } 
     } 
     } 
    } 
    } 
} 

Как она стоит, я не могу ссылаться на IngredientNode от RecipeNode так как он еще не определен. Если я попытаюсь использовать лямбда, как я видел, рекомендуется в другом месте, я получаю 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) 

Я должен думать, что есть простое решение, которое я просто не вижу. Любая помощь будет оценена по достоинству. Благодаря!

Джанго 1.8.17, Джанго-фильтр 0.15.3, графен-Джанго 1.2.0

ответ

2

Для потомков, как мы работали вокруг этого вопроса было переопределить DjangoFilterConnectionField таким образом, что требуется filterset_class аргумент, и мы удаленный код, который ссылался на атрибуты Meta узла. Нижняя сторона заключается в том, что мы больше не можем использовать ярлык filter_fields. Для нас это не проблема, поскольку мы с самого начала использовали FilterSets.

Весь окончательное решение/обходной путь:

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 таким образом позволяет использовать лямбда ссылаться на узлы, которые еще не были определены.