[BUG] transaction.non_atomic_requests is ignored on Ninja routes

Author: OscarVanLCreated Aug 28, 2026Updated Aug 28, 2026

Describe the bug On my Django database configuration I have "ATOMIC_REQUESTS": True so that all HTTP requests automatically run in a transaction. This is to ensure atomicity in the case of request failures.

On some API endpoints, like my/health route, I want to exempt this from atomic requests since there are no DB queries in this route. This is normally achieved with @transaction.non_atomic_requests. However, this does not work on a Ninja route.

I think Ninja should honour this setting to allow for more granular transaction control.

Versions (please complete the following information):

  • Python version: 3.12.14
  • Django version: 5.2.17
  • Django-Ninja version: 1.6.2
  • Pydantic version: 2.13.0

Reproducible example

python
from django.conf import settings

settings.configure(
    SECRET_KEY="repro",
    ROOT_URLCONF=__name__,
    ALLOWED_HOSTS=["testserver"],
    INSTALLED_APPS=[],
    MIDDLEWARE=[],
    DATABASES={
        "default": {
            "ENGINE": "django.db.backends.sqlite3",
            "NAME": ":memory:",
            "ATOMIC_REQUESTS": True,
        }
    },
)

import django

django.setup()

from django.db import connection, transaction
from django.http import JsonResponse
from django.test import Client
from django.urls import path, resolve
from ninja import NinjaAPI
from ninja import __version__ as ninja_version
from ninja.decorators import decorate_view


# Control: Django's decorator disables ATOMIC_REQUESTS on a regular view
@transaction.non_atomic_requests
def django_view(request):
    return JsonResponse({"in_atomic_block": connection.in_atomic_block})


api = NinjaAPI()


# Ninja: the same decorator has no effect on a ninja view
@api.get("/ninja-direct")
@transaction.non_atomic_requests
def direct_ninja_view(request):
    return {"in_atomic_block": connection.in_atomic_block}


decorated_api = NinjaAPI(urls_namespace="decorated-api")

# Test the suggested workaround (not working)
try:
    @decorated_api.get("/ninja-decorate-view")
    @decorate_view(transaction.non_atomic_requests)
    def decorated_ninja_view(request):
        return {"in_atomic_block": connection.in_atomic_block}
except AttributeError as exc:
    decorate_view_error = exc
else:
    decorate_view_error = None


urlpatterns = [
    path("django", django_view),
    path("", api.urls),
]


if __name__ == "__main__":
    client = Client()
    django_result = client.get("/django").json()
    direct_ninja_result = client.get("/ninja-direct").json()
    direct_ninja_callback = resolve("/ninja-direct").func

    print(f"Django {django.get_version()}")
    print(f"django-ninja {ninja_version}")
    print(f"Django view: {django_result}")
    print(f"Direct Ninja view: {direct_ninja_result}")
    print(f"Direct decorated function attribute: {direct_ninja_view._non_atomic_requests}")
    print(f"Direct resolved callback attribute:  {getattr(direct_ninja_callback, '_non_atomic_requests', None)}")
    print(f"decorate_view registration error: {decorate_view_error!r}")

    # The plain Django control is outside a transaction, while the equivalent Ninja view is still inside one.
    assert django_result == {"in_atomic_block": False}
    assert direct_ninja_result == {"in_atomic_block": True}

    # decorate_view currently applies the decorator to a bound method, which cannot store Django's marker.
    assert isinstance(decorate_view_error, AttributeError)
    assert "'method' object has no attribute '_non_atomic_requests'" in str(decorate_view_error)

Here's the output:

Django 5.2.17
django-ninja 1.6.2
Django view: {'in_atomic_block': False}
Direct Ninja view: {'in_atomic_block': True}
Direct decorated function attribute: {'default'}
Direct resolved callback attribute:  None
decorate_view registration error: AttributeError("'method' object has no attribute '_non_atomic_requests'")