Permissions and Access Control

djxi provides a lightweight permission system that runs before any battery endpoint handler. It requires no external packages and integrates naturally with Django’s request.user.

Quick start — requiring authentication

Set requires_auth = True on the battery. Anonymous (unauthenticated) users receive a 403 Forbidden response:

from djxi import DXEndpointBattery, dx_get, dx_delete

class SecureBattery(DXEndpointBattery):
    inline_template = "..."
    requires_auth = True

    @dx_get("data/", name="data")
    def data(self, request):
        return self.render_section(request, "data", {"rows": fetch_rows()})

Redirecting to a login page

Set login_url alongside requires_auth to redirect unauthenticated users instead of returning a 403:

class SecureBattery(DXEndpointBattery):
    inline_template = "..."
    requires_auth = True
    login_url = "/accounts/login/"

    @dx_get("data/", name="data")
    def data(self, request):
        ...

The redirect uses Django’s django.shortcuts.redirect and returns a 302.

Fine-grained control with check_permissions()

Override check_permissions(self, request) for any logic beyond a simple authentication check. Return None to allow the request through or return any HttpResponse to short-circuit the handler:

from django.http import HttpResponseForbidden

class StaffOnlyBattery(DXEndpointBattery):
    inline_template = "..."

    def check_permissions(self, request):
        if not getattr(request.user, "is_staff", False):
            return HttpResponseForbidden("Staff only.")
        return None  # allow through

    @dx_get("admin-data/", name="admin-data")
    def admin_data(self, request):
        ...

Using Django’s permission system:

from django.http import HttpResponseForbidden

class PermissionedBattery(DXEndpointBattery):
    inline_template = "..."
    REQUIRED_PERM = "myapp.change_item"

    def check_permissions(self, request):
        if not request.user.has_perm(self.REQUIRED_PERM):
            return HttpResponseForbidden()
        return None

    @dx_put("item/<int:pk>/", name="update")
    def update(self, request, pk):
        ...

How it works

check_permissions() is defined on DXRouterMixin and called inside every view wrapper generated by url_patterns() — before the handler method runs. This applies to both sync and async def handlers.

The default implementation in DXEndpointBattery honours requires_auth and login_url; if you override it you can call super() to keep that behaviour and layer your own logic on top:

class CombinedBattery(DXEndpointBattery):
    requires_auth = True
    login_url = "/login/"

    def check_permissions(self, request):
        # First, enforce authentication (from super)
        result = super().check_permissions(request)
        if result is not None:
            return result
        # Then, add a custom rule
        if request.user.profile.is_suspended:
            return HttpResponseForbidden("Account suspended.")
        return None

Class attribute reference

Attribute

Default

Description

requires_auth

False

When True, unauthenticated users are blocked by the default check_permissions() implementation.

login_url

None

URL to redirect unauthenticated users to. When None, a 403 Forbidden is returned instead.

Method reference

DXEndpointBattery.check_permissions(self, request)

Called before every endpoint handler.

Parameters:

request – The incoming HttpRequest.

Returns:

None to allow the request through, or an HttpResponse to short-circuit the handler (e.g. HttpResponseForbidden() or redirect("/login/").