Обработка баз данных SQL с помощью PyQt: Основы


Содержание:


  • Подключение PyQt к базе данных SQL

  • Создание соединения с базой данных

  • Обработка нескольких соединений

  • Использование различных SQL-дайверов

  • Открытие соединения с базой данных

  • Выполнение SQL-запросов с помощью PyQt

  • Выполнение статических SQL-запросов

  • Выполнение динамических запросов: форматирование строк

  • Выполнение динамических запросов: параметры-заполнители

  • Навигация по записям в запросе

  • Закрытие и удаление подключений к базе данных

  • Отображение и редактирование данных с помощью PyQt

  • Понимание архитектуры представления модели PyQt

  • Использование стандартных классов виджетов

  • Использование классов представления и модель

  • Использование баз данных SQL в PyQt: лучшие практики

  • Заключение
  • Создание приложений, использующих базу данных SQL, является довольно распространенной задачей программирования. Базы данных SQL есть везде и имеют большую поддержку в Python. В программировании с графическим интерфейсом PyQt обеспечивает надежную и кроссплатформенную поддержку баз данных SQL, которая позволяет последовательно создавать базы данных, подключаться к ним и управлять ими.

    Поддержка SQL PyQt полностью интегрируется с архитектурой Model-View, чтобы помочь вам в процессе создания приложений баз данных.

    В этом уроке вы узнаете, как:

  • Использовать поддержку SQL PyQt для надежного подключения к базе данных;
  • Выполнять SQL-запросы к базе данных с помощью PyQt;
  • Использовать архитектуру представления модели PyQt в приложениях баз данных;
  • Отображать и редактировать данные с помощью различных виджетов PyQt.
  • В этом учебники примеры требуют базовых знаний языка SQL, особенно системы управления базами данных SQLite. Так же будут полезны некоторые знания о программировании GUI с помощью Python и PyQt.

    Подключение PyQt к базе данных SQL

    Подключение приложения к связующей базе данных и получение приложения для создания, чтения, обновления и удаления данных, хранящихся в этой базе данных, является обычной задачей в программировании. Связующие базы данных обычно организованы в набор таблиц или отношений. Данная строка в таблице называется записью или кортежем, а столбец-атрибутом.

    Примечание: Поле термина обычно используется для идентификации отдельного фрагмента данных, хранящихся в ячейке данной записи таблицы. С другой стороны, термин "имя поля" используется для обозначения имени столбца.

    В каждом столбце хранится определенная информация, например имена, даты или цифры. Каждая строка представляет собой набор тесно связанных данных, и каждая строка имеет одну и ту же общую структуру. Например, в базе данных, хранящей данные о сотрудниках компании, конкретная строка представляет отдельного сотрудника.

    Большинство систем реляционных баз данных используют SQL (Structured Query Language) для запроса, обработки и обслуживания данных, хранящихся в базе данных. SQL - это декларативный и предметно-ориентированный язык программирования, специально разработанный для взаимодействия с базами данных.

    В настоящее время широко используются реляционные системы баз данных и SQL. Вы найдете несколько различных систем управления базами данных, таких как SQLite, PostgreSQL, MySQL, MariaDB и многие другие. Вы можете подключить Python к любой из этих систем баз данных, используя специальную библиотеку Python SQL.

    Примечание: несмотря на то, что встроенная поддержка SQL PyQt является предпочтительным вариантом управления базами данных SQL в PyQt, вы также можете использовать любую другую библиотеку для обработки подключения к базе данных. Некоторые из этих библиотек включают SQLAlchemy, pandas, SQLite и так далее.
    Однако использование другой библиотеки для управления базами данных имеет некоторые недостатки. Вы не сможете воспользоваться преимуществами интеграции между классами SQL PyQt и архитектурой Model-View. Кроме того, вы добавите дополнительные зависимости в свое приложение.

    Когда дело доходит до программирования GUI с помощью Python и PyQt, PyQt предоставляет надежный набор классов для работы с базами данных SQL. Этот набор классов будет вашим лучшим союзником, когда вам нужно будет подключить ваше приложение к базе данных SQL.

    Примечание: к сожалению, официальная документация PyQt5 содержит несколько неполных разделов. Чтобы обойти эту проблему, вы можете ознакомиться с документацией PyQt4, документацией Qt For Python или оригинальной документацией Qt. В этом учебнике некоторые ссылки приведут вас к оригинальной документации Qt, которая в большинстве случаев является лучшим источником информации.

    В этом уроке вы узнаете основы использования поддержки SQL PyQt для создания графических приложений, которые надежно взаимодействуют с реляционными базами данных для чтения, записи, удаления и отображения данных.

    Создание соединения с базой данных

    Подключение ваших приложений к физической базе данных SQL - важный шаг в процессе разработки приложений баз данных с помощью PyQt. Для успешного выполнения этого шага вам потребуется некоторая общая информация о том, как настроена ваша база данных.

    Например, вам нужно знать, на какой системе управления базой данных построена ваша база данных, и вам также может потребоваться имя пользователя, пароль, имя хоста и так далее.

    В этом руководстве вы будете использовать SQLite 3 , хорошо протестированную систему баз данных с поддержкой всех платформ и минимальными требованиями к конфигурации. SQLite позволяет вам читать и писать непосредственно в базы данных на вашем локальном диске без необходимости в отдельном серверном процессе. Это делает его удобным вариантом для обучения разработке приложений для баз данных.

    Еще одним преимуществом использования SQLite является то, что библиотека поставляется с Python, а также с PyQt, поэтому вам не нужно ничего устанавливать, чтобы начать с ней работать.

    В PyQt вы можете создать соединение с базой данных с помощью QSqlDatabaseкласса. Этот класс представляет соединение и предоставляет интерфейс для доступа к базе данных. Чтобы создать соединение, просто позвоните .addDatabase()на QSqlDatabase. Этот статический метод принимает драйвер SQL и необязательное имя соединения в качестве аргументов и возвращает соединение с базой данных:

    QSqlDatabase.addDatabase(
        driver, connectionName=QSqlDatabase.defaultConnection
    )
    

    Первый аргумент, драйвер, является обязательным аргументом, который содержит строку, содержащую имя драйвера SQL, поддерживаемого PyQt. Второй аргумент, connectionName, является необязательным аргументом, который содержит строку с именем соединения. Имя соединения по умолчанию имеет значение QSqlDatabase.DefaultConnection, которое обычно содержит строку "qt_sql_default_connection".

    Если у вас уже есть соединение с именем connectionName, то это соединение удаляется и заменяется новым соединением, а .addDatabase() возвращает только что добавленное соединение с базой данных обратно вызывающему объекту.

    Вызов функции .addDatabase() добавляет соединение с базой данных в список доступных соединений. Этот список представляет собой глобальный реестр, который PyQt поддерживает за кулисами, чтобы отслеживать доступные соединения в приложении. Регистрация соединений с помощью значимого имени соединения позволит вам управлять несколькими соединениями в приложении базы данных.

    После того как вы создали соединение, вам может потребоваться установить для него несколько атрибутов. Конкретный набор атрибутов будет зависеть от используемого вами драйвера. В общем случае вам нужно будет установить такие атрибуты, как имя базы данных, имя пользователя и пароль для доступа к базе данных.

    Вот краткое описание методов setter, которые можно использовать для установки наиболее часто используемых атрибутов или свойств подключения к базе данных:

    Метод Описание
    .setDatabaseName(name) Устанавливает имя базы данных в name, которое представляет собой строку, представляющую допустимое имя базы данных
    .setHostName(host) Устанавливает имя хоста в host, которое представляет собой строку, представляющую допустимое имя хоста
    .setUserName(username) Устанавливает имя пользователя в username, которое представляет собой строку, представляющую допустимое имя пользователя
    .setPassword(password) Устанавливает пароль в password, который представляет собой строку, представляющую действительный пароль

    Обратите внимание, что пароль, который вы передаете в качестве аргумента .set Password (), хранится в виде обычного текста и может быть получен позже с помощью вызова .password(). Это серьезная угроза безопасности, которую вы должны избегать вводить в свои приложения баз данных. Вы узнаете более безопасный подход в разделе Открытие соединения с базой данных позже в этом руководстве.

    Чтобы создать соединение с базой данных SQLite с помощью QSqlDatabase, откройте интерактивный сеанс Python и введите следующий код:

    >>> from PyQt5.QtSql import QSqlDatabase
    
    >>> con = QSqlDatabase.addDatabase("QSQLITE")
    >>> con.setDatabaseName("contacts.sqlite")
    
    >>> con
    
    
    >>> con.databaseName()
    'contacts.sqlite'
    
    >>> con.connectionName()
    'qt_sql_default_connection'
    

    Этот код создаст объект подключения к базе данных, используя "QSQLITE" в качестве драйвера соединения и "contacts.sqlite" в качестве имени базы данных соединения. Поскольку вы не передаете имя соединения в .addDatabase(), вновь созданное соединение становится вашим соединением по умолчанию, имя которого "qt_sql_default_connection".

    В случае баз данных SQLite имя базы данных обычно является именем файла или путем, включающим имя файла базы данных. Вы также можете использовать специальное имя ":memory:" для базы данных в памяти.

    Обработка нескольких соединений

    Могут возникнуть ситуации, когда вам потребуется использовать несколько подключений к одной базе данных. Например, вы можете захотеть регистрировать взаимодействие пользователей с базой данных, используя определенное соединение для каждого пользователя.

    В других ситуациях может потребоваться подключить приложение к нескольким базам данных. Например, вам может потребоваться подключиться к нескольким удаленным базам данных, чтобы собрать данные для заполнения или обновления локальной базы данных.

    Чтобы справиться с этими ситуациями, вы можете указать конкретные имена для различных соединений и ссылаться на каждое соединение по его имени. Если вы хотите присвоить соединению с базой данных имя, то передайте это имя в качестве второго аргумента в файл .add Database():

    >>> from PyQt5.QtSql import QSqlDatabase
    
    >>> # First connection
    >>> con1 = QSqlDatabase.addDatabase("QSQLITE", "con1")
    >>> con1.setDatabaseName("contacts.sqlite")
    
    >>> # Second connection
    >>> con2 = QSqlDatabase.addDatabase("QSQLITE", "con2")
    >>> con2.setDatabaseName("contacts.sqlite")
    
    >>> con1
    
    >>> con2
    
    
    >>> con1.databaseName()
    'contacts.sqlite'
    >>> con2.databaseName()
    'contacts.sqlite'
    
    >>> con1.connectionName()
    'con1'
    >>> con2.connectionName()
    'con2'
    

    Здесь вы создаете два разных соединения с одной и той же базой данных contacts.sqlite. Каждое соединение имеет свое собственное имя. Вы можете использовать имя соединения, чтобы получить ссылку на конкретное соединение в любое время позже в вашем коде в соответствии с вашими потребностями. Для этого можно вызвать функцию .database() с именем соединения:

    >>> from PyQt5.QtSql import QSqlDatabase
    
    >>> db = QSqlDatabase.database("con1", open=False)
    
    >>> db.databaseName()
    'contacts.sqlite'
    >>> db.connectionName()
    'con1'
    

    В этом примере вы видите, что .database() принимает два аргумента:

  • 1.connectionName содержит имя соединения, которое вам нужно использовать. Если вы не передадите имя соединения, то будет использоваться соединение по умолчанию.
  • 2.open содержит логическое значение, которое сообщает .database (), хотите ли вы автоматически открыть соединение или нет. Если open имеет значение True (по умолчанию) и соединение не открыто, то соединение открывается автоматически.
  • Возвращаемое значение .database() является ссылкой на объект соединения, называемый connectionName. Вы можете использовать различные имена соединений для получения ссылок на определенные объекты соединений, а затем использовать их для управления базой данных.

    Использование различных SQL-дайверов

    До сих пор вы научились создавать подключение к базе данных с помощью драйвера SQLite. Это не единственный драйвер, доступный в PyQt. Библиотека предоставляет богатый набор драйверов SQL, которые позволяют использовать различные типы систем управления базами данных в соответствии с вашими конкретными потребностями:

    Имя Система управления базами данных
    QDB2 IBM Db2 (version 7.1 and above)
    QIBASE Borland InterBase
    QMYSQL/MARIADB MySQL or MariaDB (version 5.0 and above)
    QOCI Oracle Call Interface
    QODBC Open Database Connectivity (ODBC)
    QPSQL PostgreSQL (versions 7.3 and above)
    QSQLITE2 SQLite 2 (obsolete since Qt 5.14)
    QSQLITE SQLite 3
    QTDS Sybase Adaptive Server (obsolete since Qt 4.7)

    Столбец Имя драйвера содержит строки идентификаторов, которые необходимо передать в .add Database() в качестве первого аргумента для использования связанного драйвера. В отличие от драйвера SQLite, при использовании другого драйвера вам может потребоваться установить несколько атрибутов, таких как databaseName, hostName, userName, и password, чтобы соединение работало правильно.

    Драйверы баз данных являются производными от QSqlDriver. Вы можете создать свои собственные драйверы баз данных, создав подкласс QSqlDriver, но эта тема выходит за рамки данного руководства. Если вы заинтересованы в создании собственных драйверов баз данных, то ознакомьтесь с более подробной информацией о том, как написать собственный драйвер базы данных.

    Открытие соединения с базой данных

    Как только у вас есть подключение к базе данных, вам нужно открыть это соединение, чтобы иметь возможность взаимодействовать с вашей базой данных. Для этого вы вызываете функцию .open() для объекта .open() имеет следующие два варианта:

  • 1. .open() открывает соединение с базой данных с использованием текущих значений соединения.
  • 2. .open(username, password) открывает соединение с базой данных с использованием предоставленных имени пользователя и пароля.
  • Оба варианта возвращают True, если соединение успешно. В противном случае они возвращают False. Если соединение не удается установить, то вы можете позвонить .lastError (), чтобы получить информацию о том, что произошло. Эта функция возвращает информацию о последней ошибке, сообщенной базой данных.

    Примечание: Как вы уже узнали, .SetPassword(password) хранит пароли в виде обычного текста, что представляет собой угрозу безопасности. С другой стороны, .open() вообще не хранит пароли. Он передает пароль непосредственно водителю при открытии соединения. После этого он сбрасывает пароль. Таким образом, использование .open() для управления вашими паролями-это правильный путь, если вы хотите предотвратить проблемы безопасности.

    Вот пример того, как открыть соединение с базой данных SQLite с помощью первого варианта .open():

    >>> from PyQt5.QtSql import QSqlDatabase
    
    >>> # Create the connection
    >>> con = QSqlDatabase.addDatabase("QSQLITE")
    >>> con.setDatabaseName("contacts.sqlite")
    
    >>> # Open the connection
    >>> con.open()
    True
    >>> con.isOpen()
    True
    

    В приведенном выше примере сначала создается соединение с базой данных SQLite и открывается это соединение с помощью функции .open(). Поскольку .open() возвращает True, соединение успешно. На этом этапе вы можете проверить соединение с помощью функции .isOpen (), которая возвращает True, если соединение открыто, и False в противном случае.

    Примечание: Если вы вызываете .open() в соединении, использующем драйвер SQLite, а файл базы данных не существует, то новый и пустой файл базы данных будет создан автоматически.

    В реальных приложениях вам нужно убедиться, что у вас есть действительное соединение с базой данных, прежде чем пытаться выполнять какие-либо операции с вашими данными. В противном случае ваше приложение может сломаться и выйти из строя. Например, что делать, если у вас нет прав на запись для каталога, в котором вы пытаетесь создать этот файл базы данных? Вам нужно убедиться, что вы обрабатываете любую ошибку, которая может возникнуть при открытии соединения.

    Распространенный способ вызова функции .open() заключается в том, чтобы обернуть ее в условный оператор. Это позволяет обрабатывать ошибки, которые могут возникнуть при открытии соединения:

    >>> import sys
    >>> from PyQt5.QtSql import QSqlDatabase
    
    >>> # Create the connection
    >>> con = QSqlDatabase.addDatabase("QSQLITE")
    >>> con.setDatabaseName("contacts.sqlite")
    
    >>> # Open the connection and handle errors
    >>> if not con.open():
    ...     print("Unable to connect to the database")
    ...     sys.exit(1)
    

    Обертывание вызова функции .open() в условный оператор позволяет обрабатывать любую ошибку, возникающую при открытии соединения. Таким образом, вы можете информировать своих пользователей о любых проблемах до запуска приложения. Обратите внимание, что приложение завершает работу со статусом выхода 1, который обычно используется для обозначения сбоя программы.

    В приведенном выше примере вы используете .open() в интерактивном сеансе, поэтому вы используете print() для представления сообщений об ошибках пользователям. Однако в графических приложениях вместо print () обычно используется объект QMessageBox. С помощью QMessageBox вы можете создавать небольшие диалоги для представления информации своим пользователям.

    Вот пример графического интерфейса приложения, который иллюстрирует способ обработки ошибок подключения:

    import sys
    
    from PyQt5.QtSql import QSqlDatabase
    from PyQt5.QtWidgets import QApplication, QMessageBox, QLabel
    
    # Create the connection
    con = QSqlDatabase.addDatabase("QSQLITE")
    con.setDatabaseName("/home/contacts.sqlite")
    
    # Create the application
    app = QApplication(sys.argv)
    
    # Try to open the connection and handle possible errors
    if not con.open():
        QMessageBox.critical(
            None,
            "App Name - Error!",
            "Database Error: %s" % con.lastError().databaseText(),
        )
        sys.exit(1)
    
    # Create the application's window
    win = QLabel("Connection Successfully Opened!")
    win.setWindowTitle("App Name")
    win.resize(200, 100)
    win.show()
    sys.exit(app.exec_())
    

    Оператор if в строке 14 проверяет, было ли соединение неудачным. Если каталог /home/ не существует или у вас нет разрешения на запись в него, то вызов функции .open() завершается неудачей, поскольку файл базы данных не может быть создан. В этой ситуации поток выполнения входит в блок кода оператора if и показывает сообщение на экране.

    Если вы измените путь к любому другому каталогу, в котором вы можете писать, то вызов функции .open() будет успешным, и вы увидите окно, отображающее сообщение Соединение успешно открыто! У вас также будет новый файл базы данных под названием contacts.sqlite в выбранном каталоге.

    Обратите внимание, что вы передаете None в качестве родителя сообщения, потому что на момент отображения сообщения вы еще не создали окно, поэтому у вас нет жизнеспособного родителя для окна сообщения.

    Выполнение SQL-запросов с помощью PyQt

    С полностью функциональным подключением к базе данных вы готовы начать работу с вашей базой данных. Для этого можно использовать строковые SQL-запросы и объекты QSqlQuery<>. QSqlQuery позволяет запускать любые SQL-запросы в вашей базе данных. С помощью QSqlQuery можно выполнять операторы языка обработки данных (DML), такие как SELECT, INSERT, UPDATE и DELETE, а также операторы языка определения данных (DDL), такие как CREATE TABLE и т.д.

    Конструктор QSqlQuery имеет несколько вариаций, но в этом уроке вы узнаете о двух из них:

  • 1.QSqlQuery(query, connection) создает объект запроса, используя строковый SQL-запрос и соединение с базой данных. Если вы не указали соединение или если указанное соединение является недопустимым, то используется соединение с базой данных по умолчанию. Если запрос не является пустой строкой, то он будет выполнен сразу же.
  • 2.QSqlQuery(connection) создает объект запроса с помощью соединения. Если соединение недопустимо, то используется соединение по умолчанию.
  • Вы также можете создавать объекты QSqlQuery без передачи каких-либо аргументов конструктору. В этом случае запрос будет использовать соединение с базой данных по умолчанию, если таковое имеется.

    Чтобы выполнить запрос, вам нужно вызвать .exec() для объекта запроса. Вы можете использовать .exec() двумя различными способами:

  • 1..exec(query) выполняет строковый SQL-запрос, содержащийся в запросе. Он возвращает True, если запрос был успешным, и в противном случае возвращает False.
  • 2..exec() выполняет ранее подготовленный SQL-запрос. Он возвращает True, если запрос был успешным, и в противном случае возвращает False.
  • Примечание: PyQt также реализует вариации QSqlQuery.exec() с именем .exec_(). Они обеспечивают обратную совместимость со старыми версиями Python, в которых exec был ключевым словом языка.

    Теперь, когда вы знаете основы использования QSqlQuery для создания и выполнения SQL-запросов, вы готовы научиться применять свои знания на практике.

    Выполнение статических SQL-запросов

    Чтобы начать создавать и выполнять запросы с помощью PyQt, вы запустите свой любимый редактор кода или IDE и создадите скрипт Python под названием queries.py. Сохраните скрипт и добавьте в него следующий код:

    import sys
    
    from PyQt5.QtSql import QSqlDatabase, QSqlQuery
    
    # Create the connection
    con = QSqlDatabase.addDatabase("QSQLITE")
    con.setDatabaseName("contacts.sqlite")
    
    # Open the connection
    if not con.open():
        print("Database Error: %s" % con.lastError().databaseText())
        sys.exit(1)
    
    # Create a query and execute it right away using .exec()
    createTableQuery = QSqlQuery()
    createTableQuery.exec(
        """
        CREATE TABLE contacts (
            id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL,
            name VARCHAR(40) NOT NULL,
            job VARCHAR(50),
            email VARCHAR(40) NOT NULL
        )
        """
    )
    
    print(con.tables())
    

    В этом сценарии вы начинаете с импорта модулей и классов, с которыми собираетесь работать. Затем вы создаете соединение с базой данных с помощью функции .add Database() с драйвером SQLite. Вы устанавливаете имя базы данных на "contacts.sqlite" и открываете соединение.

    Чтобы создать свой первый запрос, вы создаете экземпляр QSqlQuery без каких-либо аргументов. Имея объект запроса на месте, вы вызываете .exec(), передавая строковый SQL-запрос в качестве аргумента. Этот тип запроса известен как статический запрос, потому что он не получает никаких параметров извне запроса.

    Приведенный выше SQL-запрос создает новую таблицу под названием contacts в вашей базе данных. Эта таблица будет иметь следующие четыре столбца:

    Колонка Содержание
    id An integer with the table’s primary key
    name Строка с именем контакта
    job Строка с названием должности контакта
    email Строка с адресом электронной почты контакта

    Последняя строка в приведенном выше скрипте выводит список таблиц, содержащихся в вашей базе данных. Если вы запустите скрипт, то заметите, что в вашем текущем каталоге создан новый файл базы данных contacts.sqlite. Вы также получите что-то вроде ['contacts', 'sqlite_sequence'], напечатанного на вашем экране. Этот список содержит имена таблиц в вашей базе данных.

    Примечание: Строковый SQL-запрос должен использовать соответствующий синтаксис в соответствии с конкретной базой данных SQL, которую вы запрашиваете. Если синтаксис неверен, то .exec() игнорирует запрос и возвращает False.

    В случае SQLite запрос может содержать только один оператор одновременно.

    Вызов .exec() для объекта QSqlQuery-это распространенный способ немедленного выполнения строковых SQL-запросов в ваших базах данных, но что делать, если вы хотите заранее подготовить свои запросы для последующего выполнения? Это тема следующего раздела.

    Выполнение динамических запросов: Форматирование строк

    До сих пор вы научились выполнять статические запросы к базе данных. Статические запросы-это те, которые не принимают параметры, поэтому запрос выполняется как есть. Несмотря на то, что эти запросы довольно полезны, иногда вам нужно создавать запросы, которые извлекают данные в ответ на определенные входные параметры.

    Запросы, принимающие параметры во время выполнения, называются динамическими запросами. Использование параметров позволяет точно настроить запрос и получить данные в ответ на определенные значения параметров. Разные значения приведут к разным результатам. Вы можете использовать входные параметры в запросе, используя один из следующих двух подходов:

  • 1.Постройте запрос динамически, используя форматирование строк для интерполяции значений параметров.
  • 2.Подготовьте запрос с помощью параметров-заполнителей, а затем свяжите конкретные значения с параметрами.
  • Первый подход позволяет быстро создавать динамические запросы. Однако, чтобы безопасно использовать этот подход, вы должны быть уверены, что ваши значения параметров поступают из надежного источника. В противном случае вы можете столкнуться с атаками SQL - инъекций.

    Вот пример того, как использовать форматирование строк для создания динамических запросов в PyQt:

    >>> from PyQt5.QtSql import QSqlQuery, QSqlDatabase
    
    >>> con = QSqlDatabase.addDatabase("QSQLITE")
    >>> con.setDatabaseName("contacts.sqlite")
    >>> con.open()
    True
    
    >>> name = "Linda"
    >>> job = "Technical Lead"
    >>> email = ""
    
    >>> query = QSqlQuery()
    >>> query.exec(
    ...     f"""INSERT INTO contacts (name, job, email)
    ...     VALUES ('{name}', '{job}', '{email}')"""
    ... )
    True
    

    В этом примере <strong>f-строка используется для создания динамического запроса путем интерполяции определенных значений в строковый SQL-запрос. Последний запрос вставляет данные в вашу таблицу контактов, которая теперь содержит данные о Linda.

    Примечание: Далее в этом руководстве вы увидите, как извлекать и перемещаться по данным, хранящимся в базе данных.

    Обратите внимание, что для работы такого динамического запроса необходимо убедиться, что вставляемые значения имеют правильный тип данных. Таким образом, вы используете одинарные кавычки вокруг заполнителя в f-строке, потому что эти значения должны быть строками.

    Выполнение динамических запросов: Параметры-заполнители

    Второй подход к выполнению динамических запросов требует предварительной подготовки запросов с использованием шаблона с заполнителями для параметров. PyQt поддерживает два стиля заполнителей параметров:

  • 1.Стиль Oracle использует именованные заполнители, такие как :name или :email.
  • 2.Стиль ODBCиспользует знак вопроса (?) в качестве позиционного заполнителя.
  • Обратите внимание, что эти стили не могут быть смешаны в одном запросе. Вы можете ознакомиться с Подходами к привязке значений для получения дополнительных примеров использования заполнителей.
    Примечание: ODBC означает Открытое подключение к базе данных.

    Чтобы создать такой динамический запрос в PyQt, вы сначала создаете шаблон с заполнителем для каждого параметра запроса, а затем передаете этот шаблон в качестве аргумента в функцию .prepare(), которая анализирует, компилирует и подготавливает шаблон запроса к выполнению. Если шаблон имеет какие-либо проблемы, такие как синтаксическая ошибка SQL, то .prepare() не удается скомпилировать шаблон и возвращает False.

    Если процесс подготовки проходит успешно, то функция prepare() возвращает True. После этого вы можете передать определенное значение каждому параметру, используя .bindValue() с именованными или позиционными параметрами или используя .addBindValue() с позиционными параметрами. .bindValue() имеет следующие два варианта:

  • 1..bindValue(placeholder, val)
  • 2..bindValue(pos, val)
  • В первом варианте заполнитель представляет собой заполнитель в стиле Oracle. Во втором варианте pos представляет собой целое число на основе нуля с позицией параметра в запросе. В обоих вариантах val содержит значение, которое должно быть привязано к определенному параметру.

    .addBindValue() добавляет значение в список заполнителей с помощью позиционной привязки. Это означает, что порядок вызовов функции .addBindValue() определяет, какое значение будет привязано к каждому параметру-заполнителю в подготовленном запросе.

    Чтобы начать использовать подготовленные запросы, вы можете подготовить инструкцию INSERT INTO SQL для заполнения базы данных некоторыми образцами данных. Вернитесь к скрипту, созданному в разделе Выполнение статических SQL-запросов, и добавьте следующий код сразу после вызова функции print():

    # Creating a query for later execution using .prepare()
    insertDataQuery = QSqlQuery()
    insertDataQuery.prepare(
        """
        INSERT INTO contacts (
            name,
            job,
            email
        )
        VALUES (?, ?, ?)
        """
    )
    
    # Sample data
    data = [
        ("Joe", "Senior Web Developer", ""),
        ("Lara", "Project Manager", ""),
        ("David", "Data Analyst", ""),
        ("Jane", "Senior Python Developer", ""),
    ]
    
    # Use .addBindValue() to insert data
    for name, job, email in data:
        insertDataQuery.addBindValue(name)
        insertDataQuery.addBindValue(job)
        insertDataQuery.addBindValue(email)
        insertDataQuery.exec()
    

    Первым шагом является создание объекта QSqlQuery. Затем вы вызываете функцию .prepare() для объекта запроса. В этом случае для заполнителей используется стиль ODBC. Ваш запрос будет принимать значения для name, job и email, поэтому вам понадобятся три заполнителя. Поскольку столбец id является автоинкрементированным целым числом, вам не нужно вводить для него значения.

    Затем вы создаете некоторые образцы данных для заполнения базы данных. Данные содержат список кортежей, и каждый кортеж содержит три элемента: name, job и email каждого контакта.

    Последний шаг-привязать значения, которые вы хотите передать каждому заполнителю, а затем вызвать .exec() для выполнения запроса. Для этого вы используете цикл for. Заголовок цикла распаковывает каждый кортеж в данных на три отдельные переменные с удобными именами. Затем вы вызываете функцию .addBindValue() для объекта запроса, чтобы привязать значения к заполнителям.

    Обратите внимание, что вы используете позиционные заполнители, поэтому порядок, в котором вы вызываете .addBindValue (), будет определять порядок, в котором каждое значение передается соответствующему заполнителю.

    Этот подход к созданию динамических запросов удобен, когда вы хотите настроить свои запросы, используя значения, полученные из входных данных пользователя. Всякий раз, когда вы принимаете ввод пользователя для выполнения запроса к базе данных, вы сталкиваетесь с угрозой безопасности SQL-инъекции.

    В PyQt объединение .prepare (), .bindValue () и .addBindValue() полностью защищает вас от атак SQL-инъекций, так что это правильный путь, когда вы принимаете ненадежные входные данные для завершения своих запросов.

    Навигация по записям в запросе

    Если вы выполните SELECT оператор, ваш QSqlQuery объект получит ноль или несколько записей из одной или нескольких таблиц в вашей базе данных. Запрос будет содержать записи, содержащие данные, соответствующие критериям запроса. Если никакие данные не соответствуют критериям, ваш запрос будет пустым.

    QSqlQuery предоставляет набор методов навигации , которые можно использовать для перемещения по записям в результате запроса:

    Методика Извлечение
    .next() Следующая запись
    .previous() Предыдущая запись
    .first() Первая запись
    .last() Последняя запись
    .seek(index, relative=False) Рекорд на позиции index

    Все эти методы помещают объект запроса в полученную запись, если эта запись доступна. Большинство из этих методов имеют определенные правила, которые применяются при их использовании. С помощью этих методов вы можете перемещаться вперед, назад или произвольно по записям в результате запроса. Поскольку все они возвращают либо True или False, вы можете использовать их в цикле while для навигации по всем записям за один раз.

    Эти методы работают с активными запросами . Запрос активен, если вы успешно его выполнили .exec(), но запрос еще не завершен. Как только активный запрос находится в допустимой записи, вы можете получить данные из этой записи с помощью .value(index). Этот метод принимает целое число, отсчитываемое от нуля index, и возвращает значение по этому индексу (столбцу) в текущей записи.

    Примечание. Если вы выполните SELECT* тип запроса, то столбцы в результате не будут следовать известному порядку. Это может вызвать проблемы при использовании .value() для получения значения в заданном столбце, потому что нет способа узнать, используете ли вы правильный индекс столбца.

    Вы увидите несколько примеров того, как использовать некоторые методы навигации для перемещения по запросу ниже. Но сначала вам нужно создать соединение с вашей базой данных:

    >>> from PyQt5.QtSql import QSqlDatabase, QSqlQuery
    
    >>> con = QSqlDatabase.addDatabase("QSQLITE")
    >>> con.setDatabaseName("contacts.sqlite")
    >>> con.open()
    True
    

    Здесь вы создаете и открываете новое соединение с contacts.sqlite. Если вы до сих пор следовали этому руководству, то эта база данных уже содержит некоторые образцы данных. Теперь вы можете создать QSqlQuery объект и выполнить его с этими данными:

    >>> # Create and execute a query
    >>> query = QSqlQuery()
    >>> query.exec("SELECT name, job, email FROM contacts")
    True
    

    Этот запрос возвращает данные о name, job и email все контакты , хранящиеся в contacts таблице. После .exec() возврата True запрос был успешным и теперь является активным запросом. Вы можете перемещаться по записям в этом запросе, используя любой из методов навигации, которые вы видели ранее. Вы также можете получить данные в любом столбце записи, используя .value():

    >>> # First record
    >>> query.first()
    True
    
    >>> # Named indices for readability
    >>> name, job, email = range(3)
    
    >>> # Retrieve data from the first record
    >>> query.value(name)
    'Linda'
    
    >>> # Next record
    >>> query.next()
    True
    >>> query.value(job)
    'Senior Web Developer'
    
    >>> # Last record
    >>> query.last()
    True
    >>> query.value(email)
    ''
    

    С помощью методов навигации вы можете перемещаться по результату запроса. С помощью .value() вы можете получить данные в любом столбце данной записи.

    Вы также можете перебирать все записи в своем запросе, используя while цикл вместе с .next():

    >>> query.exec()
    True
    
    >>> while query.next():
    ...     print(query.value(name), query.value(job), query.value(email))
    ...
    Linda Technical Lead 
    Joe Senior Web Developer 
    ...
    

    С помощью .next() вы перемещаетесь по всем записям в результате запроса. .next() работает аналогично протоколу итератора в Python. После того, как вы перебрали записи в результате запроса, .next() начинает возвращаться, False пока вы не запустите .exec() снова. Вызов .exec() извлекает данные из базы данных и помещает внутренний указатель объекта запроса на одну позицию перед первой записью, поэтому при вызове .next() вы снова получаете первую запись.

    Вы также можете выполнить цикл в обратном порядке, используя .previous():

    >>> while query.previous():
    ...     print(query.value(name), query.value(job), query.value(email))
    ...
    Jane Senior Python Developer 
    David Data Analyst 
    ...
    

    .previous() работает аналогично .next(), но итерация выполняется в обратном порядке. Другими словами, цикл переходит от позиции указателя запроса обратно к первой записи.

    Иногда вам может потребоваться получить индекс, который идентифицирует данный столбец в таблице, используя имя этого столбца. Для этого вы можете вызвать .indexOf() возвращаемое значение .record():

    >>> query.first()
    True
    
    >>> # Get the index of name
    >>> name = query.record().indexOf("name")
    
    >>> query.value(name)
    'Linda'
    
    >>> # Finish the query object if unneeded
    >>> query.finish()
    >>> query.isActive()
    False
    

    Обращение к .indexOf() результату .record() возвращает индекс "name" столбца. Если "name" не существует, то .indexOf() возвращается -1. Это удобно, когда вы используете SELECT* оператор, в котором порядок столбцов неизвестен. Наконец, если вы закончили с объектом запроса, вы можете отключить его, вызвав .finish(). Это освободит системную память, связанную с рассматриваемым объектом запроса.

    Закрытие и удаление подключений к базе данных

    На практике некоторые из ваших приложений PyQt будут зависеть от базы данных, а другие - нет. Приложение, зависящее от базы данных, часто создает и открывает соединение с базой данных непосредственно перед созданием любого окна или графического компонента и сохраняет соединение открытым до закрытия приложения.

    С другой стороны, приложения, которые не зависят от базы данных, но используют базу данных для предоставления некоторых из своих функций, обычно подключаются к этой базе данных только при необходимости, если вообще. В этих случаях вы можете закрыть соединение после использования и освободить ресурсы, связанные с этим соединением, например системную память.

    Чтобы закрыть соединение в PyQt, вы вызываете .close() соединение. Этот метод закрывает соединение и освобождает все полученные ресурсы. Это также делает недействительными любые связанные QSqlQuery объекты, потому что они не могут работать должным образом без активного соединения.

    Вот пример того, как закрыть активное соединение с базой данных, используя .close():

    >>> from PyQt5.QtSql import QSqlDatabase
    
    >>> con = QSqlDatabase.addDatabase("QSQLITE")
    >>> con.setDatabaseName("contacts.sqlite")
    
    >>> con.open()
    True
    >>> con.isOpen()
    True
    
    >>> con.close()
    >>> con.isOpen()
    False
    

    Вы можете вызвать .close() соединение, чтобы закрыть его и освободить все связанные с ним ресурсы. Чтобы убедиться, что соединение закрыто, вы звоните .isOpen().

    Обратите внимание, что QSqlQuery объекты остаются в памяти после закрытия связанного с ними соединения, поэтому вы должны сделать свои запросы неактивными, вызвав .finish() или .clear(), или удалив QSqlQueryобъект перед закрытием соединения. В противном случае в вашем объекте запроса не будет остаточной памяти.

    Вы можете повторно открыть и повторно использовать любое ранее закрытое соединение. Это потому, .close() что не удаляет соединения из списка доступных соединений, поэтому они остаются пригодными для использования.

    Вы также можете полностью удалить соединения с базой данных, используя .removeDatabase(). Чтобы сделать это безопасно, сначала завершите свои запросы с помощью .finish(), затем закройте базу данных с помощью .close() и, наконец, удалите соединение. Вы можете использовать .removeDatabase(connectionName) для удаления вызываемого соединения connectionNameс базой данных из списка доступных соединений. Удаленные соединения больше не доступны для использования в текущем приложении.

    Чтобы удалить соединение с базой данных по умолчанию, вы можете вызвать .connectionName() возвращаемый объект .database()и передать результат в .removeDatabase():

    >>> # The connection is closed but still in the list of connections
    >>> QSqlDatabase.connectionNames()
    ['qt_sql_default_connection']
    
    >>> # Remove the default connection
    >>> QSqlDatabase.removeDatabase(QSqlDatabase.database().connectionName())
    
    >>> # The connection is no longer in the list of connections
    >>> QSqlDatabase.connectionNames()
    []
    
    >>> # Try to open a removed connection
    >>> con.open()
    False
    

    Здесь вызов .connectionNames() возвращает список доступных подключений. В этом случае у вас есть только одно соединение по умолчанию. Затем вы удаляете соединение с помощью .removeDatabase().

    Примечание. Перед закрытием и удалением подключения к базе данных необходимо убедиться, что все, что использует подключение, удалено или настроено на использование другого источника данных. В противном случае возможна утечка ресурсов .

    Поскольку вам нужно использовать имя соединения .removeDatabase(), вы вызываете .connectionName() результат, .database() чтобы получить имя соединения по умолчанию. Наконец, вы .connectionNames() снова звоните, чтобы убедиться, что соединение больше не находится в списке доступных соединений. Попытка открыть удаленное соединение вернется, False потому что соединение больше не существует.

    Отображение и редактирование данных с помощью PyQt

    Распространенным требованием для приложений с графическим интерфейсом пользователя, использующих базы данных, является возможность загружать, отображать и редактировать данные из базы данных с помощью различных виджетов. Виджеты Table, list, и tree обычно используются в графических интерфейсах для управления данными.

    PyQt предоставляет два разных типа виджетов для управления данными:

  • 1.Стандартные виджеты включают внутренние контейнеры для хранения данных.
  • 2.Виджеты просмотра не поддерживают внутренние контейнеры данных, но используют модели для доступа к данным
  • Для небольших приложений с графическим интерфейсом пользователя, которые управляют небольшими базами данных, вы можете использовать первый подход. Второй подход удобен при создании сложных приложений с графическим пользовательским интерфейсом для управления большими базами данных.

    Второй подход использует преимущества программирования PyQt Model-View . При таком подходе у вас есть виджеты, которые представляют представления, такие как таблицы, списки и деревья, с одной стороны, и классы моделей, которые взаимодействуют с вашими данными, с другой.

    Понимание архитектуры представления модели PyQt

    Model-View-Controller (MVC) шаблон проектирования является общей картиной программного обеспечения , предназначенной для разделения код приложения на три основные слои, каждый из которых имеет другую роль.

    Модель заботится о бизнес - логике приложения, то вид обеспечивает на экране представление, и контроллер подключает модель и представление , чтобы сделать работу приложения.

    Qt предоставляет собственный вариант MVC. Они называют это архитектурой модель-представление , и она также доступна для PyQt. Шаблон также разделяет логику на три компонента:

  • 1.Модели взаимодействуют с данными и получают доступ к ним. Они также определяют интерфейс, который используется представлениями и делегатами для доступа к данным. Все модели основаны на QAbstractItemModel. Некоторые часто используемые модели включают в себя QStandardItemModel, QFileSystemModel и SQL связанных моделей.
  • 2.Представления отвечают за отображение данных пользователю. Они также имеют функции, аналогичные контроллеру в шаблоне MVC. Все взгляды основаны на QAbstractItemView. Некоторые часто используемые взгляды QListView, QTableView и QTreeView.
  • 3.Делегаты рисуют элементы просмотра и предоставляют виджеты редактора для изменения элементов. Они также связываются с моделью, если элемент был изменен. Базовый класс - это QAbstractItemDelegate.
  • Разделение классов на эти три компонента подразумевает, что изменения моделей будут автоматически отражаться в связанных представлениях или виджетах, а изменения представлений или виджетов через делегатов будут автоматически обновлять базовую модель.

    Кроме того, вы можете отображать одни и те же данные в разных представлениях без необходимости использования нескольких моделей.

    Использование стандартных классов виджетов

    PyQt предоставляет набор стандартных виджетов для отображения и редактирования данных в ваших графических приложениях. Эти стандартные виджеты предоставляют такие представления, как таблицы, деревья и списки. Они также предоставляют внутренний контейнер для хранения данных и удобные делегаты для редактирования данных. Все эти функции сгруппированы в один класс.

    Вот три из этих стандартных классов:

    Стандартный класс Дисплеи
    QListWidget Список предметов
    QTreeWidget Иерархическое дерево элементов
    QTableWidget Таблица предметов

    QTableWidget пожалуй, самый популярный виджет, когда дело доходит до отображения и редактирования данных. Он создает двумерный массив QTableWidgetItem объектов. Каждый элемент содержит отдельное значение в виде строки. Все эти значения отображаются и организованы в виде таблицы строк и столбцов.

    С QTableWidget объектом можно выполнять как минимум следующие операции :

  • Редактирование содержимого его элементов с помощью объектов делегата
  • Добавление новых предметов с помощью .setItem()
  • Установка количества строк и столбцов с помощью .setRowCount() и .setColumnCount()
  • Добавление вертикальных и горизонтальных меток заголовков с помощью setHorizontalHeaderLabels() и .setVerticalHeaderLabels
  • Вот пример приложения, которое показывает, как использовать QTableWidget объект для отображения данных в графическом интерфейсе. Приложение использует базу данных, которую вы создали и заполнили в предыдущих разделах, поэтому, если вы хотите ее запустить, вам нужно сохранить код в том же каталоге, в котором у вас есть contacts.sqlite база данных:

    Если вы дважды щелкните любую ячейку таблицы, вы сможете редактировать содержимое ячейки. Однако ваши изменения не будут сохранены в вашей базе данных.

    Вот код вашего приложения:

    import sys
    
    from PyQt5.QtSql import QSqlDatabase, QSqlQuery
    from PyQt5.QtWidgets import (
        QApplication,
        QMainWindow,
        QMessageBox,
        QTableWidget,
        QTableWidgetItem,
    )
    
    class Contacts(QMainWindow):
        def __init__(self, parent=None):
            super().__init__(parent)
            self.setWindowTitle("QTableView Example")
            self.resize(450, 250)
            # Set up the view and load the data
            self.view = QTableWidget()
            self.view.setColumnCount(4)
            self.view.setHorizontalHeaderLabels(["ID", "Name", "Job", "Email"])
            query = QSqlQuery("SELECT id, name, job, email FROM contacts")
            while query.next():
                rows = self.view.rowCount()
                self.view.setRowCount(rows + 1)
                self.view.setItem(rows, 0, QTableWidgetItem(str(query.value(0))))
                self.view.setItem(rows, 1, QTableWidgetItem(query.value(1)))
                self.view.setItem(rows, 2, QTableWidgetItem(query.value(2)))
                self.view.setItem(rows, 3, QTableWidgetItem(query.value(3)))
            self.view.resizeColumnsToContents()
            self.setCentralWidget(self.view)
    
    def createConnection():
        con = QSqlDatabase.addDatabase("QSQLITE")
        con.setDatabaseName("contacts.sqlite")
        if not con.open():
            QMessageBox.critical(
                None,
                "QTableView Example - Error!",
                "Database Error: %s" % con.lastError().databaseText(),
            )
            return False
        return True
    
    app = QApplication(sys.argv)
    if not createConnection():
        sys.exit(1)
    win = Contacts()
    win.show()
    sys.exit(app.exec_())
    

    Вот что происходит в этом примере:

  • Строки 18–20 создают QTableWidget объект, устанавливают количество столбцов 4 и устанавливают удобные для пользователя метки для заголовка каждого столбца.
  • Строка 21 создает и выполняет SELECTSQL-запрос к вашей базе данных, чтобы получить все данные в contacts таблице.
  • Строка 22 запускает while цикл для навигации по записям в результате запроса с помощью .next().
  • Строка 24 увеличивает количество строк в таблице с 1 помощью .setRowCount().
  • Строки с 25 по 28 добавляют элементы данных в вашу таблицу с помощью .setItem(). Обратите внимание: поскольку значения в id столбцах являются целыми числами, вам необходимо преобразовать их в строки, чтобы иметь возможность хранить их в QTableWidgetItem объекте.
  • .setItem() принимает три аргумента:

  • 1.row содержит отсчитываемое от нуля целое число, представляющее индекс данной строки в таблице.
  • 2.column содержит целое число с отсчетом от нуля, которое представляет индекс данного столбца в таблице.
  • 3.item содержит QTableWidgetItem объект, который нужно разместить в заданной ячейке таблицы.
  • Наконец, вы вызываете .resizeColumnsToContents() свое представление, чтобы настроить размер столбцов в соответствии с их содержимым и обеспечить лучшую визуализацию данных.

    Отображение и редактирование таблиц базы данных с помощью стандартных виджетов может стать сложной задачей. Это потому, что у вас будет две копии одних и тех же данных. Другими словами, у вас будет копия данных в двух местах:

  • 1.Вне виджета в вашей базе данных
  • 2.Внутри виджета, во внутренних контейнерах виджета
  • Вы несете ответственность за синхронизацию обеих копий ваших данных вручную, что может быть раздражающей и подверженной ошибкам операцией. К счастью, вы можете использовать архитектуру PyQt Model-View, чтобы избежать большинства этих проблем, как вы увидите в следующем разделе.

    Использование классов представления и модели

    Классы PyQt Model-View устраняют проблемы дублирования и синхронизации данных, которые могут возникнуть при использовании стандартных классов виджетов для создания приложений баз данных. Архитектура модель-представление позволяет вам использовать несколько представлений для отображения одних и тех же данных, потому что вы можете передать одну модель многим представлениям.

    Классы модели предоставляют интерфейс прикладного программирования (API), который можно использовать для управления данными. Классы представления предоставляют удобные объекты-делегаты, которые можно использовать для непосредственного редактирования данных в представлении. Чтобы связать представление с данным модулем, вам необходимо вызвать .setModel() объект представления.

    PyQt предлагает набор классов представлений, которые поддерживают архитектуру модель-представление:

    Просмотр класса Дисплеи
    QListView Список элементов, которые принимают значения непосредственно из класса модели.
    QTreeView Иерархическое дерево элементов, которые принимают значения непосредственно из класса модели.
    QTableView Таблица элементов, которые принимают значения непосредственно из класса модели

    Эти классы представлений можно использовать вместе с классами моделей для создания приложений баз данных. Это сделает ваши приложения более надежными, более быстрыми для кодирования и менее подверженными ошибкам.

    Вот некоторые из классов моделей, которые PyQt предоставляет для работы с базами данных SQL:

    Модель Класс Описание
    QSqlQueryModel Модель данных только для чтения для запросов SQL
    QSqlTableModel Редактируемая модель данных для чтения и записи записей в одной таблице
    QSqlRelationalTableModel Редактируемая модель данных для чтения и записи записей в реляционной таблице

    После того, как вы подключили одну из этих моделей к физической таблице или запросу базы данных, вы можете использовать их для заполнения ваших представлений. Представления предоставляют объекты-делегаты, которые позволяют изменять данные непосредственно в представлении. Модель, подключенная к представлению, обновит данные в вашей базе данных, чтобы отразить любые изменения в представлении. Обратите внимание, что вам не нужно обновлять данные в базе данных вручную. Модель сделает это за вас.

    Вот пример, который показывает основы того, как использовать QTableView объект и QSqlTableModel объект вместе для создания приложения базы данных с использованием архитектуры PyQt Model-View:

    Чтобы отредактировать данные в ячейке таблицы, вы можете дважды щелкнуть ячейку. В ячейке появится удобный виджет делегата, позволяющий редактировать его содержимое. Затем вы можете нажать Enter чтобы сохранить изменения.

    Возможность автоматически обрабатывать и сохранять изменения в данных - одно из наиболее важных преимуществ использования классов PyQt Model-View. Архитектура Model-View повысит вашу продуктивность и уменьшит количество ошибок, которые могут возникнуть, когда вам придется самому писать код манипулирования данными.

    Вот код для создания приложения:

    import sys
    
    from PyQt5.QtCore import Qt
    from PyQt5.QtSql import QSqlDatabase, QSqlTableModel
    from PyQt5.QtWidgets import (
        QApplication,
        QMainWindow,
        QMessageBox,
        QTableView,
    )
    
    class Contacts(QMainWindow):
        def __init__(self, parent=None):
            super().__init__(parent)
            self.setWindowTitle("QTableView Example")
            self.resize(415, 200)
            # Set up the model
            self.model = QSqlTableModel(self)
            self.model.setTable("contacts")
            self.model.setEditStrategy(QSqlTableModel.OnFieldChange)
            self.model.setHeaderData(0, Qt.Horizontal, "ID")
            self.model.setHeaderData(1, Qt.Horizontal, "Name")
            self.model.setHeaderData(2, Qt.Horizontal, "Job")
            self.model.setHeaderData(3, Qt.Horizontal, "Email")
            self.model.select()
            # Set up the view
            self.view = QTableView()
            self.view.setModel(self.model)
            self.view.resizeColumnsToContents()
            self.setCentralWidget(self.view)
    
    def createConnection():
        con = QSqlDatabase.addDatabase("QSQLITE")
        con.setDatabaseName("contacts.sqlite")
        if not con.open():
            QMessageBox.critical(
                None,
                "QTableView Example - Error!",
                "Database Error: %s" % con.lastError().databaseText(),
            )
            return False
        return True
    
    app = QApplication(sys.argv)
    if not createConnection():
        sys.exit(1)
    win = Contacts()
    win.show()
    sys.exit(app.exec_())
    

    Вот что происходит в этом коде:

  • Строка 18 создает редактируемый QSqlTableModel объект.
  • Строка 19 связывает вашу модель с contacts таблицей в вашей базе данных с помощью .setTable().
  • Строка 20 устанавливает стратегию редактирования модели OnFieldChange. Эта стратегия позволяет модели автоматически обновлять данные в вашей базе данных, если пользователь изменяет какие-либо данные непосредственно в представлении.
  • Строки 21–24 устанавливают несколько удобных меток для горизонтальных заголовков модели using .setHeaderData().
  • Строка 25 загружает данные из вашей базы данных и заполняет модель путем вызова .select().
  • Строка 27 создает объект табличного представления для отображения данных, содержащихся в модели.
  • Строка 28 связывает представление с моделью, вызывая .setModel() представление с вашей моделью данных в качестве аргумента.
  • Строка 29 вызывает .resizeColumnsToContents() объект просмотра, чтобы настроить таблицу в соответствии с ее содержимым.
  • Это оно! Теперь у вас есть полнофункциональное приложение для работы с базой данных.

    Использование баз данных SQL в PyQt: лучшие практики

    Когда дело доходит до эффективного использования поддержки PyQt SQL, есть несколько передовых методов, которые вы, возможно, захотите использовать в своих приложениях:

  • Отдавайте предпочтение поддержке SQL в PyQt по сравнению со стандартной библиотекой Python или сторонними библиотеками, чтобы воспользоваться преимуществами естественной интеграции этих классов с остальными классами и инфраструктурой PyQt, в основном с архитектурой Model-View.
  • Используйте предварительно подготовленные динамические запросы с заполнителями для параметров и привяжите значения к этим параметрам с помощью .addBindValue() и .bindValue(). Это поможет предотвратить атаки с использованием SQL-инъекций.
  • Обрабатывайте ошибки, которые могут возникнуть при открытии подключения к базе данных, чтобы избежать неожиданного поведения и сбоев приложения.
  • Закройте и удалите ненужные подключения к базе данных и запросы, чтобы освободить все полученные системные ресурсы.
  • Сведите к минимуму использование SELECT* запросов, чтобы избежать проблем при извлечении данных с помощью .value().
  • Передавайте пароли по .open() адресу, а не .setPassword() по адресу, чтобы избежать риска нарушения безопасности.
  • Воспользуйтесь преимуществами архитектуры PyQt Model-View и ее интеграции с поддержкой PyQt SQL, чтобы сделать ваши приложения более надежными.
  • Этот список не полный, но он поможет вам лучше использовать поддержку PyQt SQL при разработке приложений для баз данных.

    Заключение

    Использование встроенной поддержки PyQt для работы с базами данных SQL является важным навыком для любого разработчика Python, который создает приложения PyQt с графическим интерфейсом пользователя и должен подключать их к базе данных. PyQt предоставляет согласованный набор классов для управления базами данных SQL.

    Эти классы полностью интегрируются с архитектурой PyQt Model-View, что позволяет разрабатывать приложения с графическим интерфейсом пользователя, которые могут управлять базами данных в удобной для пользователя форме.

    В этом уроке вы узнали, как:

  • Используйте поддержку PyQt SQL для подключения к базе данных
  • Выполнять SQL-запросы к базе данных с PyQt
  • Создавайте приложения для баз данных с использованием архитектуры PyQt Model-View
  • Отображение и редактирование данных из базы данных с помощью виджетов PyQt
  • Обладая этими знаниями, вы сможете повысить свою продуктивность при создании нетривиальных приложений для баз данных и сделать свои приложения с графическим интерфейсом пользователя более надежными.

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *