cds-104_Datenbanken/Block-2/Zoo_Aufgabe.ipynb

414 lines
13 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Zooverwaltung\n",
"\n",
"In diesem Beispiel wird ein System zur Verwaltung von Zoos implementiert. Hier gibt es eine Tabelle für Personen (TierplegerInnen) und eine Tabelle für Tiere. Eine Person kann mehrere Tiere betreuen, und ein Tier gehört einer Person.\n",
"\n",
"Im ersten Teil sollen Sie 5 Funktionen schreiben, die es Ihnen erlaubt, Personen anzulegen, zu lesen (einzeln und als Liste), zu ändern und zu löschen. Die Tabellen sind dabei vorgegeben und Sie finden im Notebook einige Testaufufe."
]
},
{
"cell_type": "code",
"metadata": {
"ExecuteTime": {
"end_time": "2026-05-08T09:17:37.945734700Z",
"start_time": "2026-05-08T09:17:37.898267121Z"
}
},
"source": [
"import psycopg2\n",
"import psycopg2.extras\n",
"import json"
],
"outputs": [],
"execution_count": 15
},
{
"cell_type": "code",
"metadata": {
"ExecuteTime": {
"end_time": "2026-05-08T09:17:54.987395691Z",
"start_time": "2026-05-08T09:17:54.967699370Z"
}
},
"source": [
"# Verbindung aufbauen\n",
"def get_connection():\n",
" with open(\"../.db_config.json\") as f:\n",
" cfg = json.load(f)\n",
"\n",
" conn = psycopg2.connect(\n",
" host = cfg[\"host\"],\n",
" port = cfg[\"port\"],\n",
" dbname = \"zoo\",\n",
" user = cfg[\"user\"],\n",
" password = cfg[\"password\"],\n",
" )\n",
" return conn"
],
"outputs": [],
"execution_count": 16
},
{
"cell_type": "code",
"metadata": {
"ExecuteTime": {
"end_time": "2026-05-08T09:17:56.651002558Z",
"start_time": "2026-05-08T09:17:56.616450554Z"
}
},
"source": [
"conn = get_connection()\n",
"\n",
"# Diese Zelle löscht die Tabellen, falls sie bereits existieren, und legt sie neu an\n",
"sql = \"\"\"\n",
" DROP TABLE IF EXISTS animals;\n",
" DROP TABLE IF EXISTS zookeepers;\n",
"\n",
" CREATE TABLE zookeepers (\n",
" id SERIAL PRIMARY KEY,\n",
" name VARCHAR(255) NOT NULL,\n",
" email VARCHAR(255),\n",
" specialty VARCHAR(255)\n",
" );\n",
"\n",
" CREATE TABLE animals (\n",
" id SERIAL PRIMARY KEY,\n",
" name VARCHAR(255) NOT NULL,\n",
" species VARCHAR(255) NOT NULL,\n",
" zookeeper_id INTEGER,\n",
" FOREIGN KEY (zookeeper_id) REFERENCES zookeepers(id)\n",
" );\n",
"\"\"\"\n",
"\n",
"# SQL ausführen\n",
"cur = conn.cursor()\n",
"cur.execute(sql)\n",
"conn.commit()\n"
],
"outputs": [],
"execution_count": 17
},
{
"cell_type": "code",
"metadata": {
"ExecuteTime": {
"end_time": "2026-05-08T09:17:58.961644024Z",
"start_time": "2026-05-08T09:17:58.949110338Z"
}
},
"source": [
"def create_zookeeper(name, email, specialty):\n",
" try:\n",
" cur.execute(\"\"\"\n",
" INSERT INTO zookeepers (name, email, specialty)\n",
" VALUES (%s, %s, %s)\n",
" RETURNING id;\n",
" \"\"\", (name, email, specialty))\n",
" conn.commit()\n",
" return cur.fetchone()[0]\n",
" except:\n",
" conn.rollback()\n",
"\n",
"def read_zookeeper(id):\n",
" cur.execute(\"SELECT * FROM zookeepers where id=%s\", (id,))\n",
" zookeeper = cur.fetchone()\n",
" return tuple(zookeeper) if zookeeper else None\n",
"\n",
"def read_all_zookeepers():\n",
" cur.execute(\"SELECT * FROM zookeepers order by id\")\n",
" return [(zookeeper) for zookeeper in cur.fetchall()]\n",
"\n",
"def update_zookeeper(id, name, email, specialty):\n",
" try:\n",
" cur.execute(\"\"\"\n",
" UPDATE zookeepers\n",
" SET name=%s, email=%s, specialty=%s\n",
" WHERE id=%s\n",
" \"\"\", (name, email, specialty, id))\n",
" conn.commit()\n",
" except:\n",
" conn.rollback()\n",
"\n",
"def delete_zookeeper(id):\n",
" try:\n",
" cur.execute(\"\"\"\n",
" DELETE FROM zookeepers\n",
" WHERE id=%s\n",
" \"\"\", (id,))\n",
" conn.commit()\n",
" except:\n",
" conn.rollback()\n",
" raise"
],
"outputs": [],
"execution_count": 18
},
{
"cell_type": "code",
"metadata": {
"ExecuteTime": {
"end_time": "2026-05-08T09:18:01.926507382Z",
"start_time": "2026-05-08T09:18:01.895838996Z"
}
},
"source": [
"# Tests - diese sollten alle erfolgreich durchlaufen werden und nicht verändert werden\n",
"\n",
"john = (\"John Doe\", \"john@example.com\", \"Elephants\")\n",
"jane = (\"Jane Doe\", \"jane@example.com\", \"Giraffes\")\n",
"\n",
"id = create_zookeeper(*john)\n",
"id2 = create_zookeeper(*jane)\n",
"\n",
"assert read_zookeeper(id) == (id, *john)\n",
"assert read_zookeeper(id2) == (id2, *jane)\n",
"\n",
"john = (\"John Smith\", \"john2@example.com\", \"Zebras\")\n",
"update_zookeeper(id, *john)\n",
"assert read_zookeeper(id) == (id, *john)\n",
"\n",
"all_zookeepers = read_all_zookeepers()\n",
"assert len(all_zookeepers) == 2\n",
"assert all_zookeepers[0] == (id, *john)\n",
"assert all_zookeepers[1] == (id2, *jane)\n",
"\n",
"delete_zookeeper(id)\n",
"delete_zookeeper(id2)\n",
"assert read_zookeeper(id) == None\n",
"assert read_zookeeper(id2) == None\n",
"\n",
"all_zookeepers = read_all_zookeepers()\n",
"assert len(all_zookeepers) == 0"
],
"outputs": [],
"execution_count": 19
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Nutzung eines ORMs\n",
"\n",
"Wie wir sehen, ist das manuelle Erstellen von Zugriffsfunktionen auf die Datenbank sehr aufwändig. Daher gibt es sogenannte Object-Relational-Mapper (ORMs), die uns diese Arbeit abnehmen. Ein bekanntes ORM für Python ist `SQLAlchemy`. Im zweiten Teil des Notebooks werden wir sehen, wie mit `SQLAlchemy` Daten eingefügt und gelesen werden können.\n",
"\n",
"Da Sie Anaconda nutzen, sollte `SQLAlchemy` bereits installiert sein. Falls nicht, können Sie es mit `conda` installieren.\n",
"\n",
"Wenn man `SQLAlchemy` verwendet, definiert man zunächst eine Klasse, die die Tabelle repräsentiert. Hier sind diese Klassen bereits für Personen und Tiere definiert. Sie können sich die Klassen ansehen, um zu verstehen, wie Tabellen in `SQLAlchemy` definiert werden."
]
},
{
"cell_type": "code",
"metadata": {
"ExecuteTime": {
"end_time": "2026-05-08T09:19:23.034281307Z",
"start_time": "2026-05-08T09:19:22.981802943Z"
}
},
"source": [
"## Nutzung von SQLAlchemy\n",
"from sqlalchemy import create_engine, Column, Integer, String, ForeignKey, select\n",
"from sqlalchemy.orm import Session, relationship, declarative_base, join, aliased\n",
"\n",
"Base = declarative_base()\n",
"\n",
"class Zookeeper(Base):\n",
" __tablename__ = 'zookeepers'\n",
" id = Column(Integer, primary_key=True)\n",
" name = Column(String, nullable=False)\n",
" email = Column(String)\n",
" specialty = Column(String)\n",
" animals = relationship(\"Animal\", back_populates=\"zookeeper\")\n",
"\n",
"class Animal(Base):\n",
" __tablename__ = 'animals'\n",
" id = Column(Integer, primary_key=True)\n",
" name = Column(String, nullable=False)\n",
" species = Column(String, nullable=False)\n",
" zookeeper_id = Column(Integer, ForeignKey('zookeepers.id'))\n",
" zookeeper = relationship(\"Zookeeper\", back_populates=\"animals\")\n",
"\n",
"with open(\"../.db_config.json\") as f:\n",
" cfg = json.load(f)\n",
"engine = create_engine(f'postgresql://{cfg[\"user\"]}:{cfg[\"password\"]}@{cfg[\"host\"]}/zoo')"
],
"outputs": [],
"execution_count": 21
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Anlegen und Abfragen von Objekten in SQLAlchemy\n",
"\n",
"In diesem Teil sollen Sie 2 Personen und 5 Tiere anlegen und dann alle Personen und Tiere abfragen. Sie können sich an den Beispielen orientieren, die im Notebook gegeben sind.\n",
"\n",
"Ein kurzes und übersichtliches Tutorial zur Nutzung von `SQLAlchemy` finden Sie hier:\n",
"\n",
"https://docs.sqlalchemy.org/en/20/orm/session_basics.html\n",
"\n",
"Relevant sind vor allem folgende Abschnitte:\n",
"\n",
"https://docs.sqlalchemy.org/en/20/orm/session_basics.html#opening-and-closing-a-session\n",
"https://docs.sqlalchemy.org/en/20/orm/session_basics.html#adding-new-or-existing-items\n",
"https://docs.sqlalchemy.org/en/20/orm/session_basics.html#querying"
]
},
{
"cell_type": "code",
"metadata": {
"ExecuteTime": {
"end_time": "2026-05-08T09:19:25.146660428Z",
"start_time": "2026-05-08T09:19:25.113939045Z"
}
},
"source": [
"with Session(engine) as session:\n",
" with session.begin():\n",
" john = Zookeeper(name=\"John Doe\", email=\"john@example.com\", specialty=\"Elephants\")\n",
" jane = Zookeeper(name=\"Jane Doe\", email=\"jane@example.com\", specialty=\"Giraffes\")\n",
" \n",
" session.add_all([john, jane])"
],
"outputs": [],
"execution_count": 22
},
{
"cell_type": "code",
"metadata": {
"ExecuteTime": {
"end_time": "2026-05-08T09:19:26.787406980Z",
"start_time": "2026-05-08T09:19:26.754820181Z"
}
},
"source": [
"with Session(engine) as session:\n",
" statement = select(Zookeeper.name)\n",
" print(session.scalars(statement).all())"
],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"['John Doe', 'Jane Doe']\n"
]
}
],
"execution_count": 23
},
{
"cell_type": "code",
"metadata": {
"ExecuteTime": {
"end_time": "2026-05-08T09:19:28.186994306Z",
"start_time": "2026-05-08T09:19:28.158814205Z"
}
},
"source": [
"with Session(engine) as session:\n",
" with session.begin():\n",
" john_clause = select(Zookeeper.id).where(Zookeeper.name.like(\"%John%\"))\n",
" john_id = session.scalars(john_clause).first()\n",
"\n",
" session.add_all([Animal(name=animal, species=\"Elephant\", zookeeper_id=john_id) for animal in [\"Babar\", \"Dumbo\", \"Hathi\"]])\n",
" \n",
"with Session(engine) as session:\n",
" with session.begin():\n",
" jane_clause = select(Zookeeper.id).where(Zookeeper.name.like(\"%Jane%\"))\n",
" jane_id = session.scalars(jane_clause).first()\n",
" \n",
" session.add_all([Animal(name=animal, species=\"Giraffe\", zookeeper_id=jane_id) for animal in [\"Melman\", \"Gloria\"]])"
],
"outputs": [],
"execution_count": 24
},
{
"cell_type": "code",
"metadata": {
"ExecuteTime": {
"end_time": "2026-05-08T09:19:31.605124061Z",
"start_time": "2026-05-08T09:19:31.570527613Z"
}
},
"source": [
"# Liste aller Tiere mit ihren Pflegern ausgeben:\n",
"with Session(engine) as session:\n",
" statement = select(Animal.name, Animal.species, Zookeeper.name).select_from(join(Animal, Zookeeper, Animal.zookeeper))\n",
" animals = session.execute(statement).all()\n",
"\n",
" col_widths = (\n",
" max([len(animal[0]) for animal in animals]),\n",
" max([len(animal[1]) for animal in animals]),\n",
" max([len(animal[2]) for animal in animals]),\n",
" )\n",
"\n",
" print(\"-\" * (sum(col_widths) + 10))\n",
" for animal in animals:\n",
" print(f\"| {animal[0]:{col_widths[0]}} | {animal[1]:{col_widths[1]}} | {animal[2]:{col_widths[2]}} |\")\n",
" print(\"-\" * (sum(col_widths) + 10))\n",
" \n",
"\n",
"# Beispielausgabe:\n",
"# \n",
"# ----------------------------------------\n",
"# | Babar | Elephant | John Doe |\n",
"# | Dumbo | Elephant | John Doe |\n",
"# | Hathi | Elephant | John Doe |\n",
"# | Melman | Giraffe | Jane Doe |\n",
"# | Gloria | Giraffe | Jane Doe |\n",
"# ----------------------------------------\n",
"\n"
],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"--------------------------------\n",
"| Babar | Elephant | John Doe |\n",
"| Dumbo | Elephant | John Doe |\n",
"| Hathi | Elephant | John Doe |\n",
"| Melman | Giraffe | Jane Doe |\n",
"| Gloria | Giraffe | Jane Doe |\n",
"--------------------------------\n"
]
}
],
"execution_count": 25
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python [conda env:base] *",
"language": "python",
"name": "conda-base-py"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.9"
}
},
"nbformat": 4,
"nbformat_minor": 4
}