Presentationer Slide 1/1

Kap 4.6

Miniuppgift: Använda SQLite-databaser

Bygg ett bibliotekssystem med databas, moduler, sökning och felhantering.

Mål

Efter miniuppgiften ska du kunna...

  • skapa en SQLite-databas och tabell
  • lägga till, söka och visa poster
  • dela upp programmet i moduler
  • validera användarens inmatning
  • hantera SQLite-fel med try/except/finally

Du kombinerar

Tidigare kapitel används tillsammans

  • Kap 3.7: egna moduler och import.
  • Kap 4.2: undantagshantering.
  • Kap 4.3: tydlig kodstruktur.
  • Kap 4.5: SQLite och SQL.

Filstruktur

Två Pythonfiler och en databasfil

bibliotekssystem/
├── main.py
├── databas.py
└── bibliotek.db

bibliotek.db skapas när programmet körs.

Ansvar

Varje fil har en uppgift

main.py

Visar menyn, läser indata och anropar funktioner.

databas.py

Öppnar databasen och kör SQL.

bibliotek.db

Sparar böckerna permanent.

Programflöde

Från menyval till databas

  1. main.py läser användarens val.
  2. En funktion i databas.py anropas.
  3. Funktionen ansluter till bibliotek.db.
  4. SQL körs mot tabellen bocker.
  5. Resultatet skrivs ut och anslutningen stängs.

Tabellen

bocker

id | titel                 | forfattare
1  | Bröderna Lejonhjärta | Astrid Lindgren
2  | Utvandrarna           | Vilhelm Moberg
  • id är unikt och skapas automatiskt.
  • titel måste ha ett värde.
  • forfattare måste ha ett värde.

databas.py

Import och databasnamn

import sqlite3

DB_FIL = "bibliotek.db"

sqlite3 ger tillgång till SQLite.

DB_FIL används av alla databasfunktioner.

Skapa databas

SQL-kommandot

CREATE TABLE IF NOT EXISTS bocker (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    titel TEXT NOT NULL,
    forfattare TEXT NOT NULL
)

Tabellen skapas bara om den inte redan finns.

Skapa databas

Hela funktionen

