Database
 sql >> Baza danych >  >> RDS >> Database

Obsługa baz danych SQL za pomocą PyQt:podstawy

Tworzenie aplikacji korzystających z bazy danych SQL jest dość powszechnym zadaniem programistycznym. Bazy danych SQL są wszędzie i mają świetne wsparcie w Pythonie. W programowaniu z graficznym interfejsem użytkownika PyQt zapewnia solidną i wieloplatformową obsługę baz danych SQL, która umożliwia spójne tworzenie, łączenie i zarządzanie bazami danych.

Obsługa SQL PyQt w pełni integruje się z architekturą Model-View, aby pomóc Ci w procesie tworzenia aplikacji bazodanowych.

W tym samouczku dowiesz się, jak:

  • Skorzystaj z obsługi SQL w PyQt niezawodnie łączyć się z bazą danych
  • Wykonywanie zapytań SQL w bazie danych za pomocą PyQt
  • Użyj architektury Model-View PyQt w aplikacjach bazodanowych
  • Wyświetlaj i edytuj dane za pomocą różnych widżetów PyQt

Przykłady w tym samouczku wymagają podstawowej znajomości języka SQL, w szczególności systemu zarządzania bazą danych SQLite. Pomocna będzie również pewna wcześniejsza znajomość programowania GUI w Pythonie i PyQt.

Bezpłatny bonus: 5 Thoughts On Python Mastery, bezpłatny kurs dla programistów Pythona, który pokazuje mapę drogową i sposób myślenia, których będziesz potrzebować, aby przenieść swoje umiejętności Pythona na wyższy poziom.


Podłączanie PyQt do bazy danych SQL

Łączenie aplikacji z relacyjną bazą danych i zmuszanie aplikacji do tworzenia, odczytywania, aktualizowania i usuwania danych przechowywanych w tej bazie danych jest typowym zadaniem w programowaniu. Relacyjne bazy danych są zazwyczaj zorganizowane w zestaw tabeli lub relacje . Dany wiersz w tabeli jest określany jako rekord lub krotka , a kolumna jest określana jako atrybut .

Uwaga: Termin pole jest powszechnie używany do identyfikacji pojedynczego fragmentu danych przechowywanych w komórce danego rekordu w tabeli. Z drugiej strony termin nazwa pola służy do identyfikacji nazwy kolumny.

Każda kolumna przechowuje określony rodzaj informacji, na przykład nazwiska, daty lub liczby. Każdy wiersz reprezentuje zestaw ściśle powiązanych danych, a każdy wiersz ma taką samą ogólną strukturę. Na przykład w bazie danych, która przechowuje dane o pracownikach w firmie, określony wiersz reprezentuje pojedynczego pracownika.

Większość relacyjnych systemów baz danych używa języka SQL (ustrukturyzowanego języka zapytań) do wykonywania zapytań, manipulowania i utrzymywania danych przechowywanych w bazie danych. SQL to deklaratywny i specyficzny dla domeny język programowania, specjalnie zaprojektowany do komunikacji z bazami danych.

Systemy relacyjnych baz danych i SQL są obecnie szeroko stosowane. Znajdziesz kilka różnych systemów zarządzania bazami danych, takich jak SQLite, PostgreSQL, MySQL, MariaDB i wiele innych. Możesz podłączyć Pythona do dowolnego z tych systemów baz danych za pomocą dedykowanej biblioteki Python SQL.

Uwaga: Mimo że wbudowana obsługa SQL PyQt jest preferowaną opcją do zarządzania bazami danych SQL w PyQt, możesz również użyć dowolnej innej biblioteki do obsługi połączenia z bazą danych. Niektóre z tych bibliotek obejmują SQLAlchemy, pandy, SQLite i tak dalej.

Jednak korzystanie z innej biblioteki do zarządzania bazami danych ma pewne wady. Nie będziesz mógł skorzystać z integracji klas SQL PyQt z architekturą Model-View. Ponadto dodasz dodatkowe zależności do swojej aplikacji.

Jeśli chodzi o programowanie GUI w Pythonie i PyQt, PyQt zapewnia solidny zestaw klas do pracy z bazami danych SQL. Ten zestaw klas będzie Twoim najlepszym sprzymierzeńcem, gdy będziesz musiał połączyć swoją aplikację z bazą danych SQL.

Uwaga: Niestety, oficjalna dokumentacja PyQt5 zawiera kilka niekompletnych sekcji. Aby obejść ten problem, możesz zapoznać się z dokumentacją PyQt4, dokumentacją Qt For Python lub oryginalną dokumentacją Qt. W tym samouczku niektóre łącza prowadzą do oryginalnej dokumentacji Qt, która w większości przypadków jest lepszym źródłem informacji.

W tym samouczku nauczysz się podstaw korzystania z obsługi SQL PyQt do tworzenia aplikacji GUI, które niezawodnie współdziałają z relacyjnymi bazami danych w celu odczytywania, zapisywania, usuwania i wyświetlania danych.


Tworzenie połączenia z bazą danych

Połączenie aplikacji z fizyczną bazą danych SQL jest ważnym krokiem w procesie tworzenia aplikacji bazodanowych za pomocą PyQt. Aby pomyślnie wykonać ten krok, potrzebujesz ogólnych informacji na temat konfiguracji bazy danych.

Na przykład musisz wiedzieć, na jakim systemie zarządzania bazą danych jest zbudowana Twoja baza danych, a także możesz potrzebować nazwy użytkownika, hasła, nazwy hosta i tak dalej.

W tym samouczku użyjesz SQLite 3, który jest dobrze przetestowanym systemem bazodanowym obsługującym wszystkie platformy i posiadającym minimalne wymagania konfiguracyjne. SQLite umożliwia odczytywanie i zapisywanie bezpośrednio do baz danych na dysku lokalnym bez potrzeby oddzielnego procesu serwerowego. To sprawia, że ​​jest to przyjazna dla użytkownika opcja nauki tworzenia aplikacji bazodanowych.

Kolejną zaletą korzystania z SQLite jest to, że biblioteka jest dostarczana z Pythonem, a także z PyQt, więc nie musisz instalować niczego więcej, aby rozpocząć z nią pracę.

W PyQt możesz utworzyć połączenie z bazą danych za pomocą QSqlDatabase klasa. Ta klasa reprezentuje połączenie i zapewnia interfejs dostępu do bazy danych. Aby utworzyć połączenie, po prostu wywołaj .addDatabase() na QSqlDatabase . Ta statyczna metoda przyjmuje jako argumenty sterownik SQL i opcjonalną nazwę połączenia i zwraca połączenie z bazą danych:

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

