Search code examples
pythondjangographqlmany-to-manygraphene-django

How to make Many-to-many mutations in Django-Graphene?


I'm working in Django 3.2 and graphene-django 2.15. I'm still learning how to use Graphene by the way.

Note: I'm not allowed to share all the code so I rewrote it for the purpose of this question. Please notify me if you've found any error unrelated to the question.

I have an Team model which has a Many-to-Many relationship with the default Django Group model:

from django.db import models
from django.contrib.auth.models import Group

class Team(models.Model):
    team_id = models.IntegerField(unique=True)  # Custom ID not related to Django's pk
    name = models.CharField(max_length=255)
    groups = models.ManyToManyField(Group, blank=True)

Here is my schema:

import graphene
from django.contrib.auth.models import Group
from graphene_django import DjangoObjectType

from .models import Team


class TeamType(DjangoObjectType):
    class Meta:
        model = Team

class GroupType(DjangoObjectType):
    class Meta:
        model = Group


class GroupInput(graphene.InputObjectType):
    id = graphene.ID(required=True)


class UpdateTeam(graphene.Mutation):
    team = graphene.Field(TeamType)

    class Arguments:
        team_id = graphene.ID(required=True)
        name = graphene.String(required=True)
        groups_id = graphene.List(GroupInput, required=True)

    def mutate(self, info, team_id, name, groups_id):
        team = Team.objects.get(pk=team_id)

        team.name = name
        team.groups = Group.objects.filter(pk__in=groups_id)

        team.save()
        return UpdateTeam(team=team)

        
class TeamMutations(graphene.ObjectType):
    update_team = UpdateTeam.Field()

class Mutation(TeamMutations, graphene.ObjectType):
    pass

schema = graphene.schema(query=Query, mutation=Mutation)

When I perform this query:

mutation{
  updateTeam(
    teamId: 65961826547,
    name: "My Team Name",
    groupsId: [{id: 1}, {id: 2}]
  ) {
    team {
      team_id,
      name,
      groups {
        name,
      }
    }
  }
}

I get this error:

{
  "errors": [
    {
      "message": "Field 'id' expected a number but got {'id': '1'}.",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "updateTeam"
      ]
    }
  ],
  "data": {
    "updateTeam": null
  }
}

I don't really understand how Many-to-may relationships are managed by Graphene. Does someone has a solution and some explanations for me? Thanks a lot.


Solution

  • I found the solution.

    At first, I thought there was something related to graphene, especially these InputObjectTypes, I didn't get correctly.

    But the issue is actually very simple.

    The GroupInput is expecting a single value, which it an ID.

    class GroupInput(graphene.InputObjectType):
        id = graphene.ID(required=True)
    

    Because I put it in graphene.List(), I'm now expecting a list of ID's:

    class UpdateTeam(graphene.Mutation):
        ...
    
        class Arguments:
            ...
            groups_id = graphene.List(GroupInput, required=True)
    

    But in my API call, instead of giving an actual list, I gave a dict:

    mutation{
      updateTeam(
        ...
        groupsId: [{id: 1}, {id: 2}]
      ) {
        ...
      }
    

    So this works:

    mutation{
      updateTeam(
        ...
        groupsId: [1, 2]
      ) {
        ...
      }
    

    Note 1:

    graphene.ID also accepts ids given as strings:

        groupsId: ["1", "2"]
    

    Note 2:

    I actually removed my GroupInput and put the graphene.ID field directly in the graphene.List:

    class UpdateTeam(graphene.Mutation):
        ...
    
        class Arguments:
            ...
            groups_id = graphene.List(graphene.ID, required=True)