Architektur

How-to: Python Rest API

Python liegt als Programmiersprache voll im Trend. Mit diesem Blogpost möchte ich euch zeigen, dass Python weitaus mehr kann als nur KI und Data Science, sondern sich auch für REST APIs sehr gut eignet und leicht zu erlernen ist.

Das Beispiel

Die Poolfahrzeuge der Firma sollen in eine Postgresql Datenbank integriert werden (Python unterstützt auch viele andere Datenbanken). Mithilfe der API soll es möglich sein, alle Fahrzeuge aufzurufen, sowie einzelne zu löschen oder neue zu erstellen. Wir benötigen also die HTTP Anfragen GET, POST und DELETE, um das Ganze übersichtlich darzustellen werden wir zudem eine Webapplikation mit Angular erzeugen. Dazu gibt es aber noch einen weiteren Blogpost.

Benötigte Programme

  • Python richtig installieren
  • Docker dient dazu unsere Datenbank in einem Container zu isolieren, sodass wir PostgreSQL nicht auf unserem PC installieren müssen. Dies ist kein Muss, aber sinnvoll, wenn man die Datenbank später über einen Server laufen lassen will.
  • Postman habe ich benutzt, um die API Anfragen zu versenden.

Umgebung

Python

Um die benötigten Pakete zu installieren benutzen wir die requirements.txt Datei.

requirements.txt
  Flask==1.1.1
  Flask-Cors==3.0.8
  flask-marshmallow==0.10.1
  Flask-SQLAlchemy==2.4.1
  marshmallow==3.2.2
  SQLAlchemy==1.3.11
  marshmallow-sqlalchemy==0.19.0
  psycopg2-binary==2.8.4

der Befehl dazu lautet:

$ pip install -r requirements.txt
  • Flask ist ein Webframework mit großer Erweiterbarkeit und guter Dokumentation und eignet sich gerade für Anfänger, da man nur sehr wenig Code für eine eigene API braucht.
  • SQLAlchemy ist ein Toolkit für SQL-Datenbanken
  • Marshmallow wandelt die Datenbank in einen Python-eigenen Datentypen um
  • psycopg2 ist ein Adapter für Datenbanken und wird benötigt, damit die Anfragen auf die Datenbank reibungslos funktionieren

Datenbank

Damit Docker eine Postgres Datenbank starten kann. benötigt es ein Image von Postgres, dies geschieht mit dem Befehl

$ docker pull postgres

Wie ihr den Docker Container startet und die Datenbank einrichtet erfahrt ihr hier. Wer seinem Server noch ein Passwort geben möchte, braucht dazu nur folgende Ergänzung

-e POSTGRES_PASSWORD=Passwort

REST API

Nun ist unser System einsatzbereit. Alles folgende findet ihr auch in python_rest.py

Python Vorgeplänkel

from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow, fields

Initialisieren der Datenbank

app = Flask(__name__)
CORS(app)

Zu Beginn erzeugen wir eine Instanz der Flask Klasse, mit der wir arbeiten können. Mit CORS(app) erlauben wir alle Arten von Zugriffen auf die Datenbank. Sonst kann es passieren, dass wir später mit unserer Webapplikation keine Zugriffsberechtigung erhalten.

Verbinden mit der Datenbank

app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://postgres:Passwort@0.0.0.0:5432/esentricar'
  • postgresql das Datenbanksystem
  • postgres Benutzer der Datenbank
  • 0.0.0.0:5432 IP Adresse der Datenbank, hier kann auch ein lokaler Pfad genannt werden, wenn die Datenbank als Datei vorliegt.
  • esentricar Name der Datenbank
  • Passwort der Datenbank

Die Datenbank kann jetzt in SQLAlchemy und Marshmallow initialisiert werden.

db = SQLAlchemy(app)
ma = Marshmallow(app)

Tabellenkonfiguration

class Pool_Car(db.Model):

    #define table, in our case already existing
    __tablename__ = 'cars'
    car_id =         db.Column(db.Integer, primary_key=True)
    license_plate =  db.Column(db.String(30), unique=True)
    car_type =       db.Column(db.String(20))
    fuel =           db.Column(db.String(20))
    number_of_seats =db.Column(db.Integer)


    # set class attributes
    def __init__(self, license_plate, car_type, fuel, number_of_seats):
        self.license_plate = license_plate
        self.car_type   = car_type
        self.fuel       = fuel
        self.number_of_seats = number_of_seats