Pierwszy argument, driver , jest wymaganym argumentem, który zawiera ciąg znaków zawierający nazwę obsługiwanego przez PyQt sterownika SQL. Drugi argument, connectionName , jest opcjonalnym argumentem, który przechowuje ciąg z nazwą połączenia. connectionName domyślnie QSqlDatabase.defaultConnection , który zwykle zawiera ciąg "qt_sql_default_connection" .

Jeśli masz już połączenie o nazwie connectionName , to połączenie jest usuwane i zastępowane nowym połączeniem, a .addDatabase() zwraca nowo dodane połączenie z bazą danych z powrotem do dzwoniącego.

Wywołanie .addDatabase() dodaje połączenie z bazą danych do listy dostępnych połączeń. Ta lista jest rejestrem globalnym które PyQt utrzymuje za kulisami, aby śledzić dostępne połączenia w aplikacji. Rejestrowanie połączeń za pomocą znaczącego connectionName pozwoli Ci zarządzać kilkoma połączeniami w aplikacji bazy danych.

Po utworzeniu połączenia może być konieczne ustawienie kilku atrybutów. Konkretny zestaw atrybutów będzie zależał od używanego sterownika. Ogólnie rzecz biorąc, musisz ustawić atrybuty, takie jak nazwa bazy danych, nazwa użytkownika i hasło dostępu do bazy danych.

Oto podsumowanie metod ustawiających, których możesz użyć do ustawienia najczęściej używanych atrybutów lub właściwości połączenia z bazą danych:

Metoda Opis
.setDatabaseName(name) Ustawia nazwę bazy danych na name , który jest ciągiem znaków reprezentującym prawidłową nazwę bazy danych
.setHostName(host) Ustawia nazwę hosta na host , który jest ciągiem znaków reprezentującym prawidłową nazwę hosta
.setUserName(username) Ustawia nazwę użytkownika na username , który jest ciągiem znaków reprezentującym prawidłową nazwę użytkownika
.setPassword(password) Ustawia hasło na password , który jest ciągiem znaków reprezentującym prawidłowe hasło

Zwróć uwagę, że hasło, które przekazujesz jako argument do .setPassword() jest przechowywany w postaci zwykłego tekstu i można go później pobrać, wywołując .password() . To poważne zagrożenie bezpieczeństwa których powinieneś unikać umieszczania w swoich aplikacjach bazodanowych. Bezpieczniejszego podejścia dowiesz się w sekcji Otwieranie połączenia z bazą danych w dalszej części tego samouczka.

Aby utworzyć połączenie z bazą danych SQLite przy użyciu QSqlDatabase , otwórz interaktywną sesję Pythona i wpisz następujący kod:

>>>
>>> from PyQt5.QtSql import QSqlDatabase

>>> con = QSqlDatabase.addDatabase("QSQLITE")
>>> con.setDatabaseName("contacts.sqlite")

>>> con
<PyQt5.QtSql.QSqlDatabase object at 0x7f0facec0c10>

>>> con.databaseName()
'contacts.sqlite'

>>> con.connectionName()
'qt_sql_default_connection'

Ten kod utworzy obiekt połączenia z bazą danych za pomocą "QSQLITE" jako sterownik połączenia i "contacts.sqlite" jako nazwę bazy danych połączenia. Ponieważ nie przekazujesz nazwy połączenia do .addDatabase() , nowo utworzone staje się połączeniem domyślnym, którego nazwa to "qt_sql_default_connection" .

W przypadku baz danych SQLite nazwą bazy danych jest zwykle nazwa pliku lub ścieżka zawierająca nazwę pliku bazy danych. Możesz także użyć specjalnej nazwy ":memory:" dla bazy danych w pamięci.



Obsługa wielu połączeń

Mogą wystąpić sytuacje, w których trzeba użyć wielu połączeń do jednej bazy danych. Na przykład możesz chcieć rejestrować interakcje użytkowników z bazą danych przy użyciu określonego połączenia dla każdego użytkownika.

W innych sytuacjach może być konieczne połączenie aplikacji z kilkoma bazami danych. Na przykład możesz chcieć połączyć się z kilkoma zdalnymi bazami danych w celu zebrania danych w celu wypełnienia lub aktualizacji lokalnej bazy danych.

Aby poradzić sobie z takimi sytuacjami, możesz podać określone nazwy dla różnych połączeń i odwoływać się do każdego połączenia za pomocą jego nazwy. Jeśli chcesz nadać swojemu połączeniu z bazą danych nazwę, przekaż tę nazwę jako drugi argument do .addDatabase() :

>>>
>>> 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
<PyQt5.QtSql.QSqlDatabase object at 0x7f367f5fbf90>
>>> con2
<PyQt5.QtSql.QSqlDatabase object at 0x7f3686dd7510>

>>> con1.databaseName()
'contacts.sqlite'
>>> con2.databaseName()
'contacts.sqlite'

>>> con1.connectionName()
'con1'
>>> con2.connectionName()
'con2'

Tutaj tworzysz dwa różne połączenia z tą samą bazą danych, contacts.sqlite . Każde połączenie ma swoją własną nazwę połączenia. Możesz użyć nazwy połączenia, aby uzyskać odwołanie do konkretnego połączenia w dowolnym momencie w kodzie, zgodnie z własnymi potrzebami. Aby to zrobić, możesz wywołać .database() z nazwą połączenia:

>>>
>>> from PyQt5.QtSql import QSqlDatabase

>>> db = QSqlDatabase.database("con1", open=False)

>>> db.databaseName()
'contacts.sqlite'
>>> db.connectionName()
'con1'

W tym przykładzie widzisz, że .database() przyjmuje dwa argumenty:

  1. connectionName przechowuje nazwę połączenia, której musisz użyć. Jeśli nie podasz nazwy połączenia, zostanie użyte połączenie domyślne.
  2. open przechowuje wartość logiczną, która mówi .database() czy chcesz automatycznie otworzyć połączenie, czy nie. Jeśli open jest True (domyślnie) i połączenie nie jest otwarte, to połączenie jest otwierane automatycznie.

Zwracana wartość .database() jest referencją do obiektu połączenia o nazwie connectionName . Możesz użyć różnych nazw połączeń, aby uzyskać odniesienia do określonych obiektów połączeń, a następnie użyć ich do zarządzania bazą danych.



