Search code examples
djangographqlgraphene-python

Django graphene with relay restricting queries access based on ID


I am trying to restrict single object queries to the user that created them.

Models.py

class Env(models.Model):
    name = models.CharField(max_length=50)
    user = models.ForeignKey(
        User, on_delete=models.CASCADE)
    description = TextField()

Schema.py

class EnvNode(DjangoObjectType):
    class Meta:
        model = Env
        filter_fields =  {
            'name': ['iexact'],
            'description': ['exact', 'icontains'],
            }
        interfaces = (relay.Node, )


Query(object):
    env = relay.Node.Field(EnvNode)
    all_envs = DjangoFilterConnectionField(EnvNode)

I tried adding a resolve query but it only worked for the "all_env" query with the filter and did not work for the single object query

def resolve_env(self, info):
    env = Env.objects.filter(user = info.context.user.id)
    if env is not None:
        return env
    else:
        return None

Also tried adding a class method to the EnvNode as recommended here under filtering Node based ID access:

@classmethod
def get_node(context, cls, id, info):
    try:
        env =  cls._meta.model.objects.get(id = id)
    except cls._meta.model.DoesNotExist:
        return None

    if context.user == env.user:
        return env
    return None

but I got an error:

"message": "get_node() missing 1 required positional argument: 'info'",

Solution

  • Seems like the documentation is not correct, also your parameters are not in the correct order for the get_node method.

    There are only three parameters for the call

    • The first one is your DjangoObjectType subclass: EnvNode
    • The second one is a graphql.execution.base.ResolveInfo which contains a reference to the context. You can get the user object from there.
    • The third one is the actual id for the object.

    You should write it like this for the restriction to work:

    @classmethod
    def get_node(cls, info, id):
        try:
            env = cls._meta.model.objects.get(id=id, user=info.context.user)
    
        except cls._meta.model.DoesNotExist:
            return None
    
        return env
    

    Hope it helps.