На этом шаге мы рассмотрим особенности выполнения запросов.
Согласно спецификации DB-API 2.0, после создания объекта соединения необходимо создать объект-курсор. Все дальнейшие запросы должны производиться через этот объект. Создание объекта-курсора осуществляется с помощью метода cursor ([<Класс курсора>]). Для выполнения запроса к базе данных предназначены следующие методы курсора MySQLdb.cursors.Cursor:
import MySQLdb con = MySQLdb.connect(host="localhost", user="root", charset="utf8") cur = con.cursor() # Создаем объект-курсор sql = """CREATE DATABASE `python` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci""" try: # Обрабатываем исключения cur.execute(sql) # Выполняем SQL-запрос except MySQLdb.DatabaseError as err: print("Ошибка:", err) else: print("Запрос успешно выполнен") cur.close() # Закрываем объект-курсор con.close() # Закрываем соединение input()
Обратим внимание на обратные апострофы, присутствующие в представленном здесь коде. Ими в MySQL выделяются имена баз данных, таблиц и полей. Если этого не сделать, запрос не будет обработан, и возникнет ошибка.
Теперь подключимся к новой базе данных, создадим таблицу и добавим запись:
import MySQLdb con = MySQLdb.connect(host="localhost", user="root", charset="utf8", db="python") cur = con.cursor() # Создаем объект-курсор sql_1 = """\ CREATE TABLE `city` ( `id_city` INT NOT NULL AUTO_INCREMENT, `name_city` CHAR(255) NOT NULL, PRIMARY KEY (`id_city`) ) ENGINE=MyISAM DEFAULT CHARSET=Utf8""" sql_2 = "INSERT INTO `city` VALUES (NULL, 'Санкт-Петербург')" try: # Обрабатываем исключения cur.execute("SET NAMES utf8") # Кодировка соединения cur.execute(sql_1) cur.execute(sql_2) except MySQLdb.DatabaseError as err: print("Ошибка:", err) else: print("Запрос успешно выполнен") con.commit() cur.close() # Закрываем объект-курсор con.close() # Закрываем соединение input()
В этом примере мы применили метод commit () объекта соединения. Метод commit () позволяет завершить транзакцию, которая запускается автоматически. При использовании транзакций в MySQL существуют нюансы. Так, таблица типа MyISAM, которую мы создали в этом примере, не поддерживает транзакции, поэтому вызов метода commit () можно опустить. Тем не менее, как видно из примера, указание метода не приводит в ошибке. Однако попытка отменить изменения с помощью метода rollback () не приведет к желаемому результату, а в некоторых случаях использование этого метода может возбудить исключение NotSupportedError.
Таблицы типа InnoDB транзакции поддерживают, поэтому все запросы, изменяющие записи (INSERT, REPLACE, UPDATE и DELETE), необходимо завершать вызовом метода commit (). При этом отменить изменения можно будет с помощью метода rollback ().
Чтобы транзакции завершались без вызова метода commit (), следует указать значение True в методе autocommit () объекта соединения:
con.autocommit(True) # Автоматическое завершение транзакции
В некоторых случаях в SQL-запрос необходимо подставлять данные, полученные от пользователя. Если данные не обработать и подставить в SQL-запрос, то пользователь получит возможность видоизменить запрос и, например, зайти в закрытый раздел без ввода пароля. Чтобы значения были правильно подставлены, необходимо их передавать в виде кортежа или словаря во втором параметре метода execute ().
В этом случае в SQL-запросе указываются следующие специальные заполнители:
Для примера заполним таблицу с городами этими способами:
import MySQLdb con = MySQLdb.connect(host="localhost", user="root", charset="utf8", db="python") con.autocommit(True) # Автоматическое завершение транзакции cur = con.cursor() # Создаем объект-курсор t1 = ("Москва",) # Запятая в конце обязательна! t2 = (3, "Новгород") d = {"id": 4, "name": """Новый ' " город"""} sql_t1 = "INSERT INTO `city` (`name_city`) VALUES (%s)" sql_t2 = "INSERT INTO `city` VALUES (%s, %s)" sql_d = "INSERT INTO `city` VALUES (%(id)s, %(name)s)" try: # Обрабатываем исключения cur.execute("SET NAMES utf8") # Кодировка соединения cur.execute(sql_t1, t1) # Кортеж из 1-го элемента cur.execute(sql_t2, t2) # Кортеж из 2-х элементов cur.execute(sql_d, d) # Словарь except MySQLdb.DatabaseError as err: print("Ошибка:", err) else: print("Запрос успешно выполнен") cur.close() # Закрываем объект-курсор con.close() # Закрываем соединение input()
Обратите внимание на значение переменной t1. Перед закрывающей круглой скобкой запятая указана не по ошибке. Если запятую убрать, то вместо кортежа мы получим строку. В значении ключа name переменной d апостроф и двойная кавычка также указаны не случайно. Это значение показывает, что при подстановке все специальные символы экранируются, поэтому никакой ошибки при вставке значения в таблицу не будет.
Добавим два города с помощью метода executemany ():
import MySQLdb con = MySQLdb.connect(host="localhost", user="root", charset="utf8", db="python") con.autocommit(True) # Автоматическое завершение транзакции cur = con.cursor() # Создаем объект-курсор arr = [ ("Пермь",), ("Самара",) ] sql = "INSERT INTO `city` (`name_city`) VALUES (%s)" try: # Обрабатываем исключения cur.execute("SET NAMES utf8") # Кодировка соединения cur.executemany(sql, arr) # Выполняем запрос except MySQLdb.DatabaseError as err: print("Ошибка:", err) else: print("Запрос успешно выполнен") cur.close() # Закрываем объект-курсор con.close() # Закрываем соединение input()
Объект-курсор поддерживает несколько атрибутов:
import MySQLdb con = MySQLdb.connect(host="localhost", user="root", charset="utf8", db="python") con.autocommit(True) # Автоматическое завершение транзакции cur = con.cursor() # Создаем объект-курсор sql = "INSERT INTO `city` (`name_city`) VALUES ('Омск')" try: # Обрабатываем исключения cur.execute("SET NAMES utf8") # Кодировка соединения cur.execute(sql) # Выполняем запрос except MySQLdb.DatabaseError as err: print("Ошибка:", err) else: print("Запрос успешно выполнен") print("Индекс:", cur.lastrowid) print("Индекс:", con.insert_id()) cur.close() # Закрываем объект-курсор con.close() # Закрываем соединение input()
Результат работы приложения:
Запрос успешно выполнен
Индекс: 7
Индекс: 7
SELECT * FROM `city` ,
( ('id_city', 3, 1, 11, 11, 0, 0),
('name_city', 254, 29, 765, 765, 0, 0))
На следующем шаге мы рассмотрим обработку результата запроса.