SQLAlchemy – wyższy poziom operacji na bazach danych

SQL pamięta dinozaury

Przez lata, tradycyjnym sposobem wykonywania operacji na bazie danych był język SQL. Język dość prosty, lecz posiadający swoje specyficzne elementy, w zależności od systemu baz danych – MySQL, Oracle i innych.

Dodatkowo w czasie kiedy języki programowania obiektowe zyskiwały na popularności, SQL pozostawał skupiony na tabelach oraz zmiennych.

Potem pojawiło się programowanie obiektowe

Tym samym, w momencie kiedy szybo i łatwo definiowaliśmy nową klasę w języku Python, aby zacząć przechowywać obiekty tego typu w bazie danych, musieliśmy nauczyć się podstaw języka SQL, zdefiniować tabelę do której obiekty będą zapisywane, format zmiennych, a dodatkowo zrobić to w wersji SQL wspieranej właśnie przez naszą bazę danych. Np Oracle, MySQL itp.

Niewątpliwie dostarczało nam to kontrolę oraz pełną i świadomą możliwość wykorzystania całego potencjału naszej bazy danych, ale czy naprawdę ma to znaczenie w każdym z naszych projektów?

SQL + programowanie obiektowe = SQLAlchemy

  • Co jeżeli przy definiowaniu klasy, odpowiednia tabela w bazie danych tworzyła się automatycznie
  • Co jeżeli zapisywanie do bazy danych, jak i również czytanie z niej, wymagało od nas jedynie znania pojedynczych komend, typu write, find, które wykonywali byśmy na samej klasie.
  • Co jeżeli byli byśmy pozbawieni konieczności nauki SQL oraz rozumienia jego specyfiki w zależności od systemu bazy danych.

Innymi słowy w momencie, kiedy zdefiniowali byśmy naszą klasę przykładowo Human, zawierającą zmienne Imie, Nazwisko i Wiek, zamiast w celu dokonania wpisu do bazy danych, tworzenia polecenia SQL:

Old and good SQL

INSERT INTO DataBase.table_with_humans (Imie, Nazwisko, Wiek)
VALUES (Mike,Smith,27’);

Wykonalibyśmy tylko proste polecenie na obiekcie

New and sexy SQLAlchemy

DataBase.add(human(Mike,Smith,27’))

Interesujące, prawda?

Od początku – Object Relational Mapping

Object Relational Mapping, która reprezentuje SQLAlchemy, jest techniką programowania polegającą na transferowaniu danych pomiędzy różnymi systemami przy użyciu programowania obiektowego. I z tym właśnie mieliśmy do czynienia w powyższym przykładnie.

Jak nie trudno się domyśleć jest to bardzo „Python way”. Może nie jest to najefektywniejsze, ale nie o efektywność zawsze chodzi. Często zależy nam, aby można było wykonać program szybko, minimalnym wysiłkiem, zachowując maximum elastyczności.

Popularne biblioteki wspierające ORM w Python:

  • Django ORM. Biblioteka/framework będąca standardem operacji na bazie danych w najpopularniejszym frameworku webowych Pythona – Django
  • Storm, Peewee – również bardzo popularne systemy ORM, prawdopodobnie warte zapoznania się
  • SQL Alchemy bodajże najpopularniejszy i najczęściej rekomendowany system ORM poza przypadkami kiedy używany jest framework Django, gdzie Django ORM jest na ogół domyślnym wyborem. Stosowany często z alternatywnym do Django frameworking – Flask.

SQLAlchemy – utworzenie bazy danych oraz tabeli

Plik – base.py

W celu zademonstrowania SQLAlchemy w akcji, przerobimy prosty przykład. Jest on spójny z przykładem na oficjalnej stronie SQLAlchemy tak więc będzie nam łatwo kontynuować naukę na podstawie oficjalnych materiałów.

Cel: utworzenie banalnej bazy danych, zawierającej transakcję zakupu. Nic więcej.

SQLAlchemy Krok 1: Otwarcie bazy danych, lub jej stworzenie, jeżeli jeszcze nie istnieje

Silnikiem bazy danych będzie SQLite3, czyli najprostszy z możliwych, lecz na nasze potrzeby wystarczający. W tym celu importujemy odpowiednią bibliotekę oraz używamy funkcji create_engine.

To on nawiąże połączenie, lub utworzy bazę danych jeżeli nie istnieje.

# import obiektów z SQLAlchemy, których działanie zobaczymy z czasem 

from sqlalchemy import create_engine
from sqlalchemy import Column, String, Integer
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

# inicjalizacja połaczenia z bazą danych 

engine = create_engine('sqlite:///C:\\Python\\BED\\sqlalchemy.sqlite', echo=True)


# obsluga zarządzania tabelami

base = declarative_base()

na początku to wszystko wygląda straszenie. Ale robimy to raz, a cieszymy się tym przez cały nasz program.

Jeżeli nie udało się zaimportować biblioteki, należy ją wcześniej doinstalować

$ pip install sqlalchemy

