Add files for online 2a
This commit is contained in:
parent
3699c26d29
commit
df92a050dc
240
code/online-part-2a/FS25 Seminarplatz Vergabe.ipynb
Normal file
240
code/online-part-2a/FS25 Seminarplatz Vergabe.ipynb
Normal file
@ -0,0 +1,240 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Seminarplatz-Vergabe\n",
|
||||
"\n",
|
||||
"In folgendem Problem geht es darum, dass eine Gruppe von Studierenden einen oder mehrere Seminarplätze in einem Kurs bekommen sollen. Es 8 Seminargruppen mit insgesamt 40 Plätzen, diese sind in der Tabelle `seminar_sessions` abgelegt.\n",
|
||||
"\n",
|
||||
"Bei einer Anmeldung einer einzelnen Person wird die Anzahl der Plätze in der Tabelle `seminar_sessions` um 1 reduziert und ein Eintrag in der Tabelle `seminar_registrations` erstellt. Dies darf natürlich nur passieren, wenn noch Plätze verfügbar sind.\n",
|
||||
"\n",
|
||||
"Die Anzahl der vergebenen Plätze in der Tabelle `seminar_registrations` und die Anzahl der verfügbaren Plätze in der Tabelle `seminar_sessions` müssen konsistent sein, also in der Summe 40 ergeben.\n",
|
||||
"\n",
|
||||
"In einem Lasttest mit mehreren Threads zeigt sich: Die Anzahl der vergebenen Plätze in der Tabelle `seminar_registrations` und die Anzahl der verfügbaren Plätze in der Tabelle `seminar_sessions` sind nicht konsistent! \n",
|
||||
"\n",
|
||||
"### Ihre Aufgabe:\n",
|
||||
"Führen Sie geeignete Transaktionen ein, um die Konsistenz zu gewährleisten!\n",
|
||||
"\n",
|
||||
"Implementierungen Sie dazu zunächst die Funktionen, die mit TODO gekennzeichnet sind, und passen Sie die Funktion `reserve_seat` an, um die Konsistenz zu gewährleisten.\n",
|
||||
"\n",
|
||||
"Am Code des Lasttests müssen Sie nichts ändern, er dient nur dazu, das Problem zu demonstrieren.\n",
|
||||
"\n",
|
||||
"### Bemerkungen:\n",
|
||||
"\n",
|
||||
"- nicht jeder Durchlauf verursacht Inkonsistenzen, manchmal müssen Sie den Code mehrmals ausführen, um Inkonsistenzen zu finden\n",
|
||||
"- auch Deadlocks können auftreten, diese müssen auch vermieden werden\n",
|
||||
"- die User-Tabelle wurde weggelassen, da sie für das Problem nicht relevant ist\n",
|
||||
"- an manchen Stellen ist der Code absichtlich etwas ineffizient gehalten, um die Inkonsistenzen zu begünstigen (z.B. würden Probleme mit Inkonsistenzen seltener auftreten, wenn die Query `UPDATE seminar_sessions SET seats = seats-1` lauten würde).\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import psycopg2\n",
|
||||
"import psycopg2.extras"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Datenbankverbindung herstellen\n",
|
||||
"def get_connection():\n",
|
||||
" # TODO Datenbankverbindung herstellen - geben Sie hier Ihre Zugangsdaten ein\n",
|
||||
" conn = psycopg2.connect(\"dbname=... user=... password=...\")\n",
|
||||
" \n",
|
||||
" # TODO später autocommit ausschalten\n",
|
||||
" conn.autocommit = True\n",
|
||||
"\n",
|
||||
" return conn"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Tabelle erzeugen - immer wenn diese Zelle ausgeführt wird, wird die Tabelle neu erzeugt\n",
|
||||
"conn = get_connection()\n",
|
||||
"cursor = conn.cursor()\n",
|
||||
"cursor.execute(\"\"\"\n",
|
||||
" DROP TABLE IF EXISTS seminar_registrations CASCADE;\n",
|
||||
" DROP TABLE IF EXISTS seminar_sessions;\n",
|
||||
" \n",
|
||||
" CREATE TABLE IF NOT EXISTS seminar_sessions (\n",
|
||||
" id SERIAL PRIMARY KEY, \n",
|
||||
" seats INTEGER NOT NULL\n",
|
||||
" );\n",
|
||||
" \n",
|
||||
" INSERT INTO seminar_sessions (seats) VALUES \n",
|
||||
" (4), (4), (5), (5), (4), (10), (5), (3);\n",
|
||||
" \n",
|
||||
" \n",
|
||||
" CREATE TABLE IF NOT EXISTS seminar_registrations (\n",
|
||||
" user_id INTEGER NOT NULL,\n",
|
||||
" session_id INTEGER NOT NULL,\n",
|
||||
" FOREIGN KEY (session_id) REFERENCES seminar_sessions(id)\n",
|
||||
" ); \n",
|
||||
"\"\"\")\n",
|
||||
"conn.commit()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Hilfsfunktion: Alle Seminar-Sessions ausgeben\n",
|
||||
"def show_sessions():\n",
|
||||
" # TODO alle Seminar-Sessions auf der Console ausgeben\n",
|
||||
" pass\n",
|
||||
"\n",
|
||||
"# Anzahl aller freien Plätze bestimmen\n",
|
||||
"def count_free_seats():\n",
|
||||
" # TODO Anzahl freier Plätze bestimmen (Summe über die Spalte \"seats\")\n",
|
||||
" # und mit return zurückgeben\n",
|
||||
" pass\n",
|
||||
"\n",
|
||||
"# Anzahl der belegten Plätze bestimmen\n",
|
||||
"def count_occupied_seats():\n",
|
||||
" # TODO Anzahl belegter Plätze bestimmen (Anzahl der Einträge in der Tabelle seminar_registrations)\n",
|
||||
" # und mit return zurückgeben\n",
|
||||
" pass\n",
|
||||
"\n",
|
||||
"# Testaufrufe\n",
|
||||
"\n",
|
||||
"# show_sessions()\n",
|
||||
"# print(\"freie Plätze\", count_free_seats())\n",
|
||||
"# print(\"belegte Plätze\", count_occupied_seats())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Diese Funktion soll einen Platz reservieren und True zurückgeben, wenn es geklappt hat.\n",
|
||||
"# Wenn kein Platz mehr frei ist, soll False zurückgegeben werden.\n",
|
||||
"\n",
|
||||
"# TODO: Führen Sie geeignete Transaktionssteuerung ein, um sicherzustellen, dass die Funktion korrekt arbeitet.\n",
|
||||
"\n",
|
||||
"def reserve_seat(conn, user_id, session_id):\n",
|
||||
" with conn.cursor() as cursor:\n",
|
||||
" cursor.execute(\"SELECT seats FROM seminar_sessions WHERE id = %s\", (session_id,))\n",
|
||||
" seats = cursor.fetchone()[0]\n",
|
||||
"\n",
|
||||
" if seats > 0:\n",
|
||||
" cursor.execute(\"UPDATE seminar_sessions SET seats = %s WHERE id = %s\", (seats-1, session_id,))\n",
|
||||
" cursor.execute(\"INSERT INTO seminar_registrations (user_id, session_id) VALUES (%s, %s)\", (user_id, session_id))\n",
|
||||
" return True\n",
|
||||
" else:\n",
|
||||
" return False\n",
|
||||
" \n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import random \n",
|
||||
"\n",
|
||||
"# 20 zufällige Anmeldungen durchführen - diese Funktion wird später als Thread gestartet\n",
|
||||
"def random_seat_reservation(n=20):\n",
|
||||
" # jeder Thread hat seine eigene Verbindung\n",
|
||||
" conn = get_connection()\n",
|
||||
" \n",
|
||||
" # n mal einen zufälligen Platz für eine zufällige User-ID reservieren\n",
|
||||
" for _ in range(n):\n",
|
||||
" random_user_id = random.randint(1, 100)\n",
|
||||
" random_session_id = random.randint(1, 8)\n",
|
||||
"\n",
|
||||
" if reserve_seat(conn, random_user_id, random_session_id):\n",
|
||||
" print(\"User\", random_user_id, \"hat sich für Session\", random_session_id, \"angemeldet\")\n",
|
||||
" else:\n",
|
||||
" print(\"Session\", random_session_id, \"ist ausgebucht\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import threading\n",
|
||||
"\n",
|
||||
"# Diese Funktion gibt Informationen über die Belegung der Seminare aus\n",
|
||||
"def print_info():\n",
|
||||
" print('-'*20)\n",
|
||||
" \n",
|
||||
" free_seats = count_free_seats()\n",
|
||||
" occupied_seats = count_occupied_seats()\n",
|
||||
"\n",
|
||||
" print(\"Freie Plätze:\", free_seats)\n",
|
||||
" print(\"Belegte Plätze:\", occupied_seats)\n",
|
||||
" print(\"Gesamt:\", free_seats + occupied_seats)\n",
|
||||
" \n",
|
||||
" if free_seats + occupied_seats == 40:\n",
|
||||
" print(\"Platzanzahl ist konsistent!\")\n",
|
||||
" else:\n",
|
||||
" print(\"Platzanzahl ist inkonsistent!\")\n",
|
||||
"\n",
|
||||
" print('-'*20)\n",
|
||||
"\n",
|
||||
"# -------------------------------------------------------------------------\n",
|
||||
"# Mini-Lasttest, 5 Threads starten, die jeweils 20 Reservierungen vornehmen\n",
|
||||
"# -------------------------------------------------------------------------\n",
|
||||
"\n",
|
||||
"print(\"Vor dem Lasttest:\")\n",
|
||||
"print_info()\n",
|
||||
"\n",
|
||||
"threads = [ threading.Thread(target=random_seat_reservation) for _ in range(5)]\n",
|
||||
"\n",
|
||||
"# Threads starten\n",
|
||||
"for t in threads:\n",
|
||||
" t.start()\n",
|
||||
"\n",
|
||||
"# Auf das Ende der Threads warten\n",
|
||||
"for t in threads:\n",
|
||||
" t.join()\n",
|
||||
"\n",
|
||||
" \n",
|
||||
"print(\"Nach dem Lasttest:\")\n",
|
||||
"print_info()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "base",
|
||||
"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.11.7"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
283
code/online-part-2a/Zoo Task CRUD Operations.ipynb
Normal file
283
code/online-part-2a/Zoo Task CRUD Operations.ipynb
Normal file
@ -0,0 +1,283 @@
|
||||
{
|
||||
"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": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import psycopg2\n",
|
||||
"import psycopg2.extras"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Verbindung aufbauen\n",
|
||||
"# TODO: hier müssen Ihre Verbindungsdaten eingetragen werden\n",
|
||||
"# TODO: ggf. müssen Sie auch den Namen der Datenbank anpassen oder eine leere Datenbank 'zoo' anlegen\n",
|
||||
"conn = psycopg2.connect(\"dbname=zoo user=... password=...\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"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": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# TODO: Implementieren Sie hier die CRUD-Operationen für Zookeeper\n",
|
||||
"\n",
|
||||
"# TODO: Tierpleger anlegen, liefert die ID des neuen Tierpflegers zurück\n",
|
||||
"def create_zookeeper(name, email, specialty):\n",
|
||||
" pass\n",
|
||||
"\n",
|
||||
"# TODO: Tierpfleger nach ID lesen, Tupel zurückgeben\n",
|
||||
"def read_zookeeper(id):\n",
|
||||
" pass\n",
|
||||
"\n",
|
||||
"# TODO: Alle Tierpfleger lesen, liefert eine Liste von Tupeln zurück (nach ID sortiert)\n",
|
||||
"def read_all_zookeepers():\n",
|
||||
" pass\n",
|
||||
"\n",
|
||||
"# TODO: Tierpfleger aktualisieren\n",
|
||||
"def update_zookeeper(id, name, email, specialty):\n",
|
||||
" pass\n",
|
||||
"\n",
|
||||
"# TODO: Tierpfleger per ID löschen\n",
|
||||
"def delete_zookeeper(id):\n",
|
||||
" pass"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"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": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"## Nutzung von SQLAlchemy\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",
|
||||
"# TODO: Verbingung zur Datenbank herstellen\n",
|
||||
"engine = create_engine('postgresql://IHR_POSTGRES_USER:IHR_POSTGRES_PASSWORD/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": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# TODO: zwei Zookeeper anlegen\n",
|
||||
"with Session(engine) as session:\n",
|
||||
" john = ...\n",
|
||||
" jane = ...\n",
|
||||
"\n",
|
||||
" session.add(...)\n",
|
||||
" \n",
|
||||
" session.commit()\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# TODO: Alle Zookeeper ausgeben\n",
|
||||
"with Session(engine) as session:\n",
|
||||
" ..."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# TODO: 3 Elefanten anlegen, die John betreut\n",
|
||||
"with Session(engine) as session:\n",
|
||||
" ...\n",
|
||||
" \n",
|
||||
"# TODO: 2 Giraffen anlegen, die Jane betreut\n",
|
||||
"with Session(engine) as session:\n",
|
||||
" ..."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Liste aller Tiere mit ihren Pflegern ausgeben:\n",
|
||||
"with Session(engine) as session:\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"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "base",
|
||||
"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.11.7"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user