Python Schulung – Kursbezug
Merksatz: Session = Arbeitseinheit. Erst commit() macht Änderungen dauerhaft.
Worum geht’s?
- Engine + Connection-String (SQLite/PostgreSQL).
- ORM-Modelle (Declarative Mapping) + Tabellen anlegen.
- Session-Management: add/commit/rollback.
- CRUD & Queries (filter, order_by, joins/relationships).
Lehr-/Lernziele
- (LZ1) Engine/Session korrekt initialisieren und Verbindungsstrings unterscheiden (SQLite vs. PostgreSQL).
- (LZ2) ORM-Modelle definieren, Tabellen erzeugen und Datensätze anlegen.
- (LZ3) CRUD sicher durchführen (Create/Read/Update/Delete) inkl.
commit()/rollback(). - (LZ4) sinnvolle Queries formulieren (Filter, Sortierung, Relationship-Join).
SemaTrain-Beispiel: Wir speichern „Kurse“ und „Teilnehmer“ (Beispieldaten) in SQLite. Später kann das Schema leicht erweitert werden (Trainer, Termine, Standorte, Buchungen).
Setup: Engine + Session (SQLite zuerst)
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
# SQLite (Datei) – perfekt für Lernpfad/Prototyp
engine = create_engine("sqlite:///daten/sematrain.db", echo=False)
Session = sessionmaker(bind=engine)
with Session() as session:
# hier würden wir arbeiten (Queries/CRUD)
print("Session bereit:", session is not None)
# PostgreSQL (Beispiel):
# engine = create_engine("postgresql+psycopg2://user:pass@localhost:5432/sematrain", echo=False)ORM-Modelle: Kurs & Teilnehmer
from sqlalchemy import ForeignKey, String, Integer, Float
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
class Base(DeclarativeBase):
pass
class Kurs(Base):
__tablename__ = "kurs"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
kursname: Mapped[str] = mapped_column(String(120), nullable=False)
format: Mapped[str] = mapped_column(String(30), nullable=False) # "Online" / "Praesenz"
standort: Mapped[str] = mapped_column(String(60), nullable=False) # z.B. "Hamburg"
dauer_tage: Mapped[int] = mapped_column(Integer, nullable=False)
preis_euro: Mapped[float] = mapped_column(Float, nullable=False)
teilnehmer: Mapped[list["Teilnehmer"]] = relationship(
back_populates="kurs",
cascade="all, delete-orphan"
)
class Teilnehmer(Base):
__tablename__ = "teilnehmer"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
name: Mapped[str] = mapped_column(String(120), nullable=False)
firma: Mapped[str] = mapped_column(String(120), nullable=False)
kurs_id: Mapped[int] = mapped_column(ForeignKey("kurs.id"), nullable=False)
kurs: Mapped["Kurs"] = relationship(back_populates="teilnehmer")Tabellen erzeugen (create_all)
from pathlib import Path
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
# Stelle sicher, dass der Ordner existiert (SQLite-Datei)
Path("daten").mkdir(exist_ok=True)
engine = create_engine("sqlite:///daten/sematrain.db", echo=False)
# Importiere Base + Modelle (hier nur symbolisch)
# from modelle import Base, Kurs, Teilnehmer
# Base.metadata.create_all(engine)
print("Tabellen erzeugt (wenn Base + Modelle importiert sind).")CRUD: Anlegen, Lesen, Aktualisieren, Löschen
from sqlalchemy import create_engine, select
from sqlalchemy.orm import sessionmaker
# angenommen: Base, Kurs, Teilnehmer sind definiert
engine = create_engine("sqlite:///daten/sematrain.db", echo=False)
Session = sessionmaker(bind=engine)
with Session() as session:
# CREATE (LZ3)
kurs = Kurs(
kursname="Python Schulung",
format="Online",
standort="Berlin",
dauer_tage=3,
preis_euro=1390.00
)
kurs.teilnehmer.append(Teilnehmer(name="Max Beispiel", firma="Beispiel GmbH"))
kurs.teilnehmer.append(Teilnehmer(name="Ada Muster", firma="Muster AG"))
session.add(kurs)
session.commit() # wichtig!
with Session() as session:
# READ (LZ3)
stmt = select(Kurs).where(Kurs.standort == "Berlin").order_by(Kurs.preis_euro.desc())
kurse = session.execute(stmt).scalars().all()
for k in kurse:
print(k.kursname, k.format, k.preis_euro, "TN:", len(k.teilnehmer))
with Session() as session:
# UPDATE (LZ3)
kurs = session.execute(select(Kurs).where(Kurs.standort == "Berlin")).scalars().first()
if kurs:
kurs.preis_euro = 1290.00
session.commit()
with Session() as session:
# DELETE (LZ3)
kurs = session.execute(select(Kurs).where(Kurs.standort == "Berlin")).scalars().first()
if kurs:
session.delete(kurs)
session.commit()Robust: rollback() bei Fehlern
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
engine = create_engine("sqlite:///daten/sematrain.db", echo=False)
Session = sessionmaker(bind=engine)
try:
with Session() as session:
kurs = Kurs(
kursname="Python Schulung",
format="Online",
standort="Hamburg",
dauer_tage=3,
preis_euro=1490.00
)
session.add(kurs)
session.commit()
except Exception as fehler:
# Falls du Session außerhalb des with nutzt: session.rollback()
print("Fehler – Transaktion abgebrochen:", fehler)Queries: Filtern, Sortieren, Join über Relationship
from sqlalchemy import create_engine, select, func
from sqlalchemy.orm import sessionmaker
engine = create_engine("sqlite:///daten/sematrain.db", echo=False)
Session = sessionmaker(bind=engine)
with Session() as session:
# Alle Online-Kurse, sortiert nach Preis
stmt = select(Kurs).where(Kurs.format == "Online").order_by(Kurs.preis_euro.asc())
for k in session.execute(stmt).scalars():
print(k.standort, k.preis_euro)
with Session() as session:
# Aggregation: Durchschnittspreis pro Format
stmt = (
select(Kurs.format, func.avg(Kurs.preis_euro))
.group_by(Kurs.format)
)
for format_, avg_preis in session.execute(stmt).all():
print(format_, round(avg_preis, 2))
with Session() as session:
# Join via Relationship: Teilnehmer + Kursname
stmt = (
select(Teilnehmer.name, Kurs.kursname, Kurs.standort)
.join(Teilnehmer.kurs)
.order_by(Kurs.standort.asc(), Teilnehmer.name.asc())
)
for name, kursname, standort in session.execute(stmt).all():
print(name, "->", kursname, standort)Praxisaufgabe (Mini)
Sie bauen eine kleine Kurs-DB (SQLite) und werten sie aus.
Beitrag zu den Lehr-/Lernzielen: LZ1 (Engine/Session), LZ2 (Modelle), LZ3 (CRUD), LZ4 (Queries).
- (LZ2) Definieren Sie Modelle
KursundTeilnehmer(Relationship 1:n). - (LZ2) Legen Sie die Tabellen an (
Base.metadata.create_all). - (LZ3) Fügen Sie 2 Kurse + je 2 Teilnehmer ein und committen Sie.
- (LZ4) Query: Alle Online-Kurse sortiert nach Preis; zusätzlich Ausgabe „Standort + Teilnehmeranzahl“.
- (Bonus) Aggregation: Durchschnittspreis pro Format (Online/Praesenz).
Lösungsvorschlag anzeigen
from pathlib import Path
from sqlalchemy import create_engine, select, Integer, String, Float, ForeignKey, func
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship, sessionmaker
Path("daten").mkdir(exist_ok=True)
class Base(DeclarativeBase):
pass
class Kurs(Base):
__tablename__ = "kurs"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
kursname: Mapped[str] = mapped_column(String(120), nullable=False)
format: Mapped[str] = mapped_column(String(30), nullable=False)
standort: Mapped[str] = mapped_column(String(60), nullable=False)
dauer_tage: Mapped[int] = mapped_column(Integer, nullable=False)
preis_euro: Mapped[float] = mapped_column(Float, nullable=False)
teilnehmer: Mapped[list["Teilnehmer"]] = relationship(back_populates="kurs", cascade="all, delete-orphan")
class Teilnehmer(Base):
__tablename__ = "teilnehmer"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
name: Mapped[str] = mapped_column(String(120), nullable=False)
firma: Mapped[str] = mapped_column(String(120), nullable=False)
kurs_id: Mapped[int] = mapped_column(ForeignKey("kurs.id"), nullable=False)
kurs: Mapped["Kurs"] = relationship(back_populates="teilnehmer")
engine = create_engine("sqlite:///daten/sematrain.db", echo=False)
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
with Session() as session:
kurs1 = Kurs(kursname="Python Schulung", format="Online", standort="Berlin", dauer_tage=3, preis_euro=1390.00)
kurs1.teilnehmer += [
Teilnehmer(name="Max Beispiel", firma="Beispiel GmbH"),
Teilnehmer(name="Ada Muster", firma="Muster AG")
]
kurs2 = Kurs(kursname="Python Schulung", format="Praesenz", standort="Hamburg", dauer_tage=3, preis_euro=1490.00)
kurs2.teilnehmer += [
Teilnehmer(name="Lena Demo", firma="Demo KG"),
Teilnehmer(name="Tim Test", firma="Test GmbH")
]
session.add_all([kurs1, kurs2])
session.commit()
with Session() as session:
print("Online-Kurse (Preis aufsteigend):")
stmt = select(Kurs).where(Kurs.format == "Online").order_by(Kurs.preis_euro.asc())
for k in session.execute(stmt).scalars():
print(k.standort, k.preis_euro, "TN:", len(k.teilnehmer))
with Session() as session:
print("Ø Preis pro Format:")
stmt = select(Kurs.format, func.avg(Kurs.preis_euro)).group_by(Kurs.format)
for format_, avg_preis in session.execute(stmt).all():
print(format_, round(avg_preis, 2))Kurz-Takeaways
- LZ1: Engine + Session sauber trennen. Connection-String bestimmt DB.
- LZ2: Modelle sind „Single Source of Truth“ für Tabellenstruktur (plus Migrationen in Projekten).
- LZ3: Ohne
commit()keine dauerhafte Änderung; bei Fehlern: Rollback-Prinzip. - LZ4: Queries mit
select()+where()+order_by()+ Joins/Relationships.