Eine Restful API für unsere App
Ein anderes Unternehmen will unsere Events importieren und auf seiner App
darstellen. Die Daten sollen im JSON-Format
von unserer Website zur Verfügung
gestellt werden und stets den aktuellen Zustand abbilden.
Dazu benötigen wir eine API. Das Django Modul Django
Restframework
ist ein ausgereiftes Paket zur Erstellung von Rest-APIs in
Django, die sich zudem komfortabel und einfach in bestehende Projekte integerieren lässt.
Dennoch ist sie mächtig genug, um alles, was an API-Technologie benötigt werden könnte, umzusetzen.
Was ist eine REST-API?
Ein Rest-API ist eine Web-API, die den Beschränkungen der REST-Architektur unterliegt und Interaktionen mit Webservices ermöglicht.
Die Architektur basiert auf 6 Prinzipien, die vom Entwickler flexibel umgesetzt werden können. Es gilt die
Client-Server-Architektur
die Zustandslosigkeit, es werden also keine Nachrichtenteile etwa in
Sessions gespeichert - es ist ein Caching implementiert - die Schnittstelle ist einheitlich - die Systeme sind mehrschichtig aufgebaut und Code kann on Demand an den Nutzer gesendet werden, um zum Beispiel via JavaScript weiterverarbeitet zu werden
Mehr zu REST auf Wikipedia: https://de.wikipedia.org/wiki/Representational_State_Transfer
Einrichten der API
Alles, was die API für die Event-App betrifft legen wir in ein eigenes Verzeichnis event_manager/events/api
. Dort legen wir drei Dateien an: serializers.py
, urls.py
und views.py
event_manager/events
├───api
│ serializers.py
│ urls.py
│ views.py
Serializer
In serializers.py
werden wir die Serialisierer für die aus- und eingehgenden Daten definieren. Unter Serialisierung versteht man in diesem Kontext den Prozess, ausgehende Daten vorweg in ein Austauschformat wie Json
oder XML
und eingehende Daten aus einem Austauschformat in Python Datentypen zu formatieren.
URLS
Unsere Api benötigt natürlich auch wieder URLs, die von außen ansprechbar sind.
http://127.0.0.1:8000/api/events
wäre zum Beispiel eine solche URL.
Views
Bei den Views handelt es sich um Controller-Klassen, die die Daten bereitstellen, die serialisiert werden sollen.
Legen wir zuerst die Serializer an:
Serializer
Wir öffnen die Datei event_manager/events/api/serializers.py
und fügen dort folgenden Code hinzu:
import arrow from rest_framework import serializers from events import models class CategorySerializer(serializers.ModelSerializer): class Meta: model = models.Category exclude = ("slug",) class ReviewInlineSerializer(serializers.ModelSerializer): class Meta: model = models.Review fields = ("author", "rating", "review") author = serializers.StringRelatedField(read_only=True) class EventSerializer(serializers.ModelSerializer): days_to_event = serializers.SerializerMethodField() reviews = ReviewInlineSerializer(many=True, read_only=True) class Meta: model = models.Event fields = ("id", "name", "sub_title", "category", "days_to_event", "date", "min_group", "reviews", ) def get_days_to_event(self, object): diff = object.date - arrow.now() return diff.days def validate_date(self, value): """ Check if date is smaller than today. oho """ if value < arrow.now(): raise serializers.ValidationError("Event date is in the past") return value def validate(self, attrs): """call the clean method of Event-model""" self.Meta.model(**attrs).clean() return attrs
Views
Für die Views nutzen wir die Viewsets aus dem Django-Restframework. Dazu legen wir die Datei event_manager/events/api/views.py
an:
from rest_framework import viewsets from rest_framework import filters from events import models from . import serializers class CategoryViewset(viewsets.ModelViewSet): queryset = models.Category.objects.all() filter_backends = (filters.SearchFilter, filters.OrderingFilter) serializer_class = serializers.CategorySerializer class EventViewset(viewsets.ModelViewSet): """ http://127.0.0.1:8000/api/events/?ordering=date http://127.0.0.1:8000/api/events/?ordering=-date """ queryset = models.Event.objects.all() serializer_class = serializers.EventSerializer filter_backends = [filters.OrderingFilter] # es darf nach folgenden Werten geordnet werden: ordering_fields = ['date', 'name'] def perform_create(self, serializer): author = self.request.user serializer.save(author=author)
Routes
Zuletzt müssen wir noch die URLs festlegen. Wir legen an: event_manager/events/api/urls.py
from rest_framework import routers from . import views router = routers.DefaultRouter() router.register(r'category', views.CategoryViewset) router.register(r'events', views.EventViewset)
und verdrahten sie mit dem Projekt. Wir öffnen event_manager/event_manager/urls.py
und fügen ein:
from events.api.urls import router
urlpatterns = [
# andere URLs
path("api-auth/", include("rest_framework.urls")),
path('api/', include(router.urls)),
]
Via Api-Auth können wir uns auch via API am System einloggen:
Diese Möglichkeit ist allerdings meistens gar nicht gewünscht, da eine
Authentifizierung an einer API hauptsächlich über sogenannte Tokens
gelöst
wird, und nicht über gewöhnliche Sessions, wie man das von Webanwendungen oft
gewohnt ist.
OpenAPI-Spezifikation
Die OpenAPI-Spezifikation (OAS) definiert eine standardisierte, sprachunabhängige Schnittstelle zu RESTful-APIs, die es sowohl Menschen als auch Computern ermöglicht, entfernte APIs bequem zu testen.
Im Idealfall, dh. wenn das OpenAPI-Schema der API richtig implementiert ist, kann ein User mit der API über diese Schnittstelle interagieren.
Eine OpenAPI-Definition kann zur Dokumentationserstellung und Code-Geniererung verwendet werden, um die API darzustellen.
Eine API-Spezifiktation wird in YAML
oder JSON
geschrieben.
Mehr zu OAS hier https://www.openapis.org/
Swagger
Swagger ist eine Reihe von Open-Source-Tools, die auf der OpenAPI-Spezifikation basieren und bei der Entwicklung, Erstellung, Dokumentation und Nutzung von REST-APIs unterstützen.
Um auf Basis der OpenAPI-Spezifikation
eine interaktive Dokumentation zu
erstellen, können wir Swagger
nutzen. Um aus den Api-Endpunkten, die wir
mit dem Django Restframework
erstellt hatten, automatisch ein OpenAPI-Schema zu
generieren, nutzen wir eine Django-App namens drf-spectacular
.
Dieses Tool sucht nach Api-Endpunkten und erstellt daraus, u.a. auch on-the-fly ein
Open-Api-Scheme, welches dann von drf-spectacular-sidecar
genutzten werden
kann, um eine Swagger-UI
zu erstellen. Eine Swagger-UI ist nichts anderes,
als eine schöne, interaktive Oberfläche zum Testen der API. Ein ähnliches Tool
ist Redoc
. Auch die Anwendung Postman
kann OpenApi-Daten lesen und
daraus eine Oberfläche zum Testen der API bereitstellen.
Es soll nicht unerwähnt bleiben, dass man das OpenAPI-Schema auch selber erstellen kann, da es sich hier um nichts weiter als eine YAML-Datei handelt.
Mehr zu Swagger and OpenApi * https://swagger.io/docs/specification/about/ * https://en.wikipedia.org/wiki/Swagger_(software)
Swagger installieren
Wir legen in der requirements.in
die beiden folgenden Pakete fest:
# andere Apps
drf-spectacular
drf-spectacular-sidecar
und installieren sie:
(eventenv) pip-compile requirements.in
(eventenv) pip-sync requirements.txt requirements-dev.txt
Mehr zu drf-specatular bzw. Sidecar hier: * https://github.com/tfranzel/drf-spectacular-sidecar * https://github.com/tfranzel/drf-spectacular * https://swagger.io/tools/swagger-ui/
in den settings/base.py
registrieren wir drf-spectacular
als
DEFAULT_SCHEMA_CLASS
des Restframeworks:
REST_FRAMEWORK = {
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
}
diese Klasse kümmert sich um das automatische Generieren der OpenApi-Spezifikation für unsere API-Anwendung.
sowie in den INSTALLED APPS …
INSTALLED_APPS = [
[..]
"django.contrib.admindocs",
"crispy_forms",
"crispy_bootstrap5",
"rest_framework",
"rest_framework.authtoken",
"drf_spectacular",
'drf_spectacular_sidecar',
[..]
]
Ebenfalls in den settings/base.py
legen wir die SPECTACULAR_SETTINGS
an, die uns die Möglichkeit gibt, die API-Beschreibung zu anzupassen.
SPECTACULAR_SETTINGS = {
'TITLE': 'Event Manager API',
'DESCRIPTION': 'Django Event manager',
'VERSION': '1.0.0',
'SERVE_INCLUDE_SCHEMA': False,
'SWAGGER_UI_DIST': 'SIDECAR',
'SWAGGER_UI_FAVICON_HREF': 'SIDECAR',
'SERVE_AUTHENTICATION': ['rest_framework.authentication.SessionAuthentication'],
'SERVE_PERMISSIONS': ['rest_framework.permissions.IsAuthenticated'],
# OTHER SETTINGS
}
Hier haben wir einen Title und eine Description angelegt sowie einige spezfische Api-Einstellungen gesetzt.
|
die Überschrift der Oberfläche |
|
eine Kurzbeschreibung der API. Hier ist auch HTML erlaubt. |
|
die aktuelle Version der API. |
|
die graph. Oberfläche für die API |
Zusätzlich geben wir noch per SERVE_AUTHENTICATION
und SERVE_PERMISSIONS
die
Rechte an, die nötig sind, um die SWAGGER-UI
aufzurufen.
Um die Api-Dokumentation via Url erreichbar zu machen, tragen wir den Path in die event_manager/event_manager/urls.py
ein:
from drf_spectacular.views import SpectacularAPIView
from drf_spectacular.views import SpectacularSwaggerView
urlpatterns = [
# andere urls
path(
"schema/",
SpectacularAPIView.as_view(api_version="v2"),
name="schema",
),
path(
"docs/",
SpectacularSwaggerView.as_view(url_name="schema"),
name="swagger-ui",
),
]
Beim Aufruf von http://127.0.0.1:8000/docs/
sollte die Api-Dokumentation zu
sehen sein. Alle Api-Endpunkte können nun ausprobiert werden. Das Schema wird
autogeneriert und über urlname="schema"
erreichbar gemacht.
Man könnte dieses Schema auch über ein anderes Programm wie Redoc
oder
Postman
integrieren. Mehr dazu unter:
https://learning.postman.com/docs/integrations/available-integrations/working-with-openAPI/
So sollte das Ergebnis jetzt aussehen:
manage.py Subkommando spectacular
Via manage.py
steht uns mit spectacular
ein neues Subkommando zur
Verfügung, um mit der Anwedung zu interagieren.
Um uns zum Beispiel das aktuelle API-Schema im YAML-Format
ausgeben zu lassen, nutzen wir
diese Befehl:
python manage.py spectacular --format openapi
Im Hilfemodus der App können wir noch weitere Möglichkeiten finden. Probiert es aus!
python manage.py spectacular --help