Python DB API – Korzystanie z relacyjnych baz danych w Pythonie



Wprowadzenie

Python posiada – jak każdy porządny język programowania – zunifikowany interfejs dostępu do relacyjnych baz danych. Koncepcyjnie jest on zbliżony do JDBC (Java DataBase Connectivity), lecz dzięki wykorzystaniu możliwości Pythona o wiele prostrzy.

W chwili obecnej obowiązuje Python Database API v2.0 (znane też jako PEP 249). Jest ona rozwinięciem Python Database API v1.0 (PEP 248).

Prosty przykład

Oto prosty przykład kodu wyświetlającego całą zawartość tabeli test. Korzystam z nim z relacyjnej bazy danych SQLite. Jej obsługa jest standardowo dostępna w Pythonie począwszy od wersji 2.5.

import sqlite3
db = sqlite3.connect('dbapi.db')
cur = db.cursor()

cur.execute('SELECT * FROM `test`')
print cur.rowcount
for i in cur.fetchall():
    print  i

db.close()

W pierwszej linii importujemy moduł obsługi wybranej przez nas bazy danych. Warto zwrócić uwagę, że niektóre bazy danych mają więcej niż jeden dostępny moduł (przykładowo PostgreSQL ma psycopg oraz psycopg2). Moduły dla większości baz trzeba samemu zainstalować.

W drugiej linii nawiązujemy połączenie z bazą. Paramatry jakie przekazujemy funkcji connect zależą od wybranej bazy danych. W przypadku SQLite jest to tylko nazwa pliku w którym przechowywane są dane. Innym bazą trzeba przekazać często o wiele więcej informacji (zwykle: nazwę bazy danych, adres maszyny na której się ona znajduje, nazwę użytkownika i jego hasło).

W trzeciej linii tworzymy kursor. Będziemy za jego pomocą wysyłali zapytania do bazy danych i odbierali z niej ich wyniki.

W piątej linii wysyłamy do bazy zapytanie. Warto zwrócić uwagę, że treść zapytania (tzn 'SELECT * FROM `test`') nie jest ujednolicana i zależy od bazy z jakiej korzystamy.

W szóstej linii wyświetlamy ile wierszy wyniku uzyskaliśmy.

W siódmej i ósmej linii pobieramy wszystkie wiersze wyniku do tablicy (robi to cur.fetchall()), a następnie wyświetlamy każdy wiersz w nowej linii.

W dziesiątej linii zamykamy połączenie z bazą danych.

Skrócony opis API

Interface modułu

connect(parametry...)
Łączy się z bazą danych wskazana poprzez parametry. Zwraca obiekt połączenia.
paramstyle

Określa sposób w jaki w zapytaniach wskazuje się miejsce na parametry.

Nie należy tworzyć zapytania przez konkatenację fragmentów SQL i parametrów. Tak stworzony kod jest podatny na ataki SQL injection.

'qmark'
cur.execute('SELECT * FROM `test` WHERE ' +
    'name = ? AND salary > ?',
    ('Adam', 1000) )
'numeric'
cur.execute('SELECT * FROM `test` WHERE ' +
    'name = :1 AND salary > :2',
    ('Adam', 1000) )
'named'
cur.execute('SELECT * FROM `test` WHERE ' +
    'name = :nm AND salary > :sal',
    {'nm': 'Adam', 'sal': 1000} )
'format'
cur.execute('SELECT * FROM `test` WHERE ' +
    'name = %s AND salary > %d',
    ('Adam', 1000) )
'pyformat'
cur.execute('SELECT * FROM `test` WHERE ' +
    'name = %(nm)s AND salary > %(sal)d',
    {'nm': 'Adam', 'sal': 1000} )

Wyjątki

Poniżej opisana jest hierarchia wyjątków.

Warning
Wyrzucany gdy nastąpi ważne ostrzeżenie.
Error
Klasa bazowa dla pozostałych błędów.

InterfaceError
Wyrzucany gdy błąd nastąpił w interfejsie bazy danych, a nie w samej bazie danych.
DatabaseError
Wyrzucany gdy błąd nastapił wewnątrz bazy danych.

