django-spec-rest
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_actionfor 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-spectacularintegration 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, and a
key one serializer writes into the shared context that another renders.
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.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| django_spec_rest-0.4.0.tar.gz | 549.9 kB | With provenance | Details for django_spec_rest-0.4.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| django_spec_rest-0.4.0-py3-none-any.whl | Python 3 | none | any | Details for django_spec_rest-0.4.0-py3-none-any.whl |
Total release size: 740.6 kB
Release files / django_spec_rest-0.4.0.tar.gz
| Download URL | django_spec_rest-0.4.0.tar.gz |
|---|---|
| Size | 549.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0adbf72d0f844678618034c1bc2ba396ef0777d77ed7691feedc585a3b9f74b2
|
|
BLAKE2b-256 checksum How to use checksums |
95c9568d371435adda96aaf4f30a84291c62fdf1ebd7802e038e0d5066774e82
|
| 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 3, 2026.
Transparency logRelease files / django_spec_rest-0.4.0-py3-none-any.whl
| Download URL | django_spec_rest-0.4.0-py3-none-any.whl |
|---|---|
| Size | 190.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d8f70466388f0c8d1d383180a04cb32af9a98f6a10246c2ed8e8c233adbce26c
|
|
BLAKE2b-256 checksum How to use checksums |
3edee1fb499284abd2e875a29906d8229539ba8c330338f8364f3ca4b2ea01f6
|
| 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 3, 2026.
Transparency logSecurity for django-spec-rest 0.4.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.
Attestation for django_spec_rest-0.4.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 3, 2026.
Transparency logA 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.