Zarábaj až 6 000 € mesačne! Akreditované rekvalifikačné kurzy od 0 €. Viac informácií.

1. diel - SQLAlchemy - Úvod a inštalácia

Dnes sa budeme zaoberať temným stredovekom a variť rôzne elixíry v malej alchymistickej dielni pomocou novodobých surovín. Teda uvediem tieto slová na pravú mieru. Zavedieme istú abstrakciu nad SQL kódom, ktorá mierne uľahčí ďalší život programátora (nie chemika) a budeme pracovať s databázovými otázkami ako s bežným dátovým typom v Pythone.

sqlalchemy log - SQLAlchemy databázy krok za krokom

Predpokladom k tomuto článku je znalosť Pythona, objektového programovania v Pythone (alebo všeobecná znalosť) ak tomu SQL databázy. Aspoň nejaké.

Iste ste už počuli o ORM (O bject- R elational M apping) ako určitej programovej vrstve medzi relačnou databázou a objektovými typmi. Existuje nespočetné množstvo knižníc, ktoré tento problém riešia. Jednou z nich je napríklad Django.ORM alebo Tortoise ORM, avšak pre túto chvíľu som zvolil SQLAlchemy a to hneď z niekoľkých dôvodov:

  • dlhá história (2006),
  • komplexnosť,
  • pomerne ľahké,
  • používané v mnohých aplikáciách,
  • veľmi dobrá rýchlosť,
  • býva používaná vo frameworkoch pre web (napr. Flask), nie súčasťou

Inštalácia

Ako už je v Pythone obvyklé, najľahšie knižnicu sqlalchemy pridáme do systému pomocou príkazu (obvyklá a notoricky známa akcia):
pip3 install sqlalchemy

Inštaláciu môžeme vykonať aj cez vývojové prostredie (klasická inštalácia balíčka). Napr. v PyCharm: File => Settings... => Project: MenoVasehoProjektu = > Python Interpreter => + => zadanie názvu balíčka do vyhľadávacieho riadku - v tomto prípade: SQLAlchemy => vyberie balíček z ponuky = > Install Package

Prvé kroky

Vzhľadom k tomu, že Alchemy (jednoducho budem pomenovanie skracovať) je veľmi rozsiahla a opísať komplexne jej schopnosti by bolo v rozsahu stoviek stránkovej knihy, pozrieme sa len na časť. Hlavný cieľ snaženia je použiteľnosť v rôznych aplikáciách.

Začneme s jednou triedou, inak vzaté, s jednou tabuľkou v databáze a postupne budeme pokračovať a rozširovať do komplexnejšej štruktúry dát.

Prvotná úloha je pripojiť sa k databáze alebo prípadne ju stvoriť. Teda založiť databázový stroj, ktorý sa postará o prácu s dátami na nízkej úrovni a nám bude jedno, ktorý typ databázy sa používa. Napriek tomu sa uchyľujem k jednoduchosti a volím SQLite a jej súborovú reprezentáciu:

from sqlalchemy import create_engine

db = create_engine("sqlite:///database.db", echo=True)

# Varianty pre iné systémy. Podobne je možné použiť napríklad: Oracle, MS SQL atď...
db = create_engine('postgresql://uživatel:heslo@localhost:5432/mojedata')
db = create_engine('mysql://uživatel:heslo@localhost/databáze')

Teda funkcia create_engine() vráti inštanciu Engine, ktorá sa dá ďalej prispôsobovať podľa dialektu databázy. Ďalej cez DBAPI sa spoja so samotným databázovým strojom. Toto je však trochu "vyššia dievčenská" a možno sa jej budeme venovať oveľa neskôr, ak bude záujem.

Za zmienku stojí parameter echo, ktorý umožní zobrazenie všetkých výstupov Alchemy vrátane vykonávaných otázok, veľmi vhodné pre ďalšie vyladenie.

Pripojenie k databáze v Alchemy je tzv. lenivé, teda k žiadnemu fyzickému spojeniu nedôjde až do okamihu nutnosti. Pre tento prípad je volanie metód z Engine ako sú connect(), execute() atp. Treba však upozorniť, že prakticky sa tieto metódy nepoužívajú priamo, ale sa toto odohráva za scénou.

Deklaratívne mapovanie

Už samotná deklarácia dát môže prebehnúť mnohými spôsobmi a tu si predstavíme ľahký prístup na základe tried. Budeme však potrebovať nejakú základnú modelovú triedu, ktorá umožní pracovať s dátami. Tú Alchemy ponúka ako declarative_base, z ktorej vytvoríme prvotný objekt:
from sqlalchemy.orm import declarative_base
Base = declarative_base(db)

Metóda declarative_base() je však iba istá skratka odkazujúca na objekt registry. Rovnako by to šlo zapísať napríklad týmto spôsobom:

from sqlalchemy.orm import registry

mapper_registry = registry()
Base = mapper_registry.generate_base()

Na druhú stranu prečo sa sťažovať prácu a navyše sa odchyľovať od tried používaním len nových typov. Iba preto, že to ide?

Od triedy Base budeme ďalej odvodzovať dátové štruktúry. Aby sme si predviedli niečo použiteľné, budeme pracovať s dátami reálneho programu pre skladovú evidenciu (teda veľmi zjednodušenú verziu):

SQLAlchemy databázy krok za krokom

