Search code examples
djangographene-pythongraphene-django

Issue joining data in graphene-django with a ManyToMany field


I've been trying to find a good example for returning a query set with joined data across a ManyToMany field, but I've not found one.

Given the following model:

class Task(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    task_assignee = models.ForeignKey("User")
    is_draft = models.Boolean(default=True)
    detail = models.ManyToManyField("Detail", through="TaskDetails")


class Detail(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    detail_name = models.CharField(max_length=250)

class TaskDetails(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    task = models.ForeignKey("Task", related_name="detail_tasks")
    detail = models.ForeignKey("Detail", related_name="task_details")
    detail_value = models.CharField(max_length=250)

I'd like to return the data for Task with it's related details. Based on the answer on this question, I tweaked my schema to the following:

class TaskDetailsType(DjangoObjectType):

    class Meta:
        model = TaskDetails
        fields = ("id", "detail_name", "detail_value")

        detail_name = graphene.String()

    def resolve_detail_name(value_obj, info):
        return value_obj.detail.detail_name


class TaskType(DjangoObjectType):

    class Meta:
        model = Task
        fields = ("id", "task_details")

    task_details = graphene.List(TaskDetailsType)

    def resolve_task_details(value_obj, info):
        return value_obj.detail_tasks

The query I'm running:

Task.objects.filter(task_assignee_id=info.context.user.id) \
    .filter(is_draft=False)

When I run this though, I get an error:

{'errors': [{'message': 'User Error: expected iterable, but did not find one for field TaskType.taskDetails.'}, {'message': 'User Error: expected iterable, but did not find one for field TaskType.taskDetails.'}, {'message': 'User Error: expected iterable, but did not find one for field TaskType.taskDetails.'}], 'data': {'getInboxTasks': [{'id': 'a430e49d-c9c3-4839-8f2c-aaebbfe9ef3a', 'taskDetails': None}, {'id': '74c8dacc-1bfd-437a-ae34-7e111075ac5e', 'taskDetails': None}, {'id': '10956caa-d74f-4a01-a5cf-9cac6a15c5a3', 'taskDetails': None}]}}


Solution

  • As indicated by @schillingt and hinted at by graphene, the problem is that a RelatedManager is not iterable:

    def resolve_task_details(value_obj, info):
        return value_obj.detail_tasks  # related manager
    

    So the change is simple:

    def resolve_task_details(value_obj, info):
        return value_obj.detail_tasks.all()  # queryset -> iterable