Daten aus der Datenbank laden#

In diesem Tutorial lernst du, wie du ein Datenmodell erstellst und mit einer Datenbank synchronisierst. Das kannst du für viele Zwecke nutzen – z. B. für Quizfragen und Antworten, Highscores, das Laden von Spielständen und mehr.

Schritt 1: Modell erstellen#

Beginne damit, ein Modell für deine Daten in einer neuen Datei zu erstellen – so, wie du es auch direkt in Python speichern würdest.

Ein Highscore ließe sich z. B. als einfache Liste mit Namen und Punktzahlen speichern:

[(Andreas, 100), (Martin, 200), (Julius, 50)]

Die Reihenfolge ist noch nicht wichtig – du kannst die Liste später sortieren.

Deine Klasse könnte anfangs so aussehen:

Datei: highscore_model.py

import sqlite3

class Highscore:
    def __init__(self, scores=[]):
        self.scores: list = scores

Die Klasse besitzt ein Attribut scores, das entweder übergeben wird oder als leere Liste startet.

Einrichten der Datenbank#

Nutze das Tool DB Browser for SQLite, um deine Datenbank einzurichten.

Lege eine Tabelle mit drei Feldern an: Name, Score und ID.

Füge Beispieldaten in die Datenbank ein, damit du später das Einlesen testen kannst.

Aus der Datenbank lesen#

Als Nächstes implementierst du eine Methode, um die Daten zu laden.

Füge deiner Klasse diese Methode hinzu:

@classmethod
def from_db(cls):
    connection = sqlite3.connect("highscore_db.db")
    cursor = connection.cursor()
    sql = 'SELECT Name, Score FROM Highscore'
    cursor.execute(sql)
    rows = cursor.fetchall()
    connection.close()
    return cls(rows)

Dies ist eine classmethod, die wie eine Fabrik verwendet wird, um ein Highscore-Objekt aus der Datenbank zu erzeugen.

Es wird eine Verbindung zur Datenbank geöffnet, eine SQL-Abfrage mit einem Cursor ausgeführt und das Ergebnis abgefragt und zurückgegeben.

Zum Testen:

hs = Highscore.from_db()
print(hs.scores)

In die Datenbank schreiben#

Füge folgende Methode hinzu, um in die Datenbank zu schreiben:

def create(self, name, score):
    connection = sqlite3.connect("highscore_db.db")
    cursor = connection.cursor()
    sql = f"INSERT INTO Highscore (Name, Score) VALUES ('{name}', '{score}')"
    cursor.execute(sql)
    connection.commit()
    connection.close()
    self.scores.append((name, score))

Das erzeugt einen neuen Datensatz – sowohl in der Datenbank als auch lokal in deiner Datenstruktur.

CRUD-Operationen#

CRUD steht für:

  • Create – Einen Datensatz erstellen

  • Read – Datensätze lesen

  • Update – Bestehende Datensätze aktualisieren

  • Delete – Datensätze löschen

Diese vier Vorgänge solltest du beim Umgang mit Datenbanken im Blick behalten.

Erstellen#

Implementiert in der Methode create.

Lesen#

Wird durch from_db umgesetzt.

Aktualisieren#

So aktualisierst du einen vorhandenen Eintrag:

def update(self, score_id, name, new_score):
    connection = sqlite3.connect("highscore_db.db")
    cursor = connection.cursor()
    sql = f"UPDATE Highscore SET Name = '{name}', Score = '{new_score}' WHERE ID='{score_id}'"
    cursor.execute(sql)
    connection.commit()
    connection.close()
    for score in self.scores:
        if score[0] == score_id:
            self.scores.remove(score)
            self.scores.append((score_id, name, new_score))

Löschen#

So löschst du einen Eintrag:

def delete(self, score_id):
    connection = sqlite3.connect("highscore_db.db")
    cursor = connection.cursor()
    sql = f"DELETE FROM Highscore WHERE ID='{score_id}'"
    cursor.execute(sql)
    connection.commit()
    connection.close()
    for score in self.scores:
        if score[0] == score_id:
            self.scores.remove(score)