Korzystanie z różnych nurków SQL

Do tej pory nauczyłeś się tworzyć połączenie z bazą danych za pomocą sterownika SQLite . To nie jedyny sterownik dostępny w PyQt. Biblioteka zapewnia bogaty zestaw sterowników SQL, które umożliwiają korzystanie z różnych typów systemów zarządzania bazami danych w zależności od konkretnych potrzeb:

Nazwa kierowcy System zarządzania bazą danych
QDB2 IBM Db2 (wersja 7.1 i nowsze)
QIBASE Borland InterBase
QMYSQL/MARIADB MySQL lub MariaDB (wersja 5.0 i nowsze)
QOCI Interfejs połączeń Oracle
QODBC Otwarta łączność z bazą danych (ODBC)
QPSQL PostgreSQL (wersje 7.3 i nowsze)
QSQLITE2 SQLite 2 (przestarzały od Qt 5.14)
QSQLITE SQLite 3
QTDS Sybase Adaptive Server (przestarzały od Qt 4.7)

Kolumna Nazwa sterownika zawiera ciągi identyfikatora które musisz przekazać do .addDatabase() jako pierwszy argument, aby użyć skojarzonego sterownika. W przeciwieństwie do sterownika SQLite, gdy używasz innego sterownika, może być konieczne ustawienie kilku atrybutów, takich jak databaseName , hostName , userName i password , aby połączenie działało prawidłowo.

Sterowniki baz danych pochodzą z QSqlDriver . Możesz tworzyć własne sterowniki baz danych, tworząc podklasę QSqlDriver , ale ten temat wykracza poza zakres tego samouczka. Jeśli jesteś zainteresowany tworzeniem własnych sterowników baz danych, zapoznaj się z artykułem Jak napisać własny sterownik bazy danych, aby uzyskać więcej informacji.



Otwieranie połączenia z bazą danych

Gdy masz połączenie z bazą danych, musisz je otworzyć, aby móc wchodzić w interakcje z bazą danych. Aby to zrobić, wywołujesz .open() na obiekcie połączenia. .open() ma następujące dwie odmiany:

  1. .open() otwiera połączenie z bazą danych przy użyciu bieżących wartości połączenia.
  2. .open(username, password) otwiera połączenie z bazą danych przy użyciu podanej username i password .

Obie odmiany zwracają True jeśli połączenie się powiedzie. W przeciwnym razie zwracają False . Jeśli nie można nawiązać połączenia, możesz wywołać .lastError() aby uzyskać informacje o tym, co się stało. Ta funkcja zwraca informacje o ostatnim błędzie zgłoszonym przez bazę danych.

Uwaga: Jak już wiesz, .setPassword(password) przechowuje hasła jako zwykły tekst, co stanowi zagrożenie dla bezpieczeństwa. Z drugiej strony .open() w ogóle nie przechowuje haseł. Przekazuje hasło bezpośrednio do kierowcy podczas otwierania połączenia. Następnie odrzuca hasło. Tak więc, używając .open() zarządzanie hasłami jest najlepszym rozwiązaniem, jeśli chcesz zapobiec problemom z bezpieczeństwem.

Oto przykład, jak otworzyć połączenie z bazą danych SQLite przy użyciu pierwszej odmiany .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

W powyższym przykładzie najpierw tworzysz połączenie z bazą danych SQLite i otwierasz je za pomocą .open() . Od .open() zwraca True , połączenie powiodło się. W tym momencie możesz sprawdzić połączenie za pomocą .isOpen() , który zwraca True jeśli połączenie jest otwarte i False inaczej.

Uwaga: Jeśli wywołasz .open() w połączeniu, które używa sterownika SQLite, a plik bazy danych nie istnieje, nowy i pusty plik bazy danych zostanie utworzony automatycznie.

W rzeczywistych aplikacjach musisz upewnić się, że masz prawidłowe połączenie z bazą danych, zanim spróbujesz wykonać jakiekolwiek operacje na swoich danych. W przeciwnym razie Twoja aplikacja może się zepsuć i zawieść. Na przykład, co jeśli nie masz uprawnień do zapisu w katalogu, w którym próbujesz utworzyć plik bazy danych? Musisz się upewnić, że radzisz sobie z każdym błędem, który może wystąpić podczas otwierania połączenia.

Popularny sposób wywoływania .open() jest umieszczenie go w instrukcji warunkowej. Pozwala to na obsługę błędów, które mogą wystąpić podczas otwierania połączenia:

>>>
>>> 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)

Zawijanie wywołania do .open() w instrukcji warunkowej pozwala obsłużyć każdy błąd, który wystąpi po otwarciu połączenia. W ten sposób możesz poinformować użytkowników o wszelkich problemach przed uruchomieniem aplikacji. Zauważ, że aplikacja kończy pracę ze statusem wyjścia 1 , który jest zwykle używany do wskazania awarii programu.

W powyższym przykładzie używasz .open() w sesji interaktywnej, więc używasz print() prezentować użytkownikom komunikaty o błędach. Jednak w aplikacjach GUI, zamiast używać print() , zwykle używasz QMessageBox obiekt. Z QMessageBox , możesz tworzyć małe okna dialogowe, aby prezentować informacje użytkownikom.

Oto przykładowa aplikacja GUI, która ilustruje sposób obsługi błędów połączenia:

 1import sys
 2
 3from PyQt5.QtSql import QSqlDatabase
 4from PyQt5.QtWidgets import QApplication, QMessageBox, QLabel
 5
 6# Create the connection
 7con = QSqlDatabase.addDatabase("QSQLITE")
 8con.setDatabaseName("/home/contacts.sqlite")
 9
10# Create the application
11app = QApplication(sys.argv)
12
13# Try to open the connection and handle possible errors
14if not con.open():
15    QMessageBox.critical(
16        None,
17        "App Name - Error!",
18        "Database Error: %s" % con.lastError().databaseText(),
19    )
20    sys.exit(1)
21
22# Create the application's window
23win = QLabel("Connection Successfully Opened!")
24win.setWindowTitle("App Name")
25win.resize(200, 100)
26win.show()
27sys.exit(app.exec_())

if oświadczenie w wierszu 14 sprawdza, czy połączenie nie powiodło się. Jeśli /home/ katalog nie istnieje lub jeśli nie masz uprawnień do zapisu w nim, wywołaj .open() kończy się niepowodzeniem, ponieważ nie można utworzyć pliku bazy danych. W tej sytuacji przepływ wykonania wprowadza if blokuje kod wyciągu i wyświetla komunikat na ekranie.