Die Klasse Pool_Car definiert die Tabelle in der Datenbank und bestimmt ihre Attribute. Im zweiten Teil wird definiert, wie wir die Tabelle aufrufen können.

class Pool_CarSchema(ma.Schema):
    class Meta:
        fields = ('car_id','license_plate','car_type','fuel','number_of_seats')

# Init schema
pool_car_schema = Pool_CarSchema()
pool_cars_schema = Pool_CarSchema(many=True)

Hier wird die Tabelle in Marshmallow eingelesen, damit kann sie mit Python besser bearbeitet und ausgegeben werden.

Tabelle erzeugen

Bei der ersten Benutzung der API muss zunächst die Tabelle in der Datenbank erstellt werden. Dies machen wir mit Python, da wir dort bereits die Attribute definiert haben. Es ist aber auch möglich dies über Postgres zu machen

$ python

in Python wird folgendes ausgeführt

   from python_rest import db
   db.create_all()

API Anfragen

Ein Grund, wieso ich Flask gewählt habe ist die Übersichtlichkeit der API Befehle, z.B:

@app.route('/car', methods=['POST'])
  • /car deklariert die Adresse
  • methods=[‚POST‘] die Art der API Anfrage, hier POST

POST

@app.route('/car', methods=['POST'])
def add_pool_car():
  # Get request data as json
  car_entity = request.get_json()
  license_plate= car_entity.get('license_plate')
  car_type = car_entity.get('car_type')
  fuel = car_entity.get('fuel')
  number_of_seats = car_entity.get('number_of_seats')

  new_pool_car = Pool_Car(license_plate, car_type, fuel, number_of_seats)

  db.session.add(new_pool_car)
  db.session.commit()
  
  return pool_car_schema.jsonify(new_pool_car)

Zuerst wird die JSON Datei eingelesen und den Attributen der Tabelle zugeordnet. Danach muss es nur noch der Tabelle hinzugefügt werden (Zeile 60) und hochgeladen werden.

GET

In unserem Beispiel soll es zwei Listen geben, einmal „/car“ mit einer groben Übersicht über alle Autos und eine „/car/car_id“ Liste für jedes einzelne Auto, die detaillierter ist. Dementsprechend werden auch zwei GET Methoden benötigt.

@app.route('/car', methods=['GET'])
def get_pool_cars():
  all_pool_cars = Pool_Car.query.all()
  result = pool_cars_schema.dump(all_pool_cars)
  car_list= []
  for car in result:
      car_details ={ "car_id":None, "license_plate":None, "car_type":None}
      car_details['car_id'] = car['car_id']
      car_details['license_plate'] = car['license_plate']
      car_details['car_type'] = car['car_type']
      car_list.append(car_details)
  return jsonify(car_list)

Diese ist komplizierter, da wir eine Zeile nicht 1zu1 wieder ausgeben, sondern nur bestimmte Attribute. Dafür wird der Inhalt der Tabelle mit Marshmallow.dump (Zeile 69) ausgelesen, so kann Python auf einzelne Elemente zugreifen. Diese werden dann in einer Liste zusammengefügt und in ein JSON-Format umgewandelt.

Die anderen beiden API-Anfragen sind ziemlich ähnlich aufgebaut und sollten vom Verständnis kein Problem mehr sein.

@app.route('/car/', methods=['GET'])
def get_pool_car(car_id):
  pool_car = Pool_Car.query.get(car_id)
  return pool_car_schema.jsonify(pool_car)

DELETE

@app.route('/car/', methods=['DELETE'])
def delete_pool_car(car_id):
  pool_car = Pool_Car.query.get(car_id)
  db.session.delete(pool_car)
  db.session.commit()

  return pool_car_schema.jsonify(pool_car)

Fazit

Eine REST API lässt sich in Python mühelos und mit wenig Aufwand schreiben. Ich kann daher nur jedem raten sich mal daran zu probieren.

Wer wissen möchte, wie es mit unseren Pool-Fahrzeugen weitergeht und wie daraus mithilfe von Angular eine nutzbare Webapplikation entsteht, sollte im Laufe der Woche auf unserer Webseite vorbeischauen. Dort gibt es dann die Fortsetzung.

Hier findet ihr das Github Repository zu diesem Artikel.