DataError
Wyrzucany gdy pojawią się niepoprawne dane, przykładowo liczby z poza obsługiwanego zakresu, napisy dłuższe niż pole na nie przeznaczone.
OperationalError
Wyrzucany gdy pojawią się błędy w trakcie przetwarzania transakcji, nastąpi nagłe rozłączenie. W praktyce bywa wyrzucany również w przypadku gdzie powinien być zastosowany ProgrammingError.
IntegrityError
Wyrzucany gdy zachodzi próba przekroczenia więzów integralności, typu ustawienie już istniejącej wartości w kolumnie, której wartości powinny być unikalne lub ustawienie nieprawidłowego klucza obcego.
InternalError
Wywoływany gdy w bazie wystąpi wewnętrzny problem.
ProgrammingError
Wyrzucany gdy pojawią się odwołania do nieistniejących tabel, próby stworzenia już istniejących, itp.
NotSupportedError
Wyrzucany gdy nastąpi próba skorzystania z metody nie obsługiwanej przez bazę danych (przykładowo rollback() w bazie bez wsparcia transakcji).

Obiekt połączenia (connection)

close()
Zamyka połączenie z bazą danych. Automatycznie zamyka również wszystkie kursory z niego korzystające. Zamknięcie połączenia bez uprzedniego zatwierdzenia transakcji spowoduje jej wycofanie.
commit()
Zatwierdza transakcje. Metoda istnieje tylko w bazach obsługujących transakcje.
rollback()
Wycofuje transakcje. Metoda istnieje tylko w bazach obsługujących transakcje.
cursor()
Zwraca nowy kursor powiązany z tym połączeniem. W razie potrzeby może być emulowane.

Obiekt kursora (cursor)

Informacje o wyniku

description
Lista opisów poszczególnych kolumn odpowiedzi. Opis pojedynczej kolumny zawiera kolejno elementy:

name
nazwa kolumny – element obowiązkowy, musi być ustawiony
type_code
typ kolumny – element obowiązkowy, musi być ustawiony
display_size
internal_size
precision
scale
null_ok
False jeśli kolumna ma atrybut NOT NULL, True w przeciwnym wypadku

Elementy które nie są ustawione mają wartość None.

rowcount
Liczba zwróconych wierszy w przypadku SELECT, lub liczba stworzonych/zmienionych/usuniętych wierszy w przypadku INSERT / UPDATE / DELETE. Jeśli nie można ustalić ilość wierszy ostatniej instrukcji to rowcount jest ustawione na -1.

Wysyłanie zapytań

execute(operation[, parameters])
Tworzy i wywołuje zapytanie w bazie danych. Opcjonalny argument parameters może być (w zależności od paramstyle) sekwencją lub słownikiem. Wyniki zapytania są dostępne za pomocą kursora.
executemany(operation, seq_of_parameters)
Tworzy i wywołuje wielokrotne zapytanie w bazie danych. Zasadniczo działa jak poniższy kod (ale umożliwia lepszą optymalizację):

for parameters in seq_of_parameters:
    execute(operation, parameters)

Pobieranie danych

Po wykonaniu zapytania pobierającego dane z bazy należy je z niej odebrać.

fetchone()
Pobierana następny wiersz z wyniku zapytania, zwracając pojedynczą sekwencję, lub None kiedy wszystkie dane zostały już pobrane. Przykład użycia tej metody do pobrania wszystkich elementów z kursora

row = cur.fetchone()
while row is not None:
    print row
    row = cur.fetchone()
fetchmany([size=cursor.arraysize])
Pobiera pewną określoną poprzez argument size ilość elementów. Jeśli argument nie jest jawnie podany to zostaje użyta wartość cursor.arraysize.
fetchall()
Pobiera wszystkie (z tych, które oczekują na pobranie) wiersze z wyniku zapytania. Zwraca sekwencję sekwencji.

Inne

callproc(procname[, parameters])
Wywołuje procedurę składowaną. Dostępne tylko w niektórych bazach danych.
close()
Zamyka kursor, od tej pory nie można z niego korzystać.