335 lines
11 KiB
Plaintext
335 lines
11 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",
|
|
"execution_count": 27,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import psycopg2\n",
|
|
"import psycopg2.extras\n",
|
|
"import os\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 28,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Verbindung aufbauen\n",
|
|
"conn = psycopg2.connect(\n",
|
|
" host=os.getenv('PGHOST', 'localhost'),\n",
|
|
" port=int(os.getenv('PGPORT', 5432)),\n",
|
|
" dbname=os.getenv('PGDATABASE', 'zoo'),\n",
|
|
" user=os.getenv('PGUSER', 'postgres'),\n",
|
|
" password=os.getenv('PGPASSWORD', 'postgres'),\n",
|
|
")\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 29,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# 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"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 30,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# CRUD-Operationen für Zookeeper\n",
|
|
"def create_zookeeper(name, email, specialty):\n",
|
|
" with conn.cursor() as cur:\n",
|
|
" cur.execute(\"INSERT INTO zookeepers (name, email, specialty) VALUES (%s, %s, %s) RETURNING id\", (name, email, specialty))\n",
|
|
" new_id = cur.fetchone()[0]\n",
|
|
" conn.commit()\n",
|
|
" return new_id\n",
|
|
"\n",
|
|
"def read_zookeeper(id):\n",
|
|
" with conn.cursor() as cur:\n",
|
|
" cur.execute(\"SELECT id, name, email, specialty FROM zookeepers WHERE id = %s\", (id,))\n",
|
|
" row = cur.fetchone()\n",
|
|
" return row\n",
|
|
"\n",
|
|
"def read_all_zookeepers():\n",
|
|
" with conn.cursor() as cur:\n",
|
|
" cur.execute(\"SELECT id, name, email, specialty FROM zookeepers ORDER BY id\")\n",
|
|
" rows = cur.fetchall()\n",
|
|
" return rows\n",
|
|
"\n",
|
|
"def update_zookeeper(id, name, email, specialty):\n",
|
|
" with conn.cursor() as cur:\n",
|
|
" cur.execute(\"UPDATE zookeepers SET name = %s, email = %s, specialty = %s WHERE id = %s\", (name, email, specialty, id))\n",
|
|
" conn.commit()\n",
|
|
"\n",
|
|
"def delete_zookeeper(id):\n",
|
|
" with conn.cursor() as cur:\n",
|
|
" cur.execute(\"DELETE FROM zookeepers WHERE id = %s\", (id,))\n",
|
|
" conn.commit()\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 31,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"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"
|
|
]
|
|
},
|
|
{
|
|
"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",
|
|
"execution_count": 32,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"## Nutzung von SQLAlchemy\n",
|
|
"import os\n",
|
|
"from sqlalchemy import create_engine, Column, Integer, String, ForeignKey\n",
|
|
"from sqlalchemy.orm import Session, relationship, declarative_base\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",
|
|
"# Verbindung zur Datenbank herstellen\n",
|
|
"engine = create_engine(os.getenv('DATABASE_URL', 'postgresql://postgres:postgres@localhost:5432/zoo'))"
|
|
]
|
|
},
|
|
{
|
|
"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",
|
|
"execution_count": 33,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Zwei Zookeeper anlegen\n",
|
|
"with Session(engine) as session:\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",
|
|
" session.add_all([john, jane])\n",
|
|
" session.commit()\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 34,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"3 John Doe john@example.com Elephants\n",
|
|
"4 Jane Doe jane@example.com Giraffes\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"# Alle Zookeeper ausgeben\n",
|
|
"with Session(engine) as session:\n",
|
|
" for zk in session.query(Zookeeper).order_by(Zookeeper.id).all():\n",
|
|
" print(zk.id, zk.name, zk.email, zk.specialty)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 35,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# 3 Elefanten anlegen, die John betreut\n",
|
|
"with Session(engine) as session:\n",
|
|
" john = session.query(Zookeeper).filter_by(name='John Doe').one()\n",
|
|
" elephants = [\n",
|
|
" Animal(name='Babar', species='Elephant', zookeeper=john),\n",
|
|
" Animal(name='Dumbo', species='Elephant', zookeeper=john),\n",
|
|
" Animal(name='Hathi', species='Elephant', zookeeper=john),\n",
|
|
" ]\n",
|
|
" session.add_all(elephants)\n",
|
|
" session.commit()\n",
|
|
"\n",
|
|
"# 2 Giraffen anlegen, die Jane betreut\n",
|
|
"with Session(engine) as session:\n",
|
|
" jane = session.query(Zookeeper).filter_by(name='Jane Doe').one()\n",
|
|
" giraffes = [\n",
|
|
" Animal(name='Melman', species='Giraffe', zookeeper=jane),\n",
|
|
" Animal(name='Gloria', species='Giraffe', zookeeper=jane),\n",
|
|
" ]\n",
|
|
" session.add_all(giraffes)\n",
|
|
" session.commit()\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 36,
|
|
"metadata": {},
|
|
"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"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"# Liste aller Tiere mit ihren Pflegern ausgeben:\n",
|
|
"with Session(engine) as session:\n",
|
|
" animals = session.query(Animal).order_by(Animal.id).all()\n",
|
|
" print('-' * 40)\n",
|
|
" for animal in animals:\n",
|
|
" print(f\"| {animal.name:<10} | {animal.species:<10} | {animal.zookeeper.name:<10} |\")\n",
|
|
" print('-' * 40)\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"
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "code (3.13.2)",
|
|
"language": "python",
|
|
"name": "python3"
|
|
},
|
|
"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.3"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 2
|
|
}
|