def skapa_databas():
    conn = None
    try:
        conn = sqlite3.connect(DB_FIL)
        cursor = conn.cursor()
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS bocker (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                titel TEXT NOT NULL,
                forfattare TEXT NOT NULL
            )
        """)
        conn.commit()
    except sqlite3.Error as e:
        print("Fel vid skapande av databas:", e)
    finally:
        if conn:
            conn.close()

conn = None

Varför skapas variabeln först?

conn = None

try:
    conn = sqlite3.connect(DB_FIL)
finally:
    if conn:
        conn.close()

Om anslutningen misslyckas finns variabeln ändå.

if conn stänger bara en anslutning som faktiskt öppnades.

Validering

Stoppa tom titel och författare

if not titel.strip() or not forfattare.strip():
    print("Titel och författare måste fyllas i.")
    return

strip() tar bort mellanslag i början och slutet.

Även text som bara består av blanksteg stoppas.

Lägg till bok

INSERT INTO

cursor.execute(
    "INSERT INTO bocker (titel, forfattare) VALUES (?, ?)",
    (titel.strip(), forfattare.strip())
)
conn.commit()

Värdena skickas separat från SQL-frågan.

commit() sparar boken i databasfilen.

Lägg till bok

Hela strukturen

def lagg_till_bok(titel, forfattare):
    if not titel.strip() or not forfattare.strip():
        print("Titel och författare måste fyllas i.")
        return

    conn = None
    try:
        conn = sqlite3.connect(DB_FIL)
        cursor = conn.cursor()
        cursor.execute(
            "INSERT INTO bocker (titel, forfattare) VALUES (?, ?)",
            (titel.strip(), forfattare.strip())
        )
        conn.commit()
        print("Bok tillagd!")
    except sqlite3.Error as e:
        print("Kunde inte lägga till bok:", e)
    finally:
        if conn:
            conn.close()

Sökning

Stoppa ett tomt sökord

nyckelord = nyckelord.strip()

if not nyckelord:
    print("Skriv ett sökord.")
    return

Ett tomt sökord skulle annars skapa mönstret %%.

%% matchar alla rader.

LIKE

Sök i titel eller författare

cursor.execute(
    "SELECT * FROM bocker "
    "WHERE titel LIKE ? OR forfattare LIKE ?",
    ("%" + nyckelord + "%", "%" + nyckelord + "%")
)

% betyder valfri text före eller efter sökordet.

Sökexempel

Vad matchar mönstret?

nyckelord = "Lindgren"
mönster = "%Lindgren%"
1. Bröderna Lejonhjärta - Astrid Lindgren

Samma fråga söker i både titel och forfattare.

Sökresultat

Visa träffar eller ett meddelande

resultat = cursor.fetchall()

for bok in resultat:
    print(f"{bok[0]}. {bok[1]} - {bok[2]}")

if not resultat:
    print("Inga träffar.")

Visa alla

Hämta hela tabellen

cursor.execute("SELECT * FROM bocker")
resultat = cursor.fetchall()

for bok in resultat:
    print(f"{bok[0]}. {bok[1]} - {bok[2]}")

if not resultat:
    print("Inga böcker finns än.")

Specifikt fel

Varför sqlite3.Error?

except sqlite3.Error as e:
    print("Sökning misslyckades:", e)

sqlite3.Error fångar fel från SQLite-biblioteket.

Exception skulle även fånga många fel som inte hör till databasen.

Det specifika undantaget gör avsikten tydligare.

finally

Anslutningen ska alltid stängas

finally:
    if conn:
        conn.close()

finally körs både när koden lyckas och när ett fel uppstår.

Databasanslutningen lämnas därför inte öppen.

main.py

Importera databasmodulen

import databas

def main():
    databas.skapa_databas()

Databasen och tabellen skapas innan menyn börjar användas.

Modulnamnet visar att funktionen kommer från databas.py.

Meny

Fyra alternativ

def visa_meny():
    print("\nMeny:")
    print("1. Lägg till bok")
    print("2. Sök bok")
    print("3. Visa alla böcker")
    print("4. Avsluta")

Menyval

Anropa rätt databasfunktion

if val == "1":
    titel = input("Titel: ")
    forfattare = input("Författare: ")
    databas.lagg_till_bok(titel, forfattare)
elif val == "2":
    nyckelord = input("Sökord: ")
    databas.sok_bok(nyckelord)
elif val == "3":
    databas.visa_alla_bocker()
elif val == "4":
    break
else:
    print("Ogiltigt val.")

Hela samarbetet

Vem gör vad?

main.py

Tar emot titel och författare.

databas.lagg_till_bok(...)

Validerar och kör INSERT INTO.

bibliotek.db

Sparar boken i tabellen bocker.

Återanvändning

Varför två filer?

  • main.py kan ändras utan att SQL-koden flyttas.
  • databas.py kan testas separat.
  • Ett framtida grafiskt gränssnitt kan återanvända databasmodulen.
  • Varje fil blir kortare och har ett tydligare ansvar.

Testfall

Testa mer än normalfallet

  1. Starta programmet och kontrollera att databasen skapas.
  2. Lägg till flera böcker.
  3. Försök lägga till en tom titel.
  4. Sök på en del av en titel.
  5. Sök på en författare.
  6. Testa ett sökord utan träffar och ett tomt sökord.

Permanent data

Testa flera körningar

  1. Lägg till böcker.
  2. Avsluta programmet.
  3. Starta programmet igen.
  4. Välj Visa alla böcker.

Böckerna ska finnas kvar i bibliotek.db.

VS Code

Inspektera databasen visuellt

Installera SQLite Viewer av Florian Klampfer.

Öppna sedan bibliotek.db i VS Code.

Du kan se tabellen bocker, kolumnerna och de sparade raderna.

Tillägget är valfritt; Pythonprogrammet fungerar utan det.

Sammanfattning

Miniuppgiften kombinerar...

  • SQLite-tabell och SQL-frågor
  • platshållare och LIKE-sökning
  • validering med strip()
  • sqlite3.Error och finally
  • moduler med separata ansvarsområden
  • meny, testfall och permanent lagring