Django Ninja - ein asynchrones Restframework für Django

https://www.youtube.com/watch?v=S4nUhruExwA aktuell 8:00

Installation

In die requirements.in wird eingetragen:

django-ninja
pip-compile requirements.in
pip-sync requirements-dev.txt requirements.txt

Vorarbeiten

Eine neue App namens tickets erstellen und ein Model anlegen.

python manage.py startapp tickets

In die neu erstellt App legen wir unter models.py ein Model für ein Ticket an.

from django.db import models
from event_manager.mixins import DateMixin


class Ticket(DateMixin):
    class Status(models.TextChoices):
        "new", "new"

    status = models.CharField(max_length=20, choices=Status.choices)

und führen wir gewohnt makemirations und migrate aus.

Unter event_manager/tickets legen wir nun eine Datei api.py, in der der Quellcode für die Ninja-API liegen wird. Wir legen auch noch eine urls.py an. Unser neues tickets-verzeichnis sieht nun so aus:

.
├── admin.py
├── api.py
├── apps.py
├── __init__.py
├── migrations
├── models.py
├── tests.py
├── urls.py
└── views.py

Legen wir zuerst unter event_manager/event_manager/urls.py die Verknüpfung zu unserer Api fest, damit die API-Endpunkte auch gefunden werden können.

urlpatterns = [
    ...
    path("api/tickets/", include("tickets.urls")),
    ...
]

in die event_manager/tickets/urls.py tragen wir folgendes ein:

from django.urls import path
from .api import api

urlpatters = [
    path("", api.urls),
]

Die tatsächlichen Routen erzeugt Django-Ninja automatisch für uns.

Der erste Endpunkt

Unter event_manager/tickets/api.py tragen wir folgenden Code ein:

from ninja import NinjaAPI

api = NinjaAPI()


@api.get("/")
def home(request):
    return "Hello World"

Hier haben wir einen Endpunkt erstellt, der jetzt unter http://127.0.0.1:8000/api/tickets/ erreichbar ist. Die URL hat Django-Ninja für uns selbst erstellt, auch um die Serialisierung mussten wir uns nicht kümmern.

Ticket erstellen

Um ein Ticket zu erstellen, benötigen wir das Django ORM. Um die Eingangsdaten allerdings erstmal zu validieren, legen wir ein Schema an

from ninja import ModelSchema, Schema
from .models import Ticket


class MessageIn(Schema):
    title: str
    msg: str

Und definieren in api.py den entsprechenden Endpunkt.

from .schema import MessageIn

@api.post("msg")
def msg(request, data: MessageIn):
    return data

jetzt können wir das zum Beispiel mit curl testen

curl http://127.0.0.1:8000/api/tickets/msg -d '{"title":"wow", "msg":"hi
there!"}' -X POST

Das hat geklappt! Jetzt legen wir ein eingehendes Schema für ein Ticket an:

class TicketIn(ModelSchema):
    class Config:
        model = Ticket
        model_fields = ("status",)

Und definieren in api.py einen weiteren Endpunkt.

@api.post("/")
def ticket_add(request, data: TicketIn) -> dict:
    ticket = Ticket.objects.create(**data.dict())
    return {"result": "success", "id": ticket.id}

Um ein neues Ticket anzulegen, brauchen wir wieder curl:

curl http://127.0.0.1:8000/api/tickets/ -d '{"status":"new"}' -X POST

Falls die Eingabedaten falsch sind, bekommen wir einen Fehler serviert

curl http://127.0.0.1:8000/api/tickets/ -d '{"status":"neu"}' -X POST -vv
curl http://127.0.0.1:8000/api/tickets/3 -d '{"status":"neu"}' -X PUT

Um ein Ticket upzudaten, können wir die HTTP-Methoden put oder patch nutzen. Für put machen wir es so

@api.put("/put/{ticket_id}")
def ticket_put(request, ticket_id: int, payload: TicketIn):
    obj = get_object_or_404(Ticket, pk=ticket_id)
    for attr, value in payload.dict().items():
        if value:
            setattr(obj, attr, value)
    return {"id": obj.pk}

curl -X PUT http://127.0.0.1:8000/api/tickets/put/3 -d ‚{„status“:“new“}‘

@api.delete("/{ticket_id}")
def ticket_delete(request, ticket_id: int):
    obj = get_object_or_404(Ticket, pk=ticket_id)
    obj.delete()
    return {"success": True}

curl -X DELETE http://127.0.0.1:8000/api/tickets/3