Jeśli zmienisz ścieżkę do dowolnego innego katalogu, w którym możesz pisać, wywołaj .open() się powiedzie, a zobaczysz okno wyświetlające komunikat Connection Successfully Opened! Będziesz także mieć nowy plik bazy danych o nazwie contacts.sqlite w wybranym katalogu.

Pamiętaj, że przekazujesz None jako rodzic wiadomości ponieważ w momencie wyświetlania wiadomości nie utworzyłeś jeszcze okna, więc nie masz realnego rodzica dla okna wiadomości.




Uruchamianie zapytań SQL za pomocą PyQt

Dzięki w pełni funkcjonalnemu połączeniu z bazą danych możesz rozpocząć pracę z bazą danych. Aby to zrobić, możesz użyć zapytań SQL opartych na ciągach znaków i QSqlQuery przedmioty. QSqlQuery pozwala na uruchamianie dowolnego rodzaju zapytania SQL w Twojej bazie danych. Z QSqlQuery , możesz wykonywać instrukcje języka manipulacji danymi (DML), takie jak SELECT , INSERT , UPDATE i DELETE , a także instrukcje języka definicji danych (DDL), takie jak CREATE TABLE i tak dalej.

Konstruktor QSqlQuery ma kilka odmian, ale w tym samouczku poznasz dwie z nich:

  1. QSqlQuery(query, connection) konstruuje obiekt zapytania przy użyciu query opartego na ciągach znaków SQL i bazę danych connection . Jeśli nie określisz połączenia lub jeśli określone połączenie jest nieprawidłowe, używane jest domyślne połączenie z bazą danych. Jeśli query nie jest pustym ciągiem, zostanie wykonany od razu.

  2. QSqlQuery(connection) konstruuje obiekt zapytania przy użyciu connection . Jeśli connection jest nieprawidłowe, używane jest połączenie domyślne.

Możesz także utworzyć QSqlQuery obiekty bez przekazywania żadnych argumentów do konstruktora. W takim przypadku zapytanie użyje domyślnego połączenia z bazą danych, jeśli takie istnieje.

Aby wykonać zapytanie, musisz wywołać .exec() na obiekcie zapytania. Możesz użyć .exec() na dwa różne sposoby:

  1. .exec(query) wykonuje zapytanie SQL oparte na ciągach znaków zawarte w query . Zwraca True jeśli zapytanie się powiodło i w przeciwnym razie zwraca False .

  2. .exec() wykonuje wcześniej przygotowane zapytanie SQL. Zwraca True jeśli zapytanie się powiodło i w przeciwnym razie zwraca False .

Uwaga: PyQt implementuje również odmiany QSqlQuery.exec() o nazwie .exec_() . Zapewniają one kompatybilność wsteczną ze starszymi wersjami Pythona, w których exec było słowem kluczowym języka.

Teraz, gdy znasz już podstawy używania QSqlQuery aby tworzyć i wykonywać zapytania SQL, jesteś gotowy, aby nauczyć się wykorzystywać swoją wiedzę w praktyce.


Wykonywanie statycznych zapytań SQL

Aby rozpocząć tworzenie i wykonywanie zapytań za pomocą PyQt, uruchomisz swój ulubiony edytor kodu lub IDE i utworzysz skrypt Pythona o nazwie queries.py . Zapisz skrypt i dodaj do niego następujący kod:

 1import sys
 2
 3from PyQt5.QtSql import QSqlDatabase, QSqlQuery
 4
 5# Create the connection
 6con = QSqlDatabase.addDatabase("QSQLITE")
 7con.setDatabaseName("contacts.sqlite")
 8
 9# Open the connection
10if not con.open():
11    print("Database Error: %s" % con.lastError().databaseText())
12    sys.exit(1)
13
14# Create a query and execute it right away using .exec()
15createTableQuery = QSqlQuery()
16createTableQuery.exec(
17    """
18    CREATE TABLE contacts (
19        id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL,
20        name VARCHAR(40) NOT NULL,
21        job VARCHAR(50),
22        email VARCHAR(40) NOT NULL
23    )
24    """
25)
26
27print(con.tables())

W tym skrypcie zaczynasz od zaimportowania modułów i klas, z którymi będziesz pracować. Następnie tworzysz połączenie z bazą danych za pomocą .addDatabase() ze sterownikiem SQLite. Ustawiasz nazwę bazy danych na "contacts.sqlite" i otwórz połączenie.

Aby utworzyć pierwsze zapytanie, tworzysz instancję QSqlQuery bez żadnych argumentów. Po umieszczeniu obiektu zapytania wywołujesz .exec() , przekazując jako argument zapytanie SQL oparte na ciągach znaków. Ten rodzaj zapytania jest znany jako zapytanie statyczne ponieważ nie pobiera żadnych parametrów spoza zapytania.

Powyższe zapytanie SQL tworzy nową tabelę o nazwie contacts w Twojej bazie danych. Ta tabela będzie miała następujące cztery kolumny:

Kolumna Treść
id Liczba całkowita z kluczem podstawowym tabeli
name Ciąg z nazwą kontaktu
job Ciąg z nazwą stanowiska osoby kontaktowej
email Ciąg z adresem e-mail kontaktu

Ostatnia linia w powyższym skrypcie wyświetla listę tabel zawartych w Twojej bazie danych. Jeśli uruchomisz skrypt, zauważysz, że nowy plik bazy danych o nazwie contacts.sqlite jest tworzony w bieżącym katalogu. Otrzymasz również coś takiego jak ['contacts', 'sqlite_sequence'] wydrukowane na ekranie. Ta lista zawiera nazwy tabel w Twojej bazie danych.

Uwaga: Zapytanie SQL oparte na ciągach musi używać odpowiedniej składni zgodnie z konkretną bazą danych SQL, której dotyczy zapytanie. Jeśli składnia jest nieprawidłowa, to .exec() ignoruje zapytanie i zwraca False .

W przypadku SQLite zapytanie może zawierać tylko jedną instrukcję na raz.

Wywołanie .exec() na QSqlQuery obiekt jest powszechnym sposobem natychmiastowego wykonywania zapytań SQL opartych na ciągach w bazach danych, ale co zrobić, jeśli chcesz wcześniej przygotować zapytania do późniejszego wykonania? To temat następnej sekcji.



