Programmering nivå 2

Kap 4.6 – Miniuppgift: Använda SQLite-databaser

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

Mål med lektionen

När du har arbetat klart med denna lektion ska du kunna:

  • Skapa och använda en SQLite-databas i Python med biblioteket sqlite3.
  • Skapa tabeller, lägga till poster, söka efter data och läsa ut all data från databasen.
  • Strukturera din kod i moduler med funktioner för bättre överblick.
  • Använda try/except-block för att hantera vanliga fel vid databasanslutning.

Så här lär du dig bäst

Arbeta med konkreta exempel: börja med att skapa databasen och tabellen. Skriv sedan funktioner för att lägga till data, visa alla poster och söka i databasen. Dela upp din kod i minst två moduler - en för gränssnittet och en för databasanropen. Testa dina funktioner separat och se till att varje steg fungerar innan du går vidare.

Centrala begrepp

  • SQLite - En lätt, filbaserad databas som inte kräver någon server.
  • SQL - Structured Query Language, språket som används för att kommunicera med databasen.
  • Moduler - Att dela upp din kod i olika .py-filer med specifika funktioner.
  • Felhantering - Att förutse och hantera fel vid till exempel anslutning eller SQL-kommandon.

Struktur för ditt program

Skapa två filer i samma mapp:

  • databas.py - innehåller all kod som pratar med databasen.
  • main.py - innehåller menyn och användarens val.

Så fungerar programmet steg för steg

  1. main.py startar programmet och anropar databas.skapa_databas().
  2. databas.py öppnar eller skapar filen bibliotek.db.
  3. Tabellen bocker skapas om den inte redan finns.
  4. Användaren väljer ett alternativ i menyn.
  5. Vid val 1 skickas titel och författare till lagg_till_bok().
  6. Vid val 2 skickas sökordet till sok_bok().
  7. Vid val 3 hämtas alla rader med visa_alla_bocker().
  8. Databasfunktionerna använder try/except för att fånga SQLite-fel och finally för att stänga anslutningen.

Det är viktigt att databas.py inte bara innehåller SQL-frågorna, utan också kontrollerar vanliga fel. I den förbättrade versionen stoppas tom titel, tom författare och tom sökning innan SQL-frågan körs. Då blir programmet tydligare för användaren och lättare att felsöka.

Databasmodul: databas.py

import sqlite3

DB_FIL = "bibliotek.db"

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()

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()

def sok_bok(nyckelord):
    nyckelord = nyckelord.strip()

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

    conn = None
    try:
        conn = sqlite3.connect(DB_FIL)
        cursor = conn.cursor()
        cursor.execute(
            "SELECT * FROM bocker WHERE titel LIKE ? OR forfattare LIKE ?",
            ("%" + nyckelord + "%", "%" + nyckelord + "%")
        )
        resultat = cursor.fetchall()

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

        if not resultat:
            print("Inga träffar.")
    except sqlite3.Error as e:
        print("Sökning misslyckades:", e)
    finally:
        if conn:
            conn.close()

def visa_alla_bocker():
    conn = None
    try:
        conn = sqlite3.connect(DB_FIL)
        cursor = conn.cursor()
        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.")
    except sqlite3.Error as e:
        print("Kunde inte hämta böcker:", e)
    finally:
        if conn:
            conn.close()

Huvudfil: main.py

import databas

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")

def main():
    databas.skapa_databas()

    while True:
        visa_meny()
        val = input("Välj alternativ: ")

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

if __name__ == "__main__":
    main()

Förklaring av förbättringarna

Varför kontrolleras tom titel och författare?

Tabellen kräver att både titel och forfattare har värden. Därför stoppas tom inmatning redan i lagg_till_bok():

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. Det gör att en användare inte kan skriva bara blanksteg och ändå få en tom bok sparad.

Varför stoppas tom sökning?

SQL-frågan använder LIKE med procenttecken runt sökordet. Om sökordet är tomt blir mönstret %%, vilket matchar allt. Därför är det tydligare att stoppa tom sökning och be användaren skriva ett sökord.

Varför sqlite3.Error?

except Exception fångar nästan alla fel, även fel som kanske inte har med databasen att göra. except sqlite3.Error är mer specifikt och säger tydligare: här hanterar vi fel från SQLite eller sqlite3.

Varför finally?

finally körs både när koden lyckas och när ett fel uppstår. Därför passar det för conn.close(). Databasanslutningen stängs även om något går fel efter att anslutningen har öppnats.

Varför delas koden i två filer?

main.py ansvarar för användarens val. databas.py ansvarar för databasen. Det gör programmet lättare att läsa, testa och bygga vidare på. Om du senare vill skapa ett grafiskt gränssnitt kan du återanvända databas.py utan att skriva om SQL-koden.

Interaktiv övning: hela bibliotekssystemet

Testa hur main.py och databas.py samarbetar. Övningen simulerar bibliotek.db och tabellen bocker, men visar samma SQL-steg som programmet använder.

Programmet är inte startat i simuleringen ännu.

Programmering 2-koppling: detta visar hur menyval i main.py leder till funktionsanrop i databas.py, som i sin tur kör SQL mot databasen.

Att testa

  • Kör main.py och kontrollera att filen bibliotek.db skapas.
  • Lägg till flera böcker med olika titlar och författare.
  • Sök på både titel och författare för att se att LIKE-frågan fungerar.
  • Stäng programmet, starta det igen och kontrollera att böckerna finns kvar.

Du kan även öppna bibliotek.db i VS Code med tillägget SQLite Viewer av publisher Florian Klampfer för att se tabellen och raderna visuellt.

Sammanfattning

  • Du har skapat en komplett programstruktur för databashantering med SQLite.
  • Du har använt funktioner och moduler för att organisera koden.
  • Du har arbetat med SQL-kommandon för att söka, spara och läsa data.
  • Du är nu redo att ta dig an Lärarbedömd uppgift 4 med god förberedelse.

Tillbaka till Kapitel 4