ээдд. Прохоренок_Н_А__Дронов_В_А_Python_3_и_PyQt_5_Разработка_приложен. Николай Прохоренок Владимир Дронов
Скачать 7.92 Mb.
|
FIL={MS Access};DBQ=c:/work/data.mdb"); con3.open() con3.close() Полное описание класса QSqlDatabase приведено на странице https://doc.qt.io/qt-5/qsql database.html. 23.2. Получение сведений о структуре таблицы PyQt позволяет получить некоторые сведения о структуре таблиц, хранящихся в базе: списки полей таблицы, параметры отдельного поля, индекса и ошибки, возникшей при ра- боте с базой. 23.2.1. Получение сведений о таблице Сведения о структуре таблицы можно получить вызовом метода record() класса QSqlDatabase . Эти сведения представляются экземпляром класса QSqlRecord Для получения сведений о полях таблицы используются следующие методы этого класса: count() — возвращает количество полей в таблице; fieldName(<Индекс поля>) — возвращает имя поля, имеющее заданный индекс, или пус- тую строку, если индекс некорректен; field(<Индекс поля>) — возвращает сведения о поле (экземпляр класса QSqlField ), чей индекс задан в качестве параметра; field(<Имя поля>) — возвращает сведения о поле (экземпляр класса QSqlField ), чье имя задано в качестве параметра; indexOf(<Имя поля>) — возвращает индекс поля с указанным именем или -1 , если тако- го поля нет. При поиске поля не учитывается регистр символов; contains(<Имя поля>) — возвращает True , если поле с указанным именем существует, и False — в противном случае; isEmpty() — возвращает True , если в таблице нет полей, и False — в противном случае. 536 Часть II. Библиотека PyQt 5 Полное описание класса QSqlRecord приведено на странице https://doc.qt.io/qt-5/qsql record.html. 23.2.2. Получение сведений об отдельном поле Сведения об отдельном поле таблицы возвращаются методом field() класса QSqlRecord . Их представляет экземпляр класса QSqlField , поддерживающий следующие методы: name() — возвращает имя поля; type() — возвращает тип поля в виде одного из следующих атрибутов класса QVariant , объявленного в модуле QtCore (здесь приведен список лишь наиболее часто употребляе- мых типов — полный их список можно найти по адресу https://doc.qt.io/qt-5/qvariant- obsolete.html#Type-enum): • Invalid — неизвестный тип; • Bool — логический ( bool ); • ByteArray — массив байтов ( QByteArray , bytes ); • Char — строка из одного символа ( str ); • Date — значение даты ( QDate или datetime.date ); • DateTime — значение даты и времени ( QDateTime или datetime.datetime ); • Double — вещественное число ( float ); • Int и LongLong — целое число ( int ); • String — строка ( str ); • Time — значение времени ( QTime или datetime.time ); • UInt и ULongLong — положительное целое число ( int ); length() — возвращает длину поля; precision() — возвращает количество знаков после запятой для полей, хранящих веще- ственные числа; defaultValue() — возвращает значение поля по умолчанию; requiredStatus() — возвращает признак, является ли поле обязательным к заполнению, в виде одного из атрибутов класса QSqlField : • Required — 1 — поле является обязательным к заполнению; • Optional — 0 — поле не является обязательным к заполнению; • Unknown — -1 — определить признак обязательности заполнения поля не представля- ется возможным; isAutoValue() — возвращает True , если значение в поле заносится автоматически (что может быть, например, у поля автоинкремента), и False — в противном случае; isReadOnly() — возвращает True , если поле доступно только для чтения, и False — в противном случае. Полное описание класса QSqlField приведено на странице https://doc.qt.io/qt-5/qsqlfield.html. Глава 23. Работа с базами данных 537 23.2.3. Получение сведений об индексе Сведения о ключевом индексе, возвращаемые методом primaryIndex() класса QSqlDatabase , представлены экземпляром класса QSqlIndex . Он наследует все методы класса QSqlRecord , тем самым позволяя узнать, в частности, список полей, на основе которых создан индекс. Также он определяет следующие методы: name() — возвращает имя индекса или пустую строку для ключевого индекса; isDescending(<Номер поля>) — возвращает True , если поле с указанным номером в ин- дексе отсортировано по убыванию, и False — в противном случае. Полное описание класса QSqlIndex приведено на странице https://doc.qt.io/qt-5/qsqlindex.html. 23.2.4. Получение сведений об ошибке Сведения об ошибке, возникшей при работе с базой данных, представляются экземпляром класса QSqlError . Выяснить, что за ошибка произошла и каковы ее причины, позволят сле- дующие методы вышеупомянутого класса: type() — возвращает код ошибки в виде одного из следующих атрибутов класса QSqlError : • NoError — 0 — никакой ошибки не возникло; • ConnectionError — 1 — ошибка соединения с базой данных; • StatementError — 2 — ошибка в коде SQL-запроса; • TransactionError — 3 — ошибка в обработке транзакции; • UnknownError — 4 — ошибка неустановленной природы. Если код ошибки не удается определить, возвращается -1 ; text() — возвращает полное текстовое описание ошибки (фактически — значения, воз- вращаемые методами databaseText() и driverText() , объединенные в одну строку); databaseText() — возвращает текстовое описание ошибки, сгенерированное базой дан- ных; driverText() — возвращает текстовое описание ошибки, сгенерированное драйвером базы данных, который входит в состав PyQt; nativeErrorCode() — возвращает строковый код ошибки, специфический для выбранно- го формата баз данных. Пример: con = QtSql.QSqlDatabase.addDatabase('QSQLITE') con.setDatabaseName('data.sqlite') if con.open(): # Работаем с базой данных else: # Выводим текст описания ошибки print(con.lastError().text()) Полное описание класса QSqlError можно найти на странице https://doc.qt.io/qt-5/qsql error.html. 538 Часть II. Библиотека PyQt 5 23.3. Выполнение SQL-запросов и получение их результатов Класс QSqlQuery позволяет выполнять SQL-запросы любого назначения: создания необхо- димых таблиц и индексов, добавления, изменения, удаления и, разумеется, выборки запи- сей. Это один из наиболее развитых механизмов работы с данными, предоставляемых PyQt. П РИМЕЧАНИЕ Далее будут рассмотрены лишь наиболее часто используемые возможности класса QSqlQuery . Полное его описание приведено на странице https://doc.qt.io/qt-5/qsql query.html. 23.3.1. Выполнение запросов Чтобы выполнить запрос к базе, сначала следует создать экземпляр класса QSqlQuery . Для этого используется один из следующих форматов вызова его конструктора: QSqlQuery([ QSqlQuery( QSqlQuery( Первый формат позволяет сразу задать SQL-код, который следует выполнить, и немедленно запустить его на исполнение. Необязательный параметр db задает соединение с базой дан- ных, запрос к которой следует выполнить, — если он не указан, будет использоваться со- единение по умолчанию. Второй формат создает пустой запрос, не содержащий ни SQL-кода, ни каких-либо прочих параметров, но позволяющий указать соединение к нужной базе данных. Третий запрос создает копию запроса, переданного в параметре. Для выполнения запросов используются следующие методы класса QSqlQuery : exec( — немедленно выполняет переданный в параметре SQL-код. Если по- следний был успешно выполнен, возвращает True и переводит запрос в активное состоя- ние, в противном случае возвращает False . Пример использования этого метода показан в листинге 23.2. Листинг 23.2. Использование метода exec() from PyQt5 import QtWidgets, QtSql import sys app = QtWidgets.QApplication(sys.argv) con = QtSql.QSqlDatabase.addDatabase('QSQLITE') con.setDatabaseName('data.sqlite') con.open() # Проверяем, есть ли в базе данных таблица good, и, если таковой нет, # создаем ее SQL-командой CREATE TABLE if 'good' not in con.tables(): query = QtSql.QSqlQuery() query.exec("create table good(id integer primary key autoincrement, goodname text, goodcount integer) ") con.close() Глава 23. Работа с базами данных 539 С ОВЕТ Метод exec() следует использовать в тех случаях, если SQL-запрос не принимает пара- метров. В противном случае рекомендуется применять методы, рассмотренные далее. prepare( — подготавливает SQL-запрос к выполнению. Применяется, если SQL-запрос содержит параметры. Параметры в коде запроса могут быть заданы либо в стиле ODBC (вопросительными знаками), либо в стиле Oracle (символьными обозна- чениями, предваренными знаком двоеточия). Метод возвращает True , если SQL-запрос был успешно подготовлен, и False — в противном случае; exec_() — выполняет подготовленный ранее запрос. Возвращает True , если запрос был успешно выполнен, и False — в противном случае; addBindValue(<Значение параметра>[, paramType=In]) — задает значение очередного по счету параметра: так, первый вызов этого метода задает значение для первого параметра, второй вызов — для второго и т. д. Необязательный параметр paramType указывает тип параметра — здесь практически всегда используется атрибут In класса QSql , означаю- щий, что этот параметр служит для занесения значения в базу. В листинге 23.3 приведен пример использования методов prepare() , addBindValue() и exec_() Листинг 23.3. Использование методов prepare(), addBindValue() и exec_() from PyQt5 import QtWidgets, QtSql import sys app = QtWidgets.QApplication(sys.argv) con = QtSql.QSqlDatabase.addDatabase('QSQLITE') con.setDatabaseName('data.sqlite') con.open() query = QtSql.QSqlQuery() # Добавляем в только что созданную таблицу good запись, # используя SQL-команду INSERT query.prepare("insert into good values(null, ?, ?)") query.addBindValue('Дискета') query.addBindValue(10) query.exec_() con.close() bindValue(<Номер параметра>, <Значение параметра>[, paramType=In]) — задает зна- чение для параметра с указанным порядковым номером (листинг 23.4). Листинг 23.4. Использование метода bindValue() для задания параметров по их порядковым номерам from PyQt5 import QtWidgets, QtSql import sys app = QtWidgets.QApplication(sys.argv) con = QtSql.QSqlDatabase.addDatabase('QSQLITE') con.setDatabaseName('data.sqlite') con.open() query = QtSql.QSqlQuery() 540 Часть II. Библиотека PyQt 5 query.prepare("insert into good values(null, ?, ?)") query.bindValue(0, 'Компакт-диск') query.bindValue(1, 5) query.exec_() con.close() bindValue(<Обозначение параметра>, <Значение параметра>[, paramType=In]) — задает значение для параметра с указанным символьным обозначением (листинг 23.5). Листинг 23.5. Использование метода bindValue() для задания параметров по их символьным обозначениям from PyQt5 import QtWidgets, QtSql import sys app = QtWidgets.QApplication(sys.argv) con = QtSql.QSqlDatabase.addDatabase('QSQLITE') con.setDatabaseName('data.sqlite') con.open() query = QtSql.QSqlQuery() query.prepare("insert into good values(null, :name, :count)") query.bindValue(':name', 'Флеш-накопитель') query.bindValue(':count', 20) query.exec_() con.close() execBatch([mode=ValuesAsRows]) — если в вызове метода addBindValue() или bindValue() в качестве значения параметра был указан список, выполнит подготовленный запрос. Необязательный параметр mode позволяет указать, как будут интерпретироваться от- дельные элементы списка. В настоящее время в качестве его значения для всех форматов баз данных поддерживается лишь атрибут ValuesAsRows класса QSqlQuery , говорящий, что подготовленный запрос должен быть выполнен столько раз, сколько элементов при- сутствует в списке, при этом на каждом выполнении запроса в его код подставляется очередной элемент списка. Метод возвращает True , если запрос был успешно выполнен, и False — в противном случае. Листинг 23.6 представляет пример добавления в таблицу сразу нескольких записей с применением метода execBatch() Листинг 23.6. Использование метода execBatch() для добавления в таблицу сразу нескольких записей from PyQt5 import QtWidgets, QtSql import sys app = QtWidgets.QApplication(sys.argv) con = QtSql.QSqlDatabase.addDatabase('QSQLITE') con.setDatabaseName('data.sqlite') con.open() query = QtSql.QSqlQuery() query.prepare("insert into good values(null, :name, :count)") Глава 23. Работа с базами данных 541 lst1 = ['Бумага офисная', 'Фотобумага', 'Картридж'] lst2 = [15, 8, 3] query.bindValue(':name', lst1) query.bindValue(':count', lst2) query.execBatch() con.close() setForwardOnly(<Флаг>) — если передано значение True , по результату запроса можно будет перемещаться только «вперед», т. е. от начала к концу. Такой режим выполнения запроса существенно сокращает потребление системных ресурсов. Этот метод должен быть вызван перед выполнением запроса, который возвращает результат: query.prepare("select * from good order by goodname") query.setForwardOnly(True) query.exec_() 23.3.2. Обработка результатов выполнения запросов Если был выполнен запрос на выборку данных (SQL-команда SELECT ), следует получить результат его выполнения. Для этого мы используем методы класса QSqlQuery , описанные в этом разделе. Запрос на выборку данных поддерживает особый внутренний указатель, указывающий на запись результата, содержимое которой в настоящее время доступно для получения. Однако сразу после выполнения запроса этот указатель хранит неопределенное значение, не иден- тифицирующее никакую реальную запись. Поэтому перед собственно выборкой данных необходимо позиционировать этот указатель на нужную запись: first() — позиционирует указатель запроса на первую запись результата. Возвращает True , если позиционирование прошло успешно, и False — в противном случае; next() — позиционирует указатель запроса на следующую запись результата или на первую запись, если этот метод был вызван сразу после выполнения запроса. Возвраща- ет True , если позиционирование прошло успешно, и False — в противном случае; previous() — позиционирует указатель запроса на предыдущую запись результата или на последнюю запись, если указатель в текущий момент находится за последней записью. Возвращает True , если позиционирование прошло успешно, и False — в про- тивном случае; last() — позиционирует указатель запроса на последнюю запись результата. Возвраща- ет True , если позиционирование прошло успешно, и False — в противном случае; seek(<Номер записи>[, relative=False]) — позиционирует указатель на запись с ука- занным номером (нумерация записей начинается с нуля). Если необязательным парамет- ром relative передано значение True , то позиционирование выполняется относительно текущей записи: положительные значения вызывают смещение указателя «вперед» (к концу), а отрицательные — «назад» (к началу). Возвращает True , если позициониро- вание прошло успешно, и False — в противном случае; isValid() — возвращает True , если внутренний указатель указывает на какую-либо запись, и False , если он имеет неопределенное значение; at() — возвращает номер записи, на которую указывает внутренний указатель запроса; 542 Часть II. Библиотека PyQt 5 size() — возвращает количество записей, возвращенных в результате выполнения за- проса, или -1 , если этот запрос не выполнял выборку данных. Для собственно выборки данных следует применять описанные далее методы: value(<Индекс поля>) — возвращает значение поля текущей записи с заданным индек- сом. Поля нумеруются в том порядке, в котором они присутствуют в таблице базы или в SQL-коде запроса; value(<Имя поля>) — возвращает значение поля текущей записи с заданным именем; isNull(<Индекс поля>) — возвращает True , если в поле с указанным индексом нет зна- чения, и False — в противном случае; isNull(<Имя поля>) — возвращает True , если в поле с указанным именем нет значения, и False — в противном случае; record() — если внутренний указатель установлен на какую-либо запись, возвращает сведения об этой записи, в противном случае возвращаются сведения о самой таблице. Возвращаемым результатом является экземпляр класса QSqlRecord ; isSelect() — возвращает True , если был выполнен запрос на выборку данных, и False , если исполнялся запрос иного рода. В листинге 23.7 приведен код, извлекающий данные из таблицы good созданной ранее базы данных и выводящий их на экран. Листинг 23.7. Выборка данных из базы from PyQt5 import QtWidgets, QtSql import sys app = QtWidgets.QApplication(sys.argv) con = QtSql.QSqlDatabase.addDatabase('QSQLITE') con.setDatabaseName('data.sqlite') con.open() query = QtSql.QSqlQuery() query.exec("select * from good order by goodname") lst = [] if query.isActive(): query.first() while query.isValid(): lst.append(query.value('goodname') + ': ' + str(query.value('goodcount')) + ' шт.') query.next() for p in lst: print(p) con.close() Результат выполнения этого кода: Бумага офисная: 15 шт. Дискета: 10 шт. Картридж: 3 шт. Компакт-диск: 5 шт. Флеш-накопитель: 20 шт. Фотобумага: 8 шт. |