Skip to content

Permissions

DjangoModelType supports a permission layer (modeled after DRF's API), checked per action (create / update / delete / retrieve / list) before each operation runs. These permission classes are the library's own — no external dependency required. Set permission_classes on the type:

from django_graphex import DjangoModelType, IsAuthenticatedOrReadOnly

class OrderType(DjangoModelType):
    permission_classes = [IsAuthenticatedOrReadOnly]

    class Meta:
        model = Order

With no permission_classes (the default) nothing is checked — every operation runs. A denied action raises a GraphQLError and the operation does not run:

{
  "errors": [{
    "message": "You do not have permission to perform this action.",
    "extensions": {"code": "PERMISSION_DENIED", "status_code": 403}
  }]
}

Ready-made permissions

Class Allows
AllowAny every action (explicit form of the default)
IsAuthenticated only authenticated users
IsAdmin only active staff superusers
IsAuthenticatedOrReadOnly anyone may retrieve/list; authenticated users may write
IsAdminOrReadOnly anyone may retrieve/list; admins may write
from django_graphex import (
    AllowAny, IsAuthenticated, IsAdmin,
    IsAuthenticatedOrReadOnly, IsAdminOrReadOnly,
)

Multiple classes are combined with AND — every class must allow the action.

Writing a custom permission

Subclass BasePermission and override either has_permission(self, info, action, model, **kwargs) (applies to every action) or a single has_<action>_permission( self, info, model, **kwargs). info.context is the request, and kwargs carries data= for create/update. Return False to deny.

from django_graphex import BasePermission

class IsOwnerOrReadOnly(BasePermission):
    def has_permission(self, info, action, model, **kwargs):
        if action in ("retrieve", "list"):
            return True
        return info.context.user.is_authenticated

Example: map to Django model permissions

The downstream pattern, mapping each action to a Django add/change/delete/view permission:

class DjangoModelPermission(BasePermission):
    _ACTION_PERM = {
        "create": "add", "update": "change",
        "delete": "delete", "retrieve": "view", "list": "view",
    }

    def has_permission(self, info, action, model, **kwargs):
        user = info.context.user
        codename = "{}.{}_{}".format(
            model._meta.app_label,
            self._ACTION_PERM[action],
            model._meta.model_name,
        )
        return user.has_perm(codename)

Customizing the check

Each CRUD operation calls authorize(cls, info, action, **kwargs) first, which runs the permission checks. Override it to customize — for example, to skip checks in local development:

from django.conf import settings

class OrderType(DjangoModelType):
    permission_classes = [IsAuthenticated]

    class Meta:
        model = Order

    @classmethod
    def authorize(cls, info, action, **kwargs):
        if settings.DEBUG:
            return  # skip checks locally
        super().authorize(info, action, **kwargs)

Per-request data scoping vs. permissions

Permissions answer "may this action run at all?". To scope the rows a user can see (row-level filtering), use filter_queryset instead.