Search code examples
pythondjangographqlgraphene-pythongraphene-django

Python Graphene working with many to many relations


if this is answered somewhere else then I am sorry but 2 days after work and still no cigar...

I have a player model:

class Player(models.Model):
    name = models.CharField(max_length=60)
    discord_id = models.CharField(max_length=60, null=True)
    known_npcs = models.ManyToManyField(NPC)

The player can know many NPCs, and any NPC can be known by many players.

NPC is nothing special:

class NPC(models.Model):
    image = models.ImageField()
    name = models.CharField(max_length=50)
    description = models.TextField()

last part of the puzzle is the Fact a fact is some piece of information attached to a NPC, however a person can know a NPC, but not necessarily all of the fact's related to the NPC are known by the Player hence the Fact looks like this:

class Fact(models.Model):
    fact = models.TextField()
    known_by = models.ManyToManyField(Player)
    npc = models.ForeignKey(NPC, on_delete=models.DO_NOTHING, null=True)

Now in graphene I want to create a Player and allPlayers query that would give me this:

{
  allPlayers {
    name
    knownNPCs {
      image
      name
      description
      factsKnown {
        fact
      }
    }
  }
}

Where the factsKnown are only the ones based on the ManyToMany relation from the Fact object.

What I have created so far returns the data but does not filter the Facts based on the player parent just shows all the facts related to the npc :(

Fact schema

class FactType(DjangoObjectType):
    class Meta:
        model = Fact
        filter_fields = ["id"]

class Query(object):
    fact = Node.Field(FactType)
    all_Facts = graphene.List(FactType)

    def resolve_all_Facts(self, info, **kwargs):
        return Fact.objects.all()

NPCSchema

class NPCType(DjangoObjectType):
    class Meta:
        model = NPCS

class Query(object):
    all_NPCs = graphene.Field(NPCType)
    facts = graphene.List(FactType)
    def resolve_all_NPCs(self, info, **kwargs):
        return NPCS.objects.all()

PlayerSchema:

class PlayerType(DjangoObjectType):
    class Meta:
        model = Player
        interfaces = (Node,)
        filter_fields = ["id"]


class Query(object):
    player = Node.Field(PlayerType)
    all_players = graphene.List(PlayerType)

    def resolve_all_players(self, info, **kwargs):
        return Player.objects.all()

    def resolve_player(self, info, **kwargs):
        player = Player.objects.filter(id=info.id)

Solution

  • SO for anyone reading still I managed to work around this by creating a 'filteredFacts' field:

    class PlayerType(DjangoObjectType):
    class Meta:
        model = Player
        filter_fields = ["id"]
    
    filtered_facts = graphene.List(FactGroup)
    
    def resolve_filtered_facts(self, info, **kwargs):
        groups = defaultdict(list)
        facts = self.known_facts.all()
        for fact in facts:
            groups[fact.npc].append(fact.fact)
        grouped_facts = []
        for key, value in groups.items():
            grouped_facts.append(FactGroup(npc=key, facts=value))
    
        return grouped_facts
    

    That takes all the facts known to a player and groups them by NPC, same result just from the other side...