Шаг 210.
Основы языка Python.
Доступ к базе данных SQLite из Python. Обработка исключений

    На этом шаге мы рассмотрим особенности обработки исключений при доступе к базе данных.

    Модуль sqlite3 поддерживает следующую иерархию исключений:

Exception
    Warning
    Error
        InterfaceError
        DatabaseError
            DataError
            ОperationalError
            IntegrityError
            InternalError
            ProgrammingError
            NotSupportedError

    Базовым классом самого верхнего уровня является класс Exception. Все остальные исключения определены в модуле sqlite3. Поэтому при указании исключения в инструкции except следует предварительно указать название модуля (например, sqlite3.DatabaseError). Исключения возбуждаются в следующих случаях:

    Для примера обработки исключений напишем программу, которая позволяет пользователям вводить название базы данных и SQL-команды в консоли: Листинг 18.19. Выполнение SQL-команд, введенных в консоли

# -*- coding: utf-8 -*-
import sqlite3, sys, re

def db_connect(db_name):
    try:
        db = sqlite3.connect (db_name, isolation_level=None)
    except (sqlite3.Error, sqlite3.Warning) as err:
        print("He удалось подключиться к БД")
        input()
        sys.exit(0)
    return db

print ("Введите название базы данных:", end=" ")
db_name = input ()
con = db_connect(db_name)# Подключаемся к базе
cur = con.cursor()
sql = ""
print ("Чтобы закончить выполнение программы, введите <Q>+<Enter>")
while True:
    tmp = input()
    if tmp in ["q", "Q"]:
        break
    if tmp.strip() == "":
        continue
    sql = "{0} {1}".format(sql, tmp)
    if sqlite3.complete_statement(sql):
        try:
            sql = sql.strip()
            cur.execute(sql)
            if re.match("SELECT ", sql, re.I):
                print(cur.fetchall())
        except (sqlite3.Error, sqlite3.Warning) as err:
            print("Ошибка:", err)
        else:
            print("Запрос успешно выполнен")
    sql = ""
cur.close()
con.close()
Архив с файлом можно взять здесь.

    Результат работы приложения:

Введите название базы данных: :memory:
Чтобы закончить выполнение программы, введите <Q>+<Enter>
SELECT 10 > 5
;
Запрос успешно выполнен
SELECT 10 > 5;
[(1,)]
Запрос успешно выполнен
SELECT 10 > 5; SELECT 20 + 2;
Ошибка: You can only execute one statement at a time.
q

    Чтобы SQL-запрос можно было разместить на нескольких строках, мы выполняем проверку завершенности запроса с помощью функции compiete_statement(<SQL-Запрос>). Функция возвращает True, если параметр содержит один или более полных SQL-запросов. Признаком завершенности запроса является точка с запятой. Никакой проверки правильности SQL-запроса не производится. Пример использования функции:

>>> sql = "SELECT 10 > 5;"
>>> sqlite3.complete_statement (sql)
True
>>> sql = "SELECT 10 > 5"
>>> sqlite3.complete_statement(sql)
False
>>> sql = "SELECT 10 > 5; SELECT 20 +2;"
>>> sqlite3.complete_statement (sql)
True

    Язык Python также поддерживает протокол менеджеров контекста, который гарантирует выполнение завершающих действий вне зависимости от того, произошло исключение внутри блока кода или нет. В модуле sqlite3 объект соединения поддерживает этот протокол. Если внутри блока with не произошло исключение, то автоматически вызывается метод commit(). В противном случае все изменения отменяются с помощью метода rollback(). Для примера добавим три рубрики в таблицу rubr. В первом случае запрос будет без ошибок, а во втором случае выполним два запроса, последний из которых будет добавлять рубрику с уже существующим идентификатором:

# -*- coding: utf-8 -*-
import sqlite3

con = sqlite3.connect(r"C:\book\catalog.db")
try:
    with con:
        # Добавление новой рубрики
        con.execute("""INSERT INTO rubr VALUES (NULL, 'Мода')""")
except sqlite3.DatabaseError as err:
    print("Ошибка:", err)
else:
    print("Запрос успешно выполнен")
try:
    with con:
        # Добавление новой рубрики
        con.execute("""INSERT INTO rubr VALUES (NULL, 'Спорт')""")
        # Рубрика с идентификатором 1 уже существует!
        con.execute("""INSERT INTO rubr VALUES (1, 'Казино')""")
except sqlite3.DatabaseError as err:
    print("Ошибка:", err)
else:
    print("Запрос успешно выполнен")
con.close()
input ()
Архив с файлом можно взять здесь.

    Результат работы приложения:

Запрос успешно выполнен
Ошибка: UNIQUE constraint failed: rubr.id_rubr

    Итак, в первом случае запрос не содержит ошибок, и рубрика мода будет успешно добавлена в таблицу. Во втором случае возникнет исключение IntegrityError. Поэтому ни рубрика Спорт, ни рубрика Казино в таблицу добавлены не будут, т. к. все изменения автоматически отменяются с помощью вызова метода rollback () (рисунок 1).


Рис.1. Содержимое таблицы rubr после выполнения программы

    На следующем шаге мы рассмотрим трассировку выполняемых запросов.




Предыдущий шаг Содержание Следующий шаг