2016-09-21 08:30:36 +03:00
Authorization in Django
=======================
2017-02-09 19:18:50 +03:00
There are several ways you may want to limit access to data when
2016-09-21 08:30:36 +03:00
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.
.. code :: python
from django.db import models
class Post(models.Model):
2016-12-05 13:01:49 +03:00
title = models.CharField(max_length=100)
2016-09-21 08:30:36 +03:00
content = models.TextField()
published = models.BooleanField(default=False)
owner = models.ForeignKey('auth.User')
Limiting Field Access
---------------------
2019-09-07 19:49:41 +03:00
To limit fields in a GraphQL query simply use the `` fields `` meta attribute.
2016-09-21 08:30:36 +03:00
.. code :: python
from graphene import relay
from graphene_django.types import DjangoObjectType
from .models import Post
class PostNode(DjangoObjectType):
class Meta:
model = Post
2019-09-07 19:49:41 +03:00
fields = ('title', 'content')
2016-09-21 08:30:36 +03:00
interfaces = (relay.Node, )
2019-09-07 19:49:41 +03:00
conversely you can use `` exclude `` meta attribute.
2017-02-07 03:59:13 +03:00
.. code :: python
from graphene import relay
from graphene_django.types import DjangoObjectType
from .models import Post
class PostNode(DjangoObjectType):
class Meta:
model = Post
2019-09-07 19:49:41 +03:00
exclude = ('published', 'owner')
2017-02-07 03:59:13 +03:00
interfaces = (relay.Node, )
2021-02-02 20:58:21 +03:00
Another pattern is to have a resolve method act as a gatekeeper, returning None
or raising an exception if the client isn't allowed to see the data.
.. code :: python
from graphene import relay
from graphene_django.types import DjangoObjectType
from .models import Post
class PostNode(DjangoObjectType):
class Meta:
model = Post
fields = ('title', 'content', 'owner')
interfaces = (relay.Node, )
def resolve_owner(self, info):
user = info.context.user
if user.is_anonymous:
raise PermissionDenied("Please login")
if not user.is_staff:
return None
return self.owner
2016-09-21 08:30:36 +03:00
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.
.. code :: python
from graphene import ObjectType
from graphene_django.filter import DjangoFilterConnectionField
from .models import Post
class Query(ObjectType):
2018-02-23 21:59:19 +03:00
all_posts = DjangoFilterConnectionField(PostNode)
2016-09-21 08:30:36 +03:00
2018-08-29 10:37:27 +03:00
def resolve_all_posts(self, info):
2018-09-05 14:21:39 +03:00
return Post.objects.filter(published=True)
2016-09-21 08:30:36 +03:00
User-based Queryset Filtering
-----------------------------
If you are using `` GraphQLView `` you can access Django's request
with the context argument.
.. code :: python
from graphene import ObjectType
from graphene_django.filter import DjangoFilterConnectionField
from .models import Post
class Query(ObjectType):
2018-02-23 21:59:19 +03:00
my_posts = DjangoFilterConnectionField(PostNode)
2016-09-21 08:30:36 +03:00
2017-10-08 15:42:34 +03:00
def resolve_my_posts(self, info):
2016-09-21 08:30:36 +03:00
# context will reference to the Django request
2019-08-16 16:33:59 +03:00
if not info.context.user.is_authenticated:
2016-09-25 14:11:01 +03:00
return Post.objects.none()
2016-09-21 08:30:36 +03:00
else:
2017-10-08 15:42:34 +03:00
return Post.objects.filter(owner=info.context.user)
2016-09-21 08:30:36 +03:00
If you're using your own view, passing the request context into the
schema is simple.
.. code :: python
result = schema.execute(query, context_value=request)
2019-03-31 14:01:17 +03:00
Global Filtering
----------------
If you are using `` DjangoObjectType `` you can define a custom `get_queryset` .
.. code :: python
from graphene import relay
from graphene_django.types import DjangoObjectType
from .models import Post
class PostNode(DjangoObjectType):
class Meta:
model = Post
2020-06-11 13:09:52 +03:00
fields = '__all__'
2019-03-31 14:01:17 +03:00
@classmethod
def get_queryset(cls, queryset, info):
if info.context.user.is_anonymous:
return queryset.filter(published=True)
return queryset
2018-08-29 10:37:27 +03:00
Filtering ID-based Node Access
2016-09-21 08:30:36 +03:00
------------------------------
In order to add authorization to id-based node access, we need to add a
method to your `` DjangoObjectType `` .
.. code :: python
from graphene_django.types import DjangoObjectType
from .models import Post
class PostNode(DjangoObjectType):
class Meta:
model = Post
2019-09-07 19:49:41 +03:00
fields = ('title', 'content')
2016-09-21 08:30:36 +03:00
interfaces = (relay.Node, )
@classmethod
2018-09-26 21:07:50 +03:00
def get_node(cls, info, id):
2016-09-21 08:30:36 +03:00
try:
2018-09-05 14:22:54 +03:00
post = cls._meta.model.objects.get(id=id)
2016-09-21 08:30:36 +03:00
except cls._meta.model.DoesNotExist:
return None
2018-08-29 10:37:27 +03:00
if post.published or info.context.user == post.owner:
2016-10-19 00:30:15 +03:00
return post
2016-09-25 14:11:01 +03:00
return None
2017-02-09 19:18:50 +03:00
2018-09-05 14:22:54 +03:00
2018-08-29 10:37:27 +03:00
Adding Login Required
2017-02-09 19:18:50 +03:00
---------------------
2018-08-29 10:37:27 +03:00
To restrict users from accessing the GraphQL API page the standard Django LoginRequiredMixin_ can be used to create your own standard Django Class Based View, which includes the `` LoginRequiredMixin `` and subclasses the `` GraphQLView `` .:
2017-02-09 19:18:50 +03:00
.. code :: python
2019-06-14 14:33:37 +03:00
# views.py
2019-04-26 18:48:37 +03:00
2017-02-09 19:18:50 +03:00
from django.contrib.auth.mixins import LoginRequiredMixin
from graphene_django.views import GraphQLView
class PrivateGraphQLView(LoginRequiredMixin, GraphQLView):
pass
2018-08-29 10:37:27 +03:00
After this, you can use the new `` PrivateGraphQLView `` in the project's URL Configuration file `` url.py `` :
2020-05-09 14:13:47 +03:00
For Django 2.2 and above:
2018-08-29 10:37:27 +03:00
.. code :: python
urlpatterns = [
# some other urls
path('graphql', PrivateGraphQLView.as_view(graphiql=True, schema=schema)),
]
2017-02-09 19:18:50 +03:00
2020-04-06 15:21:07 +03:00
.. _LoginRequiredMixin: https://docs.djangoproject.com/en/dev/topics/auth/default/#the-loginrequired-mixin