Vollständiger Code#

import sqlite3

class Highscore:
    def __init__(self, scores=[]):
        self.scores: list = scores

    @classmethod
    def from_db(cls):
        connection = sqlite3.connect("highscore_db.db")
        cursor = connection.cursor()
        sql = 'SELECT ID, Name, Score FROM Highscore'
        cursor.execute(sql)
        rows = cursor.fetchall()
        connection.close()
        return cls(rows)

    def create(self, name, score):
        connection = sqlite3.connect("highscore_db.db")
        cursor = connection.cursor()
        sql = f"INSERT INTO Highscore (Name, Score) VALUES ('{name}', '{score}')"
        cursor.execute(sql)
        connection.commit()
        connection.close()
        self.scores.append((name, score))

    def update(self, score_id, name, new_score):
        connection = sqlite3.connect("highscore_db.db")
        cursor = connection.cursor()
        sql = f"UPDATE Highscore SET Name = '{name}', Score = '{new_score}' WHERE ID='{score_id}'"
        cursor.execute(sql)
        connection.commit()
        connection.close()
        for score in self.scores:
            if score[0] == score_id:
                self.scores.remove(score)
                self.scores.append((score_id, name, new_score))

    def delete(self, score_id):
        connection = sqlite3.connect("highscore_db.db")
        cursor = connection.cursor()
        sql = f"DELETE FROM Highscore WHERE ID='{score_id}'"
        cursor.execute(sql)
        connection.commit()
        connection.close()
        for score in self.scores:
            if score[0] == score_id:
                self.scores.remove(score)

hs = Highscore.from_db()
# hs.create("Max Meier", 200)
hs.update(2, "Max Meier2", 200)
hs.delete(2)
print(hs.scores)

Das Modell in dein Spiel integrieren#

Sobald dein Modell steht, kannst du es in dein Hauptprogramm einbinden.

Hier ist ein Beispielspiel, in dem der Spieler fallenden Kugeln ausweicht. Bei einem Treffer ist das Spiel vorbei. Anfangs sieht es so aus:

from miniworlds import *
import random
import highscore_model
import easygui

world = World(200, 600)
enemies = []
player = Circle(100, 500)
player.radius = 20
my_score = 0
score_actor = Number(10, 10)

@player.register
def on_key_pressed_a(self):
    self.move_left()

@player.register
def on_key_pressed_d(self):
    self.move_right()

@player.register
def on_detecting_right_border(self):
    self.move_back()

@player.register
def on_detecting_left_border(self):
    self.move_back()

def create_enemy():
    enemy = Circle(random.randint(20, 180), 50)
    enemy.radius = random.randint(10, 30)
    enemies.append(enemy)

@world.register
def act(self):
    global my_score
    if self.frame % 100 == 0:
        create_enemy()
    for enemy in enemies:
        enemy.move_down()
        if "bottom" in enemy.detect_borders():
            enemies.remove(enemy)
            enemy.remove()
            my_score += 1
            score_actor.set_number(my_score)

So ergänzt du einen Game-Over-Screen mit Highscore-Anzeige:

@world.register
def act(self):
    global my_score
    if self.frame % 100 == 0:
        create_enemy()
    for enemy in enemies:
        enemy.move_down()
        if "bottom" in enemy.detect_borders():
            enemies.remove(enemy)
            enemy.remove()
            my_score += 1
            score_actor.set_number(my_score)
        if enemy in player.detect_actors():
            world.reset()
            name = easygui.enterbox(f"You reached {my_score} points! Enter your name", "Highscore")
            new_highscore(name, my_score)

new_highscore ist wie folgt definiert:

def new_highscore(name, points):
    highscore = highscore_model.Highscore.from_db()
    highscore.create(name, points)
    scores = highscore.from_db().scores
    scores.sort()
    for index, ranking in enumerate(scores[0:10]):
        t = Text((20, index * 40))
        t.text = f"{ranking[0]} - Points: {ranking[1]}"
        t.font_size = 10

Das lädt, aktualisiert und zeigt die zehn besten Punktzahlen an.