Každá tabuľka bude mať vlastnú triedu, kde budú definované jednotlivé stĺpce s ich typom a jednu funkciu, ktoré bude vracať textovú reprezentáciu dát.

Dátové typy SQLAlchemy

Držíme sa konceptu Pythona. Všetko je objekt, teda aj Alchýmia sa drží tejto receptúry a používa vlastné dátové typy, ktorými sa odkazuje na natívne databázy. Pre základ by som mohol vymenovať dátový typ Integer, Float, String, Datetime, Boolean... a mnoho ďalších. Pozitívne je, že sú si s Pythonom spriaznené svojim správaním a toto prenáša aj do databázových strojov bez ohľadu na druh.

Deklarácia bez relácií

Pre nás bude dôležitý objekt typu Column, ktorý predstavuje jeden stĺpik v tabuľke (databázovej) a môže mať aj ďalšie atribúty, ako sú kľúče, indexy a podobne. Vlastne každý stĺpik, ktorý má byť uložený bude tohto typu:
from sqlalchemy import Column, Integer, Float

class Vat(Base):
    __tablename__ = "vat"                                            # Pomenovanie tabuľky v databáze

    vat_id = Column(Integer, primary_key=True, autoincrement=False)  # ID položky
    rate = Column(Float, nullable=False)                             # Sadzba v percentách

    def __repr__(self):
        """Textová reprezentácia riadku z tabuľky"""
        return "<DPH : id={self.vat_id}; sadzba={self.rate}>".format(self=self)

Nutnosťou je definovať názov tabuľky __tablename__ = "tabulka". Pomenovanie triedy nerieši uloženie, tento problém spadá pod metadata a istú internú chémiu na báze ortuti a olova. Napriek tomu si poďme trochu rozobrať programovú ukážku.

Premenná vat_id je normálne ID položky, ktorý má byť celočíselný. Navyše je to primárny kľúč (primary_key=True). Alchemy vyžaduje aspoň jeden atribút definovaný s primárnym kľúčom.

Tiež obvykle u týchto dát požadujeme zvýšenie hodnoty pri novom zázname (autoincrement=True). Tu však budú iba 3 položky a budú pevné. Bez dane, nižšia sadzba (10 %) a vyššia sadzba (21 %). Pokiaľ sa štátna legislatíva nejako nezmení, tieto položky sú relatívne stále. rate je stĺpik s reálnym číslom typu Float, tu je jednoducho len percentuálna hodnota. Viac nie je čo riešiť.

Parameter nullable (ako už pomenovanie naznačuje) nám hovorí, či atribút môže nadobúdať hodnoty NULL alebo nie.

Metóda __repr__(self) len vráti nejaký text, ktorý obsahuje informácie o dátach v jednom riadku tabuľky. Používam stručnú verziu formátovaných argumentov. Myslím, že vy, ostrieľaní Pythonieri, by ste našli aj iné možnosti, ako urobiť pekný textový výstup 😃 Táto metóda však nie je povinná a pokojne sa bez nej zaobídete - je použitá len pre pochopiteľnejšie výstupy v rámci kurzu.

Niekoľko poznámok k typom

Malý problém môže nastať pri použití ďalších druhov databáz. Napríklad Oracle vyžaduje sekvenčný identifikátor. To je možné riešiť kódom:
vat_id = Column(Integer, Sequence("vat_id_seq"), ...

Ďalej môže problémy spôsobiť deklaráciu položky typu String, napríklad:

note = Column(String)

sa vygeneruje ako note VARCHAR, čo napríklad SQLite alebo PostgreSQL bude akceptovať. Avšak MySQL by sa to nepáčilo, pretože vyžaduje explicitne zadanú dĺžku reťazca. Je teda lepšie použiť preventívne:

note = Column(String(100))

Ďalšie možnosti

Dáta je možné deklarovať napríklad imperatívnym spôsobom. Na rozdiel od klasického postupu, kde sú "metadatá" vytvárané oddelene, pri použití Table sa stávajú súčasťou triedy.

Tento postup však nepatrí medzi odporúčané, preto od neho ustúpim a výhradne sa budem venovať iba vyššie uvedenému štýlu:

from sqlalchemy import Table, Column, Integer, String, ForeignKey
from sqlalchemy.orm import registry

mapper_registry = registry()

vat_table = Table(
    'vat',  # názov tabuľky
    mapper_registry.metadata,  # Zaregistrujeme metadáta a definujeme stĺpiky tabuľky
    Column('vat_id', Integer, primary_key=True, autoincrement=False),
    Column('rate', Float),
)

# Prázdna trieda postačí
class Vat:
    pass

# Namapujeme tabuľku na triedu
mapper_registry.map_imperatively(Vat, vat_table)

Aby som vás neprehltil informáciami, dokončíme si naše deklaratívne mapovanie v budúcej lekcii 🙂

V budúcej lekcii, SQLAlchemy - Session a základné práce s dátami , si preberieme Session a základnú prácu s dátami.


 

Všetky články v sekcii
SQLAlchemy databázy krok za krokom
Preskočiť článok
(neodporúčame)
SQLAlchemy - Session a základné práce s dátami
Článok pre vás napísal Virlupus
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Autor se věnuje webovým aplikacím, skladově-účetnímu softwaru, 3D grafice, lexiální analýze a parserování. Studuje fyziku na MFF UK. Učil IT na střední škole.
Aktivity