Skip to main content

django-spec-rest

CI PyPI Python versions Django versions Docs Coverage Ruff License

A service / selector layer for Django REST Framework. Serve django-service-specs kernel specs over Django REST Framework.

A kernel spec declares an operation once: the callable that runs it, the arguments it reads, who may call it, how its input is validated and how its result is presented. django-spec-rest serves that spec on a DRF view or viewset, and lets DRF fill three of its seats: a serializer validates the input (SerializerValidator), a serializer renders the result (SerializerPresenter), and DRF permission classes decide who may call it (DRFPermissionCheck). The same spec runs off HTTP through the kernel's own dispatch, and on any other transport that serves kernel specs.

  • Views and viewsets that take a kernel spec: ServiceCreateView, ServiceUpdateView, ServiceDeleteView, SelectorListView, SelectorRetrieveView, ServiceViewSet, SelectorViewSet, the per-action mixins, and @service_action / @selector_action for custom routes.
  • One reading of a request: route captures, the query string and the body become the spec's arguments by the kernel's rules, and a refusal comes back as DRF's answer with DRF's status.
  • DRF's seats: serializers for input and output, permission classes for authorization, with one serializer context per request.
  • OPTIONS and OpenAPI read off the spec: the opt-in drf-spectacular integration describes each route's request and response.

Full documentation: https://artui.github.io/django-spec-rest/

Coming from djangorestframework-services? The moving page has the import map and every change a client sees.

Install

pip install django-spec-rest
# or, with uv:
uv add django-spec-rest

The package is not a Django app and needs no INSTALLED_APPS entry. A spec's filter_set needs the kernel's filter extra (pip install "django-service-specs[filter]"), and the OpenAPI integration needs pip install "django-spec-rest[spectacular]".

Quick start

A service, a selector and two serializers, all plain:

from rest_framework import serializers

from invoices.models import Invoice


def create_invoice(*, data):
    return Invoice.objects.create(**data)


def list_invoices():
    return Invoice.objects.all()


class InvoiceInput(serializers.Serializer):
    customer = serializers.CharField(max_length=200)
    amount_cents = serializers.IntegerField(min_value=0)


class InvoiceSerializer(serializers.ModelSerializer):
    class Meta:
        model = Invoice
        fields = ("id", "customer", "amount_cents", "status")

Kernel specs with DRF in their seats, served on a viewset:

from django_service_specs import SelectorKind, SelectorSpec, ServiceSpec, Unrestricted
from rest_framework.routers import DefaultRouter

from django_spec_rest import ServiceViewSet
from django_spec_rest.service_specs.serializer_presenter import SerializerPresenter
from django_spec_rest.service_specs.serializer_validator import SerializerValidator

OPEN = (Unrestricted(),)
PRESENTER = SerializerPresenter(InvoiceSerializer)


class InvoiceViewSet(ServiceViewSet):
    action_specs = {
        "list": SelectorSpec(
            kind=SelectorKind.LIST,
            selector=list_invoices,
            permissions=OPEN,
            presenter=PRESENTER,
        ),
        "create": ServiceSpec(
            service=create_invoice,
            validator=SerializerValidator(InvoiceInput),
            permissions=OPEN,
            presenter=PRESENTER,
        ),
    }


router = DefaultRouter()
router.register("invoices", InvoiceViewSet, basename="invoice")
urlpatterns = router.urls

GET /invoices/ answers the rows rendered by InvoiceSerializer; POST /invoices/ answers 201 with the created row, or 400 keyed by field. The kernel refuses to dispatch a spec that declares no permissions, so Unrestricted() says the operation is open to every caller. The quickstart carries this on to retrieve, update, OPTIONS and a call off HTTP.

Example project

examples/ is a runnable Django project on one resource, with a test per behaviour: a spec on a viewset and on a standalone view, a filter read off the query string, OPTIONS on a detail route, a refusal of each status, a DRFPermissionCheck over HTTP and through the kernel's plain dispatch, a caller nobody authenticated refused 401 with the first authenticator's WWW-Authenticate (by an adapted DRF class and by a kernel PermissionCheck, each answering a signed-in caller 403), the generated OpenAPI schema carrying those 401 and 403 responses and the 409 a service raises, and a key one serializer writes into the shared context that another renders. make examples runs the whole suite, schema tests included.

cd examples
python manage.py migrate
python manage.py runserver

Compatibility

Axis Range
Python 3.10 – 3.14
Django 4.2, 5.0, 5.1, 5.2, 6.0
DRF ≥ 3.14

CI runs the full Python × Django matrix with 100% coverage gating.

Status

Pre-1.0. See CHANGELOG.md for the release history.

License

MIT.

Metadata

Release files for django-spec-rest 0.12.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-spec-rest 0.12.0
File Size Uploaded Provenance Actions
django_spec_rest-0.12.0.tar.gz 802.6 kB With provenance Details for django_spec_rest-0.12.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for django-spec-rest 0.12.0
File Interpreter ABI Platform Actions
django_spec_rest-0.12.0-py3-none-any.whl Python 3 none any Details for django_spec_rest-0.12.0-py3-none-any.whl

Total release size: 1.0 MB

Release files / django_spec_rest-0.12.0.tar.gz

Download URL django_spec_rest-0.12.0.tar.gz
Size 802.6 kB
Tags Source
SHA-256 checksum
How to use checksums
a86741226ae15a660a3c36d8bf708ee467ef36866d357d33bb62026e61378314
BLAKE2b-256 checksum
How to use checksums
4f995919861b844ba253e7983d574f447f845e69c68b6730252ff6979a408c1b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 9, 2026.

Transparency log

Release files / django_spec_rest-0.12.0-py3-none-any.whl

Download URL django_spec_rest-0.12.0-py3-none-any.whl
Size 235.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
65e6d402db7dd138a176574f9a7ceb85a20cf941608cc5fb0899f8fb9a0fcc2d
BLAKE2b-256 checksum
How to use checksums
77d2cd9f299d470545e7710cef945bef715804c6d575d73d3f4c9d0428a40026
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 9, 2026.

Transparency log

Security for django-spec-rest 0.12.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for django_spec_rest-0.12.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 9, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page