Erstelle deinen eigenen Preisvergleich für Games in Python Teil 3

Nachdem wir im zweiten Teil der Reihe bereits eine Verbindung zur Datenbank hergestellt haben, werden wir uns in diesem, dritten Teil, dem Datenmodell und einem Test der Datenbank widmen.

Update (19.08.19, 21:52 Uhr): Nachdem ich die test.py in einer leeren Datenbank ausgeführt habe, fiel auf, dass die Plattform und der Shop zum Zeitpunkt des „commits“ gar nicht in der Datenbank stehen. Das sollte nun funktionieren.

Hier findest du die beiden ersten Teile der Reihe:

Als erstes solltest du vier Dateien im Ordner „base“ anlegen: game.py, price.py, platform.py, shop.py. Diese Dateien spiegeln jeweils eine der Tabellen als Klasse für unsere Datenbank wider.

Ansicht aller Python-Dateien in Visual Studio Code
Ansicht aller Python-Dateien in Visual Studio Code

Game-Tabelle

Die Datei game.py füllst du mit folgendem Code.

#!/bin/python3
from sqlalchemy import Column, Integer, String, Date, ForeignKey
from sqlalchemy.orm import relationship
from base import Base, Session


class Game(Base):
    __tablename__ = 'games'
 
    id = Column(Integer, primary_key=True)
    name = Column(String)
    url = Column(String)
    release_date = Column(Date)
    platform_name = Column(String, ForeignKey('platforms.name'))
    platform = relationship('Platform', backref='games')
    shop_name = Column(String, ForeignKey('shops.name'))
    shop = relationship('Shop', backref='games')
 
    def __init__(self, name, url, release_date, platform, shop):
        self.name = name
        self.url = url
        self.release_date = release_date
        self.platform = platform
        self.shop = shop
 
    @classmethod
    def get_set(cls, name, url, platform, shop):
        session = Session()
        exists = session.query(Game).filter(
            Game.name.ilike(name),
            url == url,
            platform == platform,
            shop == shop
        ).first()
        if exists:
            session.close()
            return exists
        session.close()
        return None

Diese Klasse stellt das Objekt Game dar, welches die Basis für die Tabelle games ist.

FeldnameDatentypBeschreibung
idInteger, ZahlPrimärschlüssel der Tabelle
nameString, TextBeinhaltet den Namen des Spiels
urlString, TextBeinhaltet den Link zur Website des Spiels
release_dateDate, DatumBeinhaltet das Veröffentlichungsdatum des Spiels
platform_nameString, TextBeinhaltet den Plattform-Namen.
platformRelationStellt die Beziehung zur platforms-Tabelle dar.
shop_nameString, TextBeinhaltet den Shop-Namen.
shopRelationStellt die Beziehung zur shops-Tabelle dar.

Die Funktion get_set hilft uns dabei, doppelte Einträge zu vermeiden, dafür vergleichen wir den Namen, die Url, die Plattform und den Shop. Es kann ja vorkommen, dass das Spiel im gleichen Shop noch für eine andere Plattform verfügbar ist. Dann würden wir dieses Spiel als weiteren Eintrag in der Datenbank aufnehmen. Die Funktion gibt die Instanz der Klasse Game (mit dem Inhalt des gesuchten Spiels) zurück, wenn alle übergebenen Parameter mit einem vorhandenen Spiel übereinstimmen, ansonsten erhalten wir nichts (None) zurück.

Price-Tabelle

Die Datei price.py füllst du mit diesem Code.

#!/bin/python3

from datetime import datetime
from sqlalchemy import Column, String, Integer, DateTime, DECIMAL, ForeignKey
from sqlalchemy.orm import relationship
from base import Base


class Price(Base):
    __tablename__ = 'prices'

    id = Column(Integer, primary_key=True)
    price = Column(DECIMAL)
    price_date = Column(DateTime, default=datetime.utcnow)
    game_id = Column(Integer, ForeignKey('games.id'))
    game = relationship('Game', backref='prices')

    def __init__(self, price, game):
        self.price = price
        self.game = game

Diese Klasse stellt das Objekt Price dar, welches die Basis für die Tabelle prices ist.

FeldnameDatentypBeschreibung
idInteger, ZahlPrimärschlüssel der Tabelle
priceDecimal, DezimalzahlPreis des Spiels (game_id)
price_dateDateTime, Zeit/DatumZeitpunkt, von wann der Preis ist
game_idInteger, ZahlBeinhaltet die ID des Spiels für welches der Preis gilt
gameRelationStellt die Beziehung zur games-Tabelle dar.

Bei den Preisen kann es dir egal sein, dass du Duplikate der Preise für die Spiele hast. Außerdem kannst du so, wenn du die Daten regelmäßig abfragst, eine Historie der Preise erstellen.

Platform-Tabelle

Die Datei platform.py füllst du mit folgendem Code.

#!/bin/python3
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import relationship
from base import Base, Session


class Platform(Base):
    __tablename__ = 'platforms'

    name = Column(String, primary_key=True)

    def __init__(self, name):
        self.name = name

    @classmethod
    def get_set(cls, name):
        session = Session()
        exists = session.query(Platform).filter(
            Platform.name.ilike(name)
        ).first()
        if exists:
            session.close()
            return exists
        session.close()
        return None

Die Klasse stellt das Objekt Platform dar, welches die Basis für die Tabelle platforms ist.

Name (String, Text) ist die einzige Spalte in der Tabelle platforms und gleichzeitig auch der Primärschlüssel. Auch hier haben wir die Methode get_set, welche entweder die vorhandene Plattform oder nichts zurückgibt.

Shop-Tabelle

Die Datei shop.py füllst du mit folgendem Code.

#!/bin/python3
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import relationship
from base import Base, Session


class Shop(Base):
    __tablename__ = 'shops'

    name = Column(String, primary_key=True)

    def __init__(self, name):
        self.name = name

    @classmethod
    def get_set(cls, name):
        session = Session()
         exists = session.query(Shop).filter(
              Shop.name.ilike(name)
         ).first()
         if exists:
              session.close()
              return exists
         session.close()
         return None

Die Klasse stellt das Objekt Shop dar, welches die Basis für die Tabelle shops ist. Name (String, Text) ist die einzige Spalte in der Tabelle shops und gleichzeitig auch der Primärschlüssel. Wie bei den Klassen Game und Platform haben wir auch hier wieder die Methode get_set, die entweder den angegebenen Shop, oder nichts zurückgibt.

Testen

Natürlich sollten wir auch testen, was wir da gebastelt haben. Lege dazu im Ordner base die Datei test.py an. Hier kannst du folgenden Code einfügen.

from datetime import date
from game import Game
from shop import Shop
from platform import Platform
from price import Price
from base import db_engine, Session, Base

# Erstellen der Tabellen auf Basis der Klassen
Base.metadata.create_all(db_engine)
session = Session()

# Prüfen ob es die Plattform PC gibt, ansonsten zur Session hinzufügen
platform_pc = Platform.get_set('PC')
if not platform_pc:
    session.add(Platform('PC'))

# Prüfen ob es den Shop MMOGA gibt, ansonsten zur Session hinzufügen
shop_mmoga = Shop.get_set('MMOGA')
if not shop_mmoga:
    session.add(Shop('MMOGA'))

# Prüfen ob es das Game schon gibt, ohne release_date
game_borderlands = Game.get_set(
'Borderlands',
       'https://www.mmoga.de/',
       platform_pc,
       shop_mmoga
)

session.commit()

# Zur Session hinzufügen, falls nicht vorhanden
if not game_borderlands:
       game_borderlands = Game(
           'Borderlands',
           'https://www.mmoga.de/',
           date(2009, 10, 30),
           Platform.get_set('PC'),
           Shop.get_set('MMOGA')
)
session.add(game_borderlands)


# dem Spiel einen Preis hinzufügen
price_borderlands = Price(
       3.49, game_borderlands
)
session.add(price_borderlands)

# Änderungen in die Datenbank schreiben
session.commit()

# Herkömmlicher Weg zum Abfragen einer Tabelle
game_test = session.query(Game).filter(
Game.name.ilike('Borderlands')
).first()

# Ausgabe aller Werte des Game-Objekts mit Plattform, Shop und Preis
print(f'Name: {game_test.name}\r\nLink: {game_test.url}')
print(f'Release: {game_test.release_date}\r\nPlattform: {game_test.platform.name}')
print(f'Shop: {game_test.shop.name}')
print(f'Price: {game_test.prices[-1].price} Date: {game_test.prices[-1].price_date}')

# Schließen der SQL-Sitzung
session.close()

Wenn du nun im Terminal bzw. der Konsole via python test.py ausführst, solltest du folgende Ausgabe erhalten.

Name: Borderlands
Link: https://www.mmoga.de/
Release: 2009-10-30
Plattform: PC
Shop: MMOGA
Price: 3.49 Date: 2019-07-22 20:07:59.441257

Und damit sind wir am Ende dieses Teils angekommen. Sollte bei dir etwas nicht wie beschrieben funktionieren, kannst du gerne ein Kommentar hinterlassen, dann schauen wir, ob wir das Problem gelöst bekommen.

Werbeanzeigen

Kommentar verfassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.