Krok 2: Definiujemy klasę reprezentującą użytkowników, którzy mają być przechowywani w bazie danych

Cała idea tego artykułu polega na mapowaniu klas na obiekty w bazie danych. Tak więc potrzebujemy klasy 🙂

Teraz… aby nasza klasa znalazła swoje odzwierciedlenie w bazie danych, przy użyciu SQL Alchemy, musi spełnić 2 warunki

1. dziedziczyć po klasie Base, którą wcześniej musimy zainicjować

2. zawierać zmienną __tablename__ , która wskazuje nazwę tabeli w bazie danych, pod którą chcemy przechowywać informacje na temat transakcji

Dodatkowo, każda zmienna którą chcemy przechowywać, musi zostać stworzona za pomocą polecenia Column, tak jak poniżej.

class Transakcje(base):

    __tablename__ = 'transakcje'

    transakcja_id = Column(Integer, primary_key=True )
    data = Column(String)
    przedmiot_id = Column(Integer)
    cena = Column(Integer)

    def __init__(self, transakcja_id, data, przedmiot_id, cena):
        self.transakcja_id = transakcja_id
        self.data = data
        self.przedmiot_id = przedmiot_id
        self.cena = cena

Konstruktor nie jest wymagany przez SQLAlchemy, ale dodaliśmy go dla naszej wygody.

Wygląda wystarczająco podobnie do tworzenia standardowej klasy aby szybko i łatwo weszło nam to w krew. Po za drobnymi modyfikacjami, jest to po prostu klasa w Python.

Krok 3: Utworzenie obiektów

Mamy już naszą klasę Transakcje, która dziedziczy po klasie Base. Pora stworzyć obiekty, które będziemy chcieć przechowywać.

W naszym przypadku jest to bardzo proste. Tworzymy tylko 1 przykładowy obiekt na potrzeby demonstracji.

# tworzenie tabel

base.metadata.create_all(engine)

I w ten sposób, we wskazanej przez nas bazie danych, została utworzona tabela, która odpowiada klasie która zdefiniowaliśmy

Krok 4: Podziwiamy efekty swojej pracy w DB Viewer albo innym narzędziu do podglądu danych w bazie SQLite

Boom!

Działa. Utworzyliśmy klasę Transakcje i dzięki SQLAlchemy powstała tabela w bazie danych, która ją reprezentuje, To wszystko umieściliśmy w pliku base.py i będziemy go wykorzystywać w kolejnych krokach.

SQLAlchemy – dodawanie danych

Plik – insert.py

Płynąc na fali utworzonego pliku base.py, utworzymy kolejny plik insert.py, który będzie zawierać prosty przykład jak można dodać dane do naszej bazy danych.

Nie będzie on długi, gdyż wykorzystamy plik base.py gdyż duża część naszej pracy jest w nim zrobiona.

To co będzie nowe, to:

  • otwarcie sesji
  • utworzenie nowego obiektu klasy Transakcje
  • wykorzystanie sesji do zapisu nowego obiektu do naszej bazy danych
  • wykorzystanie sesji do zapisu zmian
import base
from sqlalchemy.orm import sessionmaker

# Stworzenie nowej sesji

Session = sessionmaker(bind=base.engine)
session = Session()

# dodanie danych

for t in range(10):
    tr = base.Transakcje(t, '2020/05/06', 12, 19)
    session.add(tr)

# zapis zmian w bazie danych

session.commit()

a w rezultacie, jak zajrzymy do naszej bazy, zobaczymy:

2 x BOOM

Na początku utworzyliśmy base.py. Wyglądało grośnie, ale jak się okazało, raz napisaliśmy i wykorzystaliśmy do zapisu danych w pliku insert.py. A plik insert.py już wygląda przyjemnie 🙂

SQLAlchemy – odczyt danych

Plik select.py

Równie prosto, możemy napisać program, który odczytuje z naszej bazy i tabeli Transakcje.

import base
from sqlalchemy.orm import sessionmaker

Session = sessionmaker(bind=base.engine)
session = Session()

# wszystkie dane

for s in session.query(base.Transakcje).all():
    print(s.transakcja_id)

# wybrane transakcje

for s in session.query(base.Transakcje).filter(base.Transakcje.transakcja_id>5):
    print(s.transakcja_id)

3 x BOOM

Ponownie wykorzystaliśmy base.py i tak samo jak w przypadku zapisu danych do bazy, tak samo odczyt ich jest prosty.

Podsumowując SQLAlchemy

ORM, czyli Object Relational Mapping, pozwala nam w prosty sposób operować na bazie danych bez pomocy SQL oraz bez znajomości jej specyfiki.

Tym samym z praktycznego punktu widzenia, przyspiesza tworzenie samej aplikacji bez konieczności poświęcania czasu na warstwę komunikacji z bazą. Jest to bardzo Pythonowski sposób myślenia, który jest czymś standardowych w przypadki frameworku Django.

W przypadku pozostałych aplikacji będzie od nas wymagał użycia dodatkowej biblioteki jak SQL Alchemy.