Authorization in Django

There are several ways you may want to limit access to data when working with Graphene and Django: limiting which fields are accessible via GraphQL and limiting which objects a user can access.

Let’s use a simple example model.

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    published = models.BooleanField(default=False)
    owner = models.ForeignKey('auth.User')

Limiting Field Access

This is easy, simply use the only_fields meta attribute.

from graphene import relay
from graphene_django.types import DjangoObjectType
from .models import Post

class PostNode(DjangoObjectType):
    class Meta:
        model = Post
        only_fields = ('title', 'content')
        interfaces = (relay.Node, )

conversely you can use exclude_fields meta attribute.

from graphene import relay
from graphene_django.types import DjangoObjectType
from .models import Post

class PostNode(DjangoObjectType):
    class Meta:
        model = Post
        exclude_fields = ('published', 'owner')
        interfaces = (relay.Node, )

Queryset Filtering On Lists

In order to filter which objects are available in a queryset-based list, define a resolve method for that field and return the desired queryset.

from graphene import ObjectType
from graphene_django.filter import DjangoFilterConnectionField
from .models import Post

class Query(ObjectType):
    all_posts = DjangoFilterConnectionField(PostNode)

    def resolve_all_posts(self, args, info):
        return Post.objects.filter(published=True)

User-based Queryset Filtering

If you are using GraphQLView you can access Django’s request with the context argument.

from graphene import ObjectType
from graphene_django.filter import DjangoFilterConnectionField
from .models import Post

class Query(ObjectType):
    my_posts = DjangoFilterConnectionField(PostNode)

    def resolve_my_posts(self, info):
        # context will reference to the Django request
        if not info.context.user.is_authenticated():
            return Post.objects.none()
        else:
            return Post.objects.filter(owner=info.context.user)

If you’re using your own view, passing the request context into the schema is simple.

result = schema.execute(query, context_value=request)

Filtering ID-based node access

In order to add authorization to id-based node access, we need to add a method to your DjangoObjectType.

from graphene_django.types import DjangoObjectType
from .models import Post

class PostNode(DjangoObjectType):
    class Meta:
        model = Post
        only_fields = ('title', 'content')
        interfaces = (relay.Node, )

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

        if post.published or context.user == post.owner:
            return post
        return None

Adding login required

If you want to use the standard Django LoginRequiredMixin you can create your own view, which includes the LoginRequiredMixin and subclasses the GraphQLView:

from django.contrib.auth.mixins import LoginRequiredMixin
from graphene_django.views import GraphQLView


class PrivateGraphQLView(LoginRequiredMixin, GraphQLView):
    pass

After this, you can use the new PrivateGraphQLView in urls.py:

urlpatterns = [
  # some other urls
  url(r'^graphql', PrivateGraphQLView.as_view(graphiql=True, schema=schema)),
]