Wykonywanie zapytań dynamicznych:formatowanie ciągów

Do tej pory nauczyłeś się wykonywać statyczne zapytania w bazie danych. Zapytania statyczne to te, które nie akceptują parametrów , więc zapytanie działa bez zmian. Mimo że te zapytania są dość przydatne, czasami trzeba utworzyć zapytania, które pobierają dane w odpowiedzi na określone parametry wejściowe.

Zapytania, które akceptują parametry w czasie wykonywania, są nazywane zapytaniami dynamicznymi . Korzystanie z parametrów umożliwia precyzyjne dostrojenie zapytania i pobieranie danych w odpowiedzi na określone wartości parametrów. Różne wartości dadzą różne wyniki. Możesz wziąć parametry wejściowe w zapytaniu, korzystając z jednego z następujących dwóch podejść:

  1. Buduj zapytanie dynamicznie, używając formatowania ciągu do interpolacji wartości parametrów.
  2. Przygotuj zapytanie za pomocą parametrów zastępczych, a następnie powiąż określone wartości z parametrami.

Pierwsze podejście pozwala na szybkie tworzenie zapytań dynamicznych. Jednak, aby bezpiecznie korzystać z tego podejścia, musisz mieć pewność, że wartości parametrów pochodzą z zaufanego źródła. W przeciwnym razie możesz napotkać ataki typu SQL injection.

Oto przykład, jak używać formatowania ciągów do tworzenia dynamicznych zapytań w PyQt:

>>>
>>> from PyQt5.QtSql import QSqlQuery, QSqlDatabase

>>> con = QSqlDatabase.addDatabase("QSQLITE")
>>> con.setDatabaseName("contacts.sqlite")
>>> con.open()
True

>>> name = "Linda"
>>> job = "Technical Lead"
>>> email = "[email protected]"

>>> query = QSqlQuery()
>>> query.exec(
...     f"""INSERT INTO contacts (name, job, email)
...     VALUES ('{name}', '{job}', '{email}')"""
... )
True

W tym przykładzie używasz f-string do tworzenia dynamicznego zapytania przez interpolację określonych wartości do zapytania SQL opartego na ciągach. Ostatnie zapytanie wstawia dane do Twoich contacts tabela, która teraz zawiera dane o Linda .

Uwaga: W dalszej części tego samouczka zobaczysz, jak pobierać i nawigować po danych przechowywanych w bazie danych.

Zwróć uwagę, że aby tego rodzaju dynamiczne zapytanie zadziałało, musisz upewnić się, że wstawiane wartości mają właściwy typ danych. Tak więc używasz pojedynczych cudzysłowów wokół symbolu zastępczego w f-stringu, ponieważ te wartości muszą być ciągami.



Wykonywanie zapytań dynamicznych:parametry zastępcze

Drugie podejście do wykonywania zapytań dynamicznych wymaga wcześniejszego przygotowania zapytań przy użyciu szablonu z elementami zastępczymi dla parametrów. PyQt obsługuje dwa style symboli zastępczych:

  1. Styl Oracle używa nazwanych symboli zastępczych, takich jak :name lub :email .
  2. Styl ODBC używa znaku zapytania (? ) jako pozycyjny symbol zastępczy.

Pamiętaj, że tych stylów nie można mieszać w jednym zapytaniu. Możesz zapoznać się z podejściami do wartości powiązań, aby uzyskać dodatkowe przykłady użycia symboli zastępczych.

Uwaga: ODBC to skrót od Open Database Connectivity.

Aby utworzyć tego rodzaju dynamiczne zapytanie w PyQt, najpierw utwórz szablon z symbolem zastępczym dla każdego parametru zapytania, a następnie przekaż ten szablon jako argument do .prepare() , który analizuje, kompiluje i przygotowuje szablon zapytania do wykonania. Jeśli szablon ma jakieś problemy, takie jak błąd składni SQL, to .prepare() nie może skompilować szablonu i zwraca False .

Jeśli proces przygotowania się powiedzie, prepare() zwraca True . Następnie możesz przekazać określoną wartość do każdego parametru za pomocą .bindValue() z nazwanymi lub pozycyjnymi parametrami lub za pomocą .addBindValue() z parametrami pozycyjnymi. .bindValue() ma następujące dwie odmiany:

  1. .bindValue(placeholder, val)
  2. .bindValue(pos, val)

W pierwszej odmianie placeholder reprezentuje symbol zastępczy w stylu Oracle. W drugiej odmianie pos reprezentuje liczbę całkowitą liczoną od zera z pozycją parametru w zapytaniu. W obu odmianach val przechowuje wartość do powiązania z określonym parametrem.

.addBindValue() dodaje wartość do listy symboli zastępczych za pomocą wiązania pozycyjnego. Oznacza to, że kolejność wywołań .addBindValue() określa, jaka wartość zostanie powiązana z każdym parametrem zastępczym w przygotowanym zapytaniu.

Aby zacząć używać przygotowanych zapytań, możesz przygotować INSERT INTO Instrukcja SQL do wypełnienia bazy danych przykładowymi danymi. Wróć do skryptu utworzonego w sekcji Wykonywanie statycznych zapytań SQL i dodaj następujący kod zaraz po wywołaniu funkcji print() :

28# Creating a query for later execution using .prepare()
29insertDataQuery = QSqlQuery()
30insertDataQuery.prepare(
31    """
32    INSERT INTO contacts (
33        name,
34        job,
35        email
36    )
37    VALUES (?, ?, ?)
38    """
39)
40
41# Sample data
42data = [
43    ("Joe", "Senior Web Developer", "[email protected]"),
44    ("Lara", "Project Manager", "[email protected]"),
45    ("David", "Data Analyst", "[email protected]"),
46    ("Jane", "Senior Python Developer", "[email protected]"),
47]
48
49# Use .addBindValue() to insert data
50for name, job, email in data:
51    insertDataQuery.addBindValue(name)
52    insertDataQuery.addBindValue(job)
53    insertDataQuery.addBindValue(email)
54    insertDataQuery.exec()

Pierwszym krokiem jest utworzenie QSqlQuery obiekt. Następnie wywołujesz .prepare() na obiekcie zapytania. W tym przypadku dla symboli zastępczych użyj stylu ODBC. Twoje zapytanie przyjmie wartości odpowiadające name kontaktu , job i email , więc potrzebujesz trzech symboli zastępczych. Ponieważ id kolumna jest automatycznie zwiększaną liczbą całkowitą, nie musisz podawać jej wartości.

Następnie tworzysz przykładowe dane, aby zapełnić bazę danych. data zawiera listę krotek, a każda krotka zawiera trzy elementy:imię i nazwisko, stanowisko i adres e-mail każdego kontaktu.

Ostatnim krokiem jest powiązanie wartości, które chcesz przekazać do każdego symbolu zastępczego, a następnie wywołanie .exec() do wykonania zapytania. Aby to zrobić, użyj for pętla. Nagłówek pętli rozpakowuje każdą krotkę w data na trzy oddzielne zmienne o wygodnych nazwach. Następnie wywołujesz .addBindValue() na obiekcie zapytania, aby powiązać wartości z symbolami zastępczymi.

Pamiętaj, że używasz pozycyjnych symboli zastępczych , czyli kolejność, w jakiej wywołujesz .addBindValue() określi kolejność, w jakiej każda wartość jest przekazywana do odpowiedniego symbolu zastępczego.

Takie podejście do tworzenia zapytań dynamicznych jest przydatne, gdy chcesz dostosować zapytania przy użyciu wartości pochodzących z danych wejściowych użytkownika. Anytime you take the user’s input to complete a query on a database, you face the security risk of SQL injection.

In PyQt, combining .prepare() , .bindValue() , and .addBindValue() fully protects you against SQL injection attacks, so this is the way to go when you’re taking untrusted input to complete your queries.



Navigating the Records in a Query

If you execute a SELECT statement, then your QSqlQuery object will retrieve zero or more records from one or more tables in your database. The query will hold records containing data that matches the query’s criteria. If no data matches the criteria, then your query will be empty.

QSqlQuery provides a set of navigation methods that you can use to move throughout the records in a query result:

Method Retrieves
.next() The next record
.previous() The previous record
.first() The first record
.last() The last record
.seek(index, relative=False) The record at position index

All these methods position the query object on the retrieved record if that record is available. Most of these methods have specific rules that apply when using them. With these methods, you can move forward, backward, or arbitrarily through the records in a query result. Since they all return either True or False , you can use them in a while loop to navigate all the records in one go.

These methods work with active queries . A query is active when you’ve successfully run .exec() on it, but the query isn’t finished yet. Once an active query is on a valid record, you can retrieve data from that record using .value(index) . This method takes a zero-based integer number, index , and returns the value at that index (column) in the current record.

Uwaga: If you execute a SELECT * type of query, then the columns in the result won’t follow a known order. This might cause problems when you use .value() to retrieve the value at a given column because there’s no way of knowing if you’re using the right column index.

You’ll look at a few examples of how to use some of the navigation methods to move throughout a query below. But first, you need to create a connection to your database:

>>>
>>> from PyQt5.QtSql import QSqlDatabase, QSqlQuery

>>> con = QSqlDatabase.addDatabase("QSQLITE")
>>> con.setDatabaseName("contacts.sqlite")
>>> con.open()
True

Here, you create and open a new connection to contacts.sqlite . If you’ve been following along with this tutorial so far, then this database already contains some sample data. Now you can create a QSqlQuery object and execute it on that data:

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

This query retrieves data about the name , job i email of all the contacts stored in the contacts stół. Since .exec() returned True , the query was successful and is now an active query. You can navigate the records in this query using any of the navigation methods you saw before. You can also retrieve the data at any column in a record using .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)
'[email protected]'

With the navigation methods, you can move around the query result. With .value() , you can retrieve the data at any column in a given record.

You can also iterate through all the records in your query using a while loop along with .next() :

>>>
>>> query.exec()
True

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

With .next() , you navigate all the records in a query result. .next() works similar to the iterator protocol in Python. Once you’ve iterated over the records in a query result, .next() starts returning False until you run .exec() again. A call to .exec() retrieves data from a database and places the query object’s internal pointer one position before the first record, so when you call .next() , you get the first record again.

You can also loop in reverse order using .previous() :

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

.previous() works similar to .next() , but the iteration is done in reverse order. In other words, the loop goes from the query pointer’s position back to the first record.

Sometimes you might want to get the index that identifies a given column in a table by using the name of that column. To do that, you can call .indexOf() on the return value of .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

The call to .indexOf() on the result of .record() returns the index of the "name" column. If "name" doesn’t exist, then .indexOf() returns -1 . This is handy when you use a SELECT * statement in which the order of columns is unknown. Finally, if you’re done with a query object, then you can turn it inactive by calling .finish() . This will free the system memory associated with the query object at hand.




Closing and Removing Database Connections

In practice, some of your PyQt applications will depend on a database, and others won’t. An application that depends on a database often creates and opens a database connection just before creating any window or graphical component and keeps the connection open until the application is closed.

On the other hand, applications that don’t depend on a database but use a database to provide some of their functionalities typically connect to that database only when needed, if at all. In these cases, you can close the connection after use and free the resources associated with that connection, such as system memory.

To close a connection in PyQt, you call .close() on the connection. This method closes the connection and frees any acquired resources. It also invalidates any associated QSqlQuery objects because they can’t work properly without an active connection.

Here’s an example of how to close an active database connection using .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

You can call .close() on a connection to close it and free all its associated resources. To make sure that a connection is closed, you call .isOpen() .

Note that QSqlQuery objects remain in memory after closing their associated connection, so you must make your queries inactive by calling .finish() or .clear() , or by deleting the QSqlQuery object before closing the connection. Otherwise, residual memory is left out in your query object.

You can reopen and reuse any previously closed connection. That’s because .close() doesn’t remove connections from the list of available connections, so they remain usable.

You can also completely remove your database connections using .removeDatabase() . To do this safely, first finish your queries using .finish() , then close the database using .close() , and finally remove the connection. You can use .removeDatabase(connectionName) to remove the database connection called connectionName from the list of available connections. Removed connections are no longer available for use in the application at hand.

To remove the default database connection, you can call .connectionName() on the object returned by .database() and pass the result to .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

Here, the call to .connectionNames() returns the list of available connections. In this case, you have only one connection, the default. Then you remove the connection using .removeDatabase() .

Uwaga: Before closing and removing a database connection, you need to make sure that everything that uses the connection is deleted or set to use a different data source. Otherwise, you can have a resource leak .

Since you need a connection name to use .removeDatabase() , you call .connectionName() on the result of .database() to get the name of the default connection. Finally, you call .connectionNames() again to make sure that the connection is no longer in the list of available connections. Trying to open a removed connection will return False because the connection no longer exists.



Displaying and Editing Data With PyQt

A common requirement in GUI applications that use databases is the ability to load, display, and edit data from the database using different widgets. Table, list, and tree widgets are commonly used in GUIs to manage data.

PyQt provides two different kind of widgets for managing data:

  1. Standard widgets include internal containers for storing data.
  2. View widgets don’t maintain internal data containers but use models to access data.

For small GUI applications that manage small databases, you can use the first approach. The second approach is handy when you’re building complex GUI applications that manage large databases.

The second approach takes advantage of PyQt’s Model-View programming. With this approach, you have widgets that represent views such as tables, lists, and trees on one hand and model classes that communicate with your data on the other hand.


Understanding PyQt’s Model-View Architecture

The Model-View-Controller (MVC) design pattern is a general software pattern intended to divide an application’s code into three general layers, each with a different role.

The model takes care of the business logic of the application, the view provides on-screen representations, and the controller connects the model and the view to make the application work.

Qt provides a custom variation of MVC. They call it the Model-View architecture, and it’s available for PyQt as well. The pattern also separates the logic into three components:

  1. Models communicate with and access the data. They also define an interface that’s used by views and delegates to access the data. All models are based on QAbstractItemModel . Some commonly used models include QStandardItemModel , QFileSystemModel , and SQL-related models.

  2. Views are responsible for displaying the data to the user. They also have similar functionality to the controller in the MVC pattern. All views are based on QAbstractItemView . Some commonly used views are QListView , QTableView , and QTreeView .

  3. Delegates paint view items and provide editor widgets for modifying items. They also communicate back with the model if an item has been modified. The base class is QAbstractItemDelegate .

Separating classes into these three components implies that changes on models will be reflected on associated views or widgets automatically, and changes on views or widgets through delegates will update the underlying model automatically.

In addition, you can display the same data in different views without the need for multiple models.



Using Standard Widget Classes

PyQt provides a bunch of standard widgets for displaying and editing data in your GUI applications. These standard widgets provide views such as tables, trees, and lists. They also provide an internal container for storing data and convenient delegates for editing the data. All these features are grouped into a single class.

Here are three of these standard classes:

Standard Class Displays
QListWidget A list of items
QTreeWidget A hierarchical tree of items
QTableWidget A table of items

QTableWidget is arguably the most popular widget when it comes to displaying and editing data. It creates a 2D array of QTableWidgetItem objects. Each item holds an individual value as a string. All these values are displayed and organized in a table of rows and columns.

You can perform at least the following operations on a QTableWidget object:

  • Editing the content of its items using delegate objects
  • Adding new items using .setItem()
  • Setting the number of rows and columns using .setRowCount() and .setColumnCount()
  • Adding vertical and horizontal header labels using setHorizontalHeaderLabels() and .setVerticalHeaderLabels

Here’s a sample application that shows how to use a QTableWidget object to display data in a GUI. The application uses the database you created and populated in previous sections, so if you want to run it, then you need to save the code into the same directory in which you have the contacts.sqlite baza danych:

If you double-click any cell of the table, then you’ll be able to edit the content of the cell. However, your changes won’t be saved to your database.

Here’s the code for your application:

 1import sys
 2
 3from PyQt5.QtSql import QSqlDatabase, QSqlQuery
 4from PyQt5.QtWidgets import (
 5    QApplication,
 6    QMainWindow,
 7    QMessageBox,
 8    QTableWidget,
 9    QTableWidgetItem,
10)
11
12class Contacts(QMainWindow):
13    def __init__(self, parent=None):
14        super().__init__(parent)
15        self.setWindowTitle("QTableView Example")
16        self.resize(450, 250)
17        # Set up the view and load the data
18        self.view = QTableWidget()
19        self.view.setColumnCount(4)
20        self.view.setHorizontalHeaderLabels(["ID", "Name", "Job", "Email"])
21        query = QSqlQuery("SELECT id, name, job, email FROM contacts")
22        while query.next():
23            rows = self.view.rowCount()
24            self.view.setRowCount(rows + 1)
25            self.view.setItem(rows, 0, QTableWidgetItem(str(query.value(0))))
26            self.view.setItem(rows, 1, QTableWidgetItem(query.value(1)))
27            self.view.setItem(rows, 2, QTableWidgetItem(query.value(2)))
28            self.view.setItem(rows, 3, QTableWidgetItem(query.value(3)))
29        self.view.resizeColumnsToContents()
30        self.setCentralWidget(self.view)
31
32def createConnection():
33    con = QSqlDatabase.addDatabase("QSQLITE")
34    con.setDatabaseName("contacts.sqlite")
35    if not con.open():
36        QMessageBox.critical(
37            None,
38            "QTableView Example - Error!",
39            "Database Error: %s" % con.lastError().databaseText(),
40        )
41        return False
42    return True
43
44app = QApplication(sys.argv)
45if not createConnection():
46    sys.exit(1)
47win = Contacts()
48win.show()
49sys.exit(app.exec_())

Here’s what’s happening in this example:

  • Lines 18 to 20 create a QTableWidget object, set the number of columns to 4 , and set user-friendly labels for each column’s header.
  • Line 21 creates and executes a SELECT SQL query on your database to get all the data in the contacts table.
  • Linia 22 starts a while loop to navigate the records in the query result using .next() .
  • Line 24 increments the number of rows in the table by 1 using .setRowCount() .
  • Lines 25 to 28 add items of data to your table using .setItem() . Note that since the values in the id columns are integer numbers, you need to convert them into strings to be able to store them in a QTableWidgetItem obiekt.

.setItem() takes three arguments:

  1. row holds a zero-based integer that represents the index of a given row in the table.
  2. column holds a zero-based integer that represents the index of a given column in the table.
  3. item holds the QTableWidgetItem object that you need to place at a given cell in the table.

Finally, you call .resizeColumnsToContents() on your view to adjust the size of the columns to their content and provide a better rendering of the data.

Displaying and editing database tables using standard widgets can become a challenging task. That’s because you’ll have two copies of the same data. In other words you’ll have a copy of the data in two locations:

  1. Outside the widget, in your database
  2. Inside the widget, in the widget’s internal containers

You’re responsible for synchronizing both copies of your data manually, which can be an annoying and error-prone operation. Luckily, you can use PyQt’s Model-View architecture to avoid most of these problems, as you’ll see in the following section.



Using View and Model Classes

PyQt’s Model-View classes eliminate the problems of data duplication and synchronization that may occur when you use standard widget classes to build database applications. The Model-View architecture allows you to use several views to display the same data because you can pass one model to many views.

Model classes provide an application programming interface (API) that you can use to manipulate data. View classes provide convenient delegate objects that you can use to edit data in the view directly. To connect a view with a given module, you need to call .setModel() on the view object.

PyQt offers a set of view classes that support the Model-View architecture:

View Class Displays
QListView A list of items that take values directly from a model class
QTreeView A hierarchical tree of items that take values directly from a model class
QTableView A table of items that take values directly from a model class

You can use these view classes along with model classes to create your database applications. This will make your applications more robust, faster to code, and less error-prone.

Here are some of the model classes that PyQt provides for working with SQL databases:

Model Class Opis
QSqlQueryModel A read-only data model for SQL queries
QSqlTableModel An editable data model for reading and writing records in a single table
QSqlRelationalTableModel An editable data model for reading and writing records in a relational table

Once you’ve connected one of these models to a physical database table or query, you can use them to populate your views. Views provide delegate objects that allow you to modify the data directly in the view. The model connected to the view will update the data in your database to reflect any change in the view. Note that you don’t have to update the data in the database manually. The model will do that for you.

Here’s an example that shows the basics of how to use a QTableView object and a QSqlTableModel object together to build a database application using PyQt’s Model-View architecture:

To edit the data in a cell of the table, you can double-click the cell. A convenient delegate widget will show in the cell, allowing you to edit its content. Then you can hit Enter to save the changes.

The ability to automatically handle and save changes in the data is one of the more important advantages of using PyQt’s Model-View classes. The Model-View architecture will improve your productivity and reduce the errors that can appear when you have to write data manipulation code by yourself.

Here’s the code to create the application:

 1import sys
 2
 3from PyQt5.QtCore import Qt
 4from PyQt5.QtSql import QSqlDatabase, QSqlTableModel
 5from PyQt5.QtWidgets import (
 6    QApplication,
 7    QMainWindow,
 8    QMessageBox,
 9    QTableView,
10)
11
12class Contacts(QMainWindow):
13    def __init__(self, parent=None):
14        super().__init__(parent)
15        self.setWindowTitle("QTableView Example")
16        self.resize(415, 200)
17        # Set up the model
18        self.model = QSqlTableModel(self)
19        self.model.setTable("contacts")
20        self.model.setEditStrategy(QSqlTableModel.OnFieldChange)
21        self.model.setHeaderData(0, Qt.Horizontal, "ID")
22        self.model.setHeaderData(1, Qt.Horizontal, "Name")
23        self.model.setHeaderData(2, Qt.Horizontal, "Job")
24        self.model.setHeaderData(3, Qt.Horizontal, "Email")
25        self.model.select()
26        # Set up the view
27        self.view = QTableView()
28        self.view.setModel(self.model)
29        self.view.resizeColumnsToContents()
30        self.setCentralWidget(self.view)
31
32def createConnection():
33    con = QSqlDatabase.addDatabase("QSQLITE")
34    con.setDatabaseName("contacts.sqlite")
35    if not con.open():
36        QMessageBox.critical(
37            None,
38            "QTableView Example - Error!",
39            "Database Error: %s" % con.lastError().databaseText(),
40        )
41        return False
42    return True
43
44app = QApplication(sys.argv)
45if not createConnection():
46    sys.exit(1)
47win = Contacts()
48win.show()
49sys.exit(app.exec_())

Here’s what’s happening in this code:

  • Line 18 creates an editable QSqlTableModel obiekt.
  • Line 19 connects your model with the contacts table in your database using .setTable() .
  • Line 20 sets the edit strategy of the model to OnFieldChange . This strategy allows the model to automatically update the data in your database if the user modifies any of the data directly in the view.
  • Lines 21 to 24 set some user-friendly labels to the horizontal headers of the model using .setHeaderData() .
  • Line 25 loads the data from your database and populates the model by calling .select() .
  • Linia 27 creates the table view object to display the data contained in the model.
  • Linia 28 connects the view with the model by calling .setModel() on the view with your data model as an argument.
  • Line 29 calls .resizeColumnsToContents() on the view object to adjust the table to its content.

Otóż ​​to! You now have a fully-functional database application.




Using SQL Databases in PyQt:Best Practices

When it comes to using PyQt’s SQL support effectively, there are some best practices that you might want to use in your applications:

  • Favor PyQt’s SQL support over Python standard library or third-party libraries to take advantage of the natural integration of these classes with the rest of PyQt’s classes and infrastructure, mostly with the Model-View architecture.

  • Use previously prepared dynamic queries with placeholders for parameters and bind values to those parameters using .addBindValue() and .bindValue() . This will help prevent SQL injection attacks.

  • Handle errors that can occur when opening a database connection to avoid unexpected behaviors and application crashes.

  • Close and remove unneeded database connections and queries to free any acquired system resources.

  • Minimize the use of SELECT * queries to avoid problems when retrieving data with .value() .

  • Pass your passwords to .open() instead of to .setPassword() to avoid the risk of compromising your security.

  • Take advantage of PyQt’s Model-View architecture and its integration with PyQt’s SQL support to make your applications more robust.

This list isn’t complete, but it’ll help you make better use of PyQt’s SQL support when developing your database applications.



Conclusion

Using PyQt’s built-in support to work with SQL databases is an important skill for any Python developer who’s creating PyQt GUI applications and needs to connect them to a database. PyQt provides a consistent set of classes for managing SQL databases.

These classes fully integrate with PyQt’s Model-View architecture, allowing you to develop GUI applications that can manage databases in a user-friendly way.

In this tutorial, you’ve learned how to:

  • Use PyQt’s SQL support to connect to a database
  • Execute SQL queries on a database with PyQt
  • Build database applications using PyQt’s Model-View architecture
  • Display and edit data from a database using PyQt widgets

With this knowledge, you can improve your productivity when creating nontrivial database applications and make your GUI applications more robust.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak zamawiać alfabetycznie w SQL

  2. Dlaczego wiele sprzężeń JOIN jest szkodliwych dla zapytania lub nie przeszkadza w optymalizacji?

  3. Pobieranie XMLA do analizy struktury kostki

  4. Kompresja i jej wpływ na wydajność

  5. Jak usunąć kolumnę w tabeli