Python è cresciuto in modo esplosivo in popolarità negli ultimi 5 anni. Molti nuovi programmatori ne sono attratti a causa della sua curva di apprendimento delicata rispetto ad altri linguaggi di programmazione. I programmatori esperti ne sono attratti per la sua estensibilità e potenza. Tuttavia, uno dei principali driver di questa adozione di massa è la facilità con cui Python può lavorare con i database. Questo tutorial di programmazione Python esplorerà come iniziare a usare Python per comunicare con SQL Express usando Python 3.
Programmazione Python e Database
Un principiante Python che desidera integrare la funzionalità del database in qualsiasi progetto software scritto in qualsiasi la lingua deve avere una conoscenza di base di almeno due le lingue. Il primo è, ovviamente, Python e il secondo è lo specifico linguaggio di query strutturato (SQL) utilizzato dal database. Sebbene SQL sia standardizzato, in pratica non è un linguaggio universale, ma le implementazioni tra vari database sono abbastanza vicine che il passaggio da un database all'altro non rappresenta una grande sfida una volta che si ha un livello confortevole di esperienza nello sviluppo di applicazioni di database.
Un'altra considerazione importante per i database è che tutti richiedono un software di gestione. Questi strumenti di gestione possono aiutare a configurare l'accesso e i privilegi di sicurezza all'interno di un database. Possono anche essere usati per eseguire il debug di applicazioni di database in quanto possono consentire a uno sviluppatore di fare cose come:
- Creazione e gestione del contenuto di tabelle, viste e altri oggetti di database. Ciò include le relazioni tra le tabelle, nonché la configurazione delle regole di integrità.
- Interazione con il database direttamente tramite immissione di codice SQL.
- Debug della sintassi SQL.
- Annullamento (in una certa misura) del danno causato da istruzioni SQL codificate in modo errato nell'applicazione.
Anche se uno sviluppatore sceglie di utilizzare un database basato su noSQL come MongoDB, ci saranno comunque difficoltà nell'apprendimento della sintassi di codifica specifica del database necessaria per far funzionare tale soluzione. Naturalmente, questi non sono deterrenti, sono solo fattori tecnici che devono essere considerati in qualsiasi progetto di sviluppo software.
Driver Python e database
Python, come qualsiasi altro linguaggio di programmazione, non può comunicare in modo nativo con un determinato database. Richiede l'aggiunta di moduli aggiuntivi per un server di database specifico. Dal punto di vista delle migliori pratiche, è meglio utilizzare un modulo del driver di database specifico per il server di database scelto per il progetto software. Ciò garantisce che Python possa accedere a tutte le funzionalità del server di database, anche a costo aggiuntivo dell'utilizzo di una sintassi di programmazione specifica per il modulo. Sebbene siano stati fatti alcuni tentativi per creare moduli driver di database "universali" in grado di connettersi a più server di database, questi spesso vanno a scapito della perdita dell'accesso a determinate funzionalità di un particolare server di database.
Cos'è SQL Express?
SQL Server è da decenni la soluzione server di database di riferimento per Windows. Sebbene sia il più lontano possibile da una soluzione server di database gratuita, Microsoft fornisce una variante ridotta e a costo zero di SQL Server chiamata SQL Express. SQL Express è uno strumento di apprendimento ideale per i principianti poiché supporta la stessa sintassi SQL utilizzata da SQL Server. Sia SQL Express che SQL Server utilizzano un'estensione personalizzata di SQL denominata "Transact-SQL", nota anche come "T-SQL". Sia SQL Express che SQL Server supportano l'uso di account utente Windows e i tradizionali sistemi di nome utente e password per la gestione degli accessi.
Python comunica con SQL Express o SQL Server con un modulo denominato PyODBC . Sia SQL Server che SQL Express sono gestiti da un'applicazione Windows separata a costo zero denominata "SQL Server Management System", popolarmente nota come "SSMS". Al momento della stesura di questo documento, sia SQL Express che SSMS sono download separati da Microsoft:
- Scarica SSMS
- Download di SQL Express
Come configurare SQL Express per lo sviluppo Python
SQL Express, come SQL Server, supporta due tipi di autenticazione. Il primo è l'autenticazione basata sull'account utente Windows di un utente, noto anche come "Connessione attendibile". Il secondo è l'autenticazione tradizionale basata su nome utente e password implementata in quella che viene chiamata "Autenticazione in modalità mista". L'autenticazione in modalità mista supporta sia l'autenticazione basata sull'account utente di Windows sia l'autenticazione basata su nome utente e password. Non è possibile supportare l'autenticazione basata su nome utente e password da sola in SQL Server o SQL Express.
Microsoft si è allontanata dall'autenticazione in modalità mista, poiché uno dei principali vantaggi dell'utilizzo di connessioni affidabili è che le credenziali del database non devono essere archiviate nel codice dell'applicazione. Anche la dimostrazione in questo articolo non lo utilizzerà.
NON copiare la stringa di connessione dopo l'installazione
Un punto controverso per gli sviluppatori di applicazioni di livello principiante è la confusione iniziale che circonda le stringhe di connessione di SQL Server. Se si installa SQL Express, il programma di installazione fornisce la stringa di connessione per l'istanza SQL Express creata dopo l'installazione. Sfortunatamente, la stringa di connessione fornita probabilmente non funzionerà con PyODBC . Sebbene sia allettante essere "cullati" in un senso di sicurezza con questo "omaggio", causerà più problemi di quanti ne valga la pena.
Figura 1 – Ottenere la stringa di connessione dal programma di installazione SQL Express
Si noti che, al momento della stesura di questo documento, il programma di installazione per SQL Express include anche un collegamento per scaricare il programma di installazione di SSMS.
Come creare un database in SQL Express
Una volta installati SQL Express e SSMS, è il momento di creare un database di base con restrizioni di accesso adeguate. Il modo più semplice per avviare SSMS è fare clic su Avvia in Windows, digita "ssms" nella barra di ricerca, attendi che "Microsoft SQL Server Management Studio 18" appaia in alto a destra, quindi fai clic su Apri collegamento nella parte destra del pannello del menu Start:
Figura 2 – Avvio di SSMS
All'avvio di SSMS, si viene accolti dalla seguente finestra di dialogo:
Figura 3 – Finestra di dialogo di apertura SSMS
Con l'autenticazione di Windows, non è necessario immettere alcuna credenziale. L'Account utente Windows in cui è stato installato SQL Express dispone dei privilegi di amministratore per l'istanza di SQL Express. Fai semplicemente clic su Connetti per procedere.
All'estrema sinistra della Finestra dell'applicazione SSMS , ci sarà Esplora oggetti . Per creare un nuovo database, fai clic con il pulsante destro del mouse su Database e seleziona Crea database dal Contesto menu:
Figura 4 – Creazione di un nuovo database – Parte 1 di 2
Facendo clic su Nuovo database... si aprirà una nuova finestra di dialogo che consente di inserire i dettagli del nuovo database. Per questa dimostrazione, il database si chiamerà RazorDemo , come un ritorno al passato di un articolo precedente sullo sviluppo di applicazioni basate su Razor in C#. Inserisci il nome del database nella casella di testo accanto a Nome database e quindi fare clic su OK pulsante nella parte inferiore della finestra di dialogo. Nota che, nell'illustrazione seguente, le colonne per il Nome logico dei file sono stati leggermente allargati in modo che i Nomi logici completi dei file di database in fase di creazione sono stati esposti:
Figura 5 – Creazione di un nuovo database – Parte 2 di 2
Il nuovo database apparirà quindi in Esplora oggetti sotto i Banche dati cartella:
Figura 6 – Il database "RazorDemo" appena creato
Come creare tabelle in SQL Express
Un database relazionale non è molto utile senza tabelle per archiviare i dati e il modo più semplice per creare tali tabelle è utilizzare il codice SQL. Nota, mentre si potrebbe utilizzare la Procedura guidata per la creazione di tabelle per creare una tabella, l'utilizzo del codice SQL è più veloce, più semplice e molto più diretto. Inizia facendo clic con il pulsante destro del mouse su RazorDemo voce del database, quindi fare clic con il pulsante sinistro del mouse su Nuova query opzione nella finestra di contesto:
Figura 7 – Apertura di una nuova finestra di query
A destra di Esplora oggetti verrà visualizzata una finestra dell'editor di query simile a quella seguente :
Figura 8 – La finestra dell'editor di query
Il codice di creazione della tabella è mostrato nell'elenco seguente:
use RazorDemo; # See the Important Note below create table artists (rcdid int not null identity primary key, artist_name varchar(max)); create table albums (rcdid int not null identity primary key, artist_id int not null references artists(rcdid) on delete cascade, album_name varchar(max)); Listing 1 - Table Creation SQL Code
Nota, durante la creazione di una finestra dell'editor di query dal database di solito garantisce che il database selezionato sarà quello su cui viene eseguito il codice, è buona norma utilizzare sempre esplicitamente usare il database previsto all'inizio del codice. L'uso comando seleziona esplicitamente il nome del database che lo segue.
Premendo F5 o facendo clic su Esegui il pulsante eseguirà le istruzioni rispetto a RazorDemo Banca dati. Se l'esecuzione ha esito positivo, verrà visualizzato un messaggio che lo indica in "Messaggi casella sotto:
Figura 9 – Creazione tabella riuscita
Le tabelle appena create e le relative colonne possono essere visualizzate in Esplora oggetti anche. Tieni presente che a volte Aggiorna potrebbe essere necessario selezionare l'opzione dal menu contestuale che appare facendo clic con il pulsante destro del mouse per mostrare i nuovi oggetti all'interno di un database:
Figura 10 – Aggiornamento di Esplora oggetti
Figura 11 – Le nuove tabelle e le relative colonne
A questo punto, SSMS può essere chiuso in sicurezza.
Si noti che SSMS funziona allo stesso modo anche con qualsiasi database di SQL Server. È sempre consigliabile salvare tutto il codice di creazione delle tabelle, indipendentemente dal server di database utilizzato. Anche se SQL Server e SQL consentono il ripristino di tali script, entrambi consentono l'uso della crittografia anche su tali istruzioni e, in questi casi, il codice non può essere ripristinato.
Python e SQL Express
Normalmente, qui sarebbe necessaria una discussione sulla sicurezza di SQL Server, ma poiché Connessioni attendibili verrà utilizzato, purché il processo in esecuzione che esegue il codice Python sia di proprietà di un Utente Windows che ha già accesso a un database a cui si sta accedendo, quella discussione non sarà necessaria. Tieni presente che sia SQL Server che SQL Express offrono personalizzazioni relative alla sicurezza molto solide, ma non rientrano nell'ambito di un articolo destinato ai principianti.
Nota, non trascurare la corretta sicurezza del database per qualsiasi applicazione in esecuzione in un ambiente di produzione! Assicurati che vengano assegnati solo i privilegi minimi possibili all'account utente che accederebbe a un database in un ambiente di proiezione.
La versione di Python utilizzata per questi esempi di codice è la 3.10 ed è stata installata tramite Microsoft Store in Windows. L'installazione di Python con questo metodo aggiungerà gli eseguibili Python e PIP3 al percorso di sistema, in modo che i percorsi completi di questi comandi non debbano essere digitati nel Prompt dei comandi finestre. Per l'immissione del codice, un buon editor di testo a costo zero è Notepad++.
Apertura delle finestre del prompt dei comandi
È meglio eseguire il codice Python tramite il Prompt dei comandi . Per accedere al Prompt dei comandi , fai clic su Avvia pulsante in Windows e inserisci cmd nella barra di ricerca. Attendi il Prompt dei comandi per visualizzare e quindi fare clic su Apri link sul lato destro del menu Start :
Figura 12 – Apertura di un prompt dei comandi
Un tipico prompt dei comandi la finestra si presenta così:
Figura 13 – Un tipico prompt dei comandi
Come installare PyODBC
PyODBC è il modulo Python che consente a Python di accedere sia a SQL Server che a SQL Express. Dopo l'installazione di Python tramite Microsoft Store, PyODBC può essere aggiunto a Python tramite il comando:
pip3 install pyodbc
Figura 14 – Installazione riuscita di PyODBC
Nota, se sono installate più versioni di Python, ad esempio sia Python 2 che Python 3, potrebbe essere necessario anteporre pip3 comando con il percorso completo di Windows al comando per la versione appropriata di Python.
Nota inoltre che se è installato solo Python 3, pip3 il comando dovrebbe essere comunque utilizzato sopra il più generico pip comando, poiché questa è la convenzione corretta.
Scrittura di codice Python
Ora che il database è configurato e PyODBC sono installati, il database può essere popolato. Nel caso di un database che cataloghi artisti e album, alcuni nomi di band e album generati casualmente saranno sufficienti. È incluso anche il codice Python per connettersi al database, ma gli inserti non lo sono (ancora):
# bad-band-name-maker.py import sys import random import pyodbc part1 = ["The", "Uncooked", "Appealing", "Larger than Life", "Drooping", "Unwell", "Atrocious", "Glossy", "Barrage", "Unlawful"] part2 = ["Defeated", "Hi-Fi", "Extraterrestrial", "Adumbration", "Limpid", "Looptid", "Cromulent", "Unsettled", "Soot", "Twinkle"] part3 = ["Brain", "Segment", "Audio", "Legitimate Business", "Mentality", "Sound", "Canticle", "Monsoon", "Preserves", "Hangout"] part4 = ["Cougar", "Lion", "Lynx", "Ocelot", "Puma", "Jaguar", "Panther"] part5 = ["Fodder", "Ersatz Goods", "Leftovers", "Infant Formula", "Mush", "Smoothie", "Milkshakes"] def main(argv): # Connect to the RazorDemo database. conn = pyodbc.connect("Driver={ODBC Driver 17 for SQL Server};Server=localhost\SQLEXPRESS;Database=RazorDemo;Trusted_Connection=yes;") # Generate 15 bad band names: for x in range(1, 16): rand1 = random.randrange(0, 9) rand2 = random.randrange(0, 9) rand3 = random.randrange(0, 9) badName = part1[rand1] + ' ' + part2[rand2] + ' ' + part3[rand3] print ("Band name [" + str(x) + "] is [" + badName + "]") for y in range(1, 3): rand4 = random.randrange(0, len(part4)) rand5 = random.randrange(0, len(part5)) albumName = part4[rand4] + " " + part5[rand5] print ("\tAlbum [" + albumName + "]") # Close the Connection conn.close() return 0 if __name__ == "__main__": main(sys.argv[1:]) Listing 2 - Making up some data
Questo dà il seguente output:
Figura 15 – Nomi delle bande generati casualmente
Nota l'uso del cd comando per passare alla directory in cui è salvato il codice Python. Il PyODBC connetti() la funzione fallirà se l'Account utente Windows attualmente connesso non è elencato come avente accesso in SQL Express. Questo è un problema solo se il database viene creato con un account utente Windows ma il codice viene eseguito con un account utente Windows diverso .
Il modo meno peggiore per INSERIRE dati in SQL e PyODBC
Molti sviluppatori Python principianti sono tentati di effettuare chiamate a PyODBC chiamate a INSERIRE affermazioni nelle seguenti sezioni del codice, e nel contesto di quanto verrà affermato in seguito, questa non è una cattiva idea:
Figura 16 – Il modo "quasi" sbagliato di lavorare con un database
Il motivo per cui utilizzare le chiamate a PyODBC per eseguire INSERTI , SELEZIONA e altre funzioni relative al database, come UPDATE o ELIMINA , all'interno dei loop può essere dannoso perché c'è un sovraccarico che deriva da ciascuna di queste chiamate. All'interno di un ciclo che potrebbe potenzialmente iterare centinaia di volte, migliaia di volte o anche di più, ciò può comportare una notevole quantità di tempo (minuti o più) per l'esecuzione di uno script. Per le applicazioni Web che utilizzano un tale approccio, i problemi di prestazioni si aggravano ulteriormente, poiché molti server Web impongono limiti rigidi alla quantità di tempo che uno script può essere eseguito. Non passare mai in nessun caso gli input creati dall'utente direttamente a un database. Controlla sempre l'input per assicurarti che non interrompa la funzionalità del database o causi problemi di sicurezza tramite un attacco SQL Injection.
Idealmente, si vorrebbe utilizzare i cicli precedenti per creare un batch SQL (un elenco di istruzioni) e quindi avere PyODBC eseguito su quel singolo batch, ma questa sarebbe una pessima idea se i dati non vengono disinfettati.
Perché i dati dovrebbero essere sanificati? Il motivo si riduce alla sicurezza, poiché l'input dell'utente non può mai essere considerato attendibile. Sanificare i dati significa rappresentarli in un modo che impedisce l'esecuzione di qualcosa di diverso dall'istruzione SQL creata dallo sviluppatore del programma. Un utente potrebbe passare una stringa costruita in modo dannoso che consentirebbe l'esecuzione di codice SQL creato in modo arbitrario. Questo è noto come attacco SQL Injection. Sebbene i valori dei dati inseriti in un batch possano essere disinfettati, il processo per farlo va oltre lo scopo di un tutorial introduttivo.
PyODBC fornisce un meccanismo per proteggere il database dagli attacchi SQL Injection disinfettando gli input degli utenti. Questi implicano l'uso di dichiarazioni parametrizzate , indicato anche come dichiarazioni preparate . La sicurezza deve essere sempre una priorità, anche a scapito della velocità o di altri parametri di prestazione.
L'account utente di Windows che ha accesso a questo database ha per impostazione predefinita i privilegi di amministratore di sistema. Ciò significa che se si verifica un attacco SQL Injection, un utente malintenzionato può accedere a tutti i dati in ogni database sul server. In pratica, nessun account con privilegi di amministratore di sistema dovrebbe accedere a qualsiasi database dal codice Python.
L'elenco seguente amplia il primo esempio di codice Python utilizzando PyODBC cursori per inserire i dati:
# bad-band-name-maker2.py import sys import random import pyodbc part1 = ["The", "Uncooked", "Appealing", "Larger than Life", "Drooping", "Unwell", "Atrocious", "Glossy", "Barrage", "Unlawful"] part2 = ["Defeated", "Hi-Fi", "Extraterrestrial", "Adumbration", "Limpid", "Looptid", "Cromulent", "Unsettled", "Soot", "Twinkle"] part3 = ["Brain", "Segment", "Audio", "Legitimate Business", "Mentality", "Sound", "Canticle", "Monsoon", "Preserves", "Hangout"] part4 = ["Cougar", "Lion", "Lynx", "Ocelot", "Puma", "Jaguar", "Panther"] part5 = ["Fodder", "Ersatz Goods", "Leftovers", "Infant Formula", "Mush", "Smoothie", "Milkshakes"] def main(argv): # Connect to the RazorDemo database. conn = pyodbc.connect("Driver={ODBC Driver 17 for SQL Server};Server=localhost\SQLEXPRESS;Database=RazorDemo;Trusted_Connection=yes;") # Generate 15 bad band names, and try to keep them unique. previousNames = "" nameCount = 0 while (nameCount < 16): rand1 = random.randrange(0, 9) rand2 = random.randrange(0, 9) rand3 = random.randrange(0, 9) badName = part1[rand1] + ' ' + part2[rand2] + ' ' + part3[rand3] # A crude but effective way of ensuring uniqueness, although there is no unique constraint on the artist name in the database. # This prepends and appends bars to both the list of previously used names and the current name. If the current name is # new, it will not be in that string. if ("|" + previousNames + "|").find("|" + badName + "|") == -1: print ("Band name [" + str(nameCount) + "] is [" + badName + "]") sql1 = "insert into artists (artist_name) values (?)" values1 = [badName] rs1 = conn.cursor() rs1.execute(sql1, values1) rs1.commit() # If the cursor is not closed, then other cursors cannot be executed. rs1.close() for y in range(1, 3): rand4 = random.randrange(0, len(part4)) rand5 = random.randrange(0, len(part5)) albumName = part4[rand4] + " " + part5[rand5] print ("\tAlbum [" + albumName + "]") sql2 = "insert into albums (artist_id, album_name) values ((select top 1 rcdid from artists where artist_name=?), ?)" # Each array item here corresponds to the position of the ? in the SQL statement above. values2 = [badName, albumName] rs2 = conn.cursor () rs2.execute(sql2, values2) rs2.commit() rs2.close() # Creates a bar-delimited list of previously used names. if previousNames == "": previousNames = badName else: previousNames = previousNames + "|" + badName nameCount = 1 + nameCount else: print ("Found a duplicate of [" + badName + "]") #print (previousNames) # Close the Connection conn.close() return 0 if __name__ == "__main__": main(sys.argv[1:]) Listing 3 - Inserting the data
La query seguente può essere eseguita in SSMS per verificare l'output del codice:
Figura 17 – L'inserimento riuscito dei dati
SELEZIONE dei dati in SQL Express e Python
Ora che ci sono dati nel database, sarebbe bello interrogarli. Di seguito è riportato un semplice script che accetta i dati utente dalla tastiera e li passa nel database tramite query parametrizzata:
# bad-band-name-maker3.py import sys import pyodbc def main(argv): searchValue = input("Enter something: ") # Cap the length at something reasonable. The first 20 characters. searchValue = searchValue[0:20] # Set the search value to lower case so we can perform case-insensitive matching: searchValue = searchValue.lower() # Connect to the RazorDemo database. conn = pyodbc.connect("Driver={ODBC Driver 17 for SQL Server};Server=localhost\SQLEXPRESS;Database=RazorDemo;Trusted_Connection=yes;") # You must use a parameterized query here in order to protect from SQL Injection Attacks! # For the like operator, the percent signs must be separated from the term or else the parameterization will fail. sql1 = ("select a.artist_name, b.album_name from artists a, albums b where b.artist_id = a.rcdid and " + "lower(album_name) like ('%' + ? + '%') order by a.artist_name, b.album_name") # Below is an array with one element: values1 = [searchValue] rs1 = conn.cursor() rs1.execute(sql1, values1) rows1 = rs1.fetchone() #print ("Type is [" + str(type(rows1)) + "]") if str(type(rows1)).find("NoneType") == -1: while rows1: # Columns are indexed by number only. 0 is the a.artist_name column and 1 is the b.album_name columns print(rows1[0] + " - " + rows1[1]) rows1 = rs1.fetchone() else: print ("No album name matched [" + searchValue + "]") # Close the Connection conn.close() return 0 if __name__ == "__main__": main(sys.argv[1:]) Listing 4 - Querying the Data
Stessi risultati. Uno include anche un esempio di un attacco SQL Injection grezzo:
Figura 18 – Risultati della query. Prendere nota dell'ultimo valore di ricerca.
Considerazioni finali sullo sviluppo del database Python
Non c'è davvero limite a quali applicazioni basate su SQL Server possono essere sviluppate utilizzando Python. Uno sviluppatore è limitato solo dalla conoscenza di SQL e si spera che i concetti fondamentali presentati in questo articolo possano indirizzare uno sviluppatore di livello principiante nella giusta direzione in termini di ampliamento della propria comprensione di SQL e creazione di applicazioni più complesse.
Questo tutorial sulla programmazione del database Python ha presentato i mezzi con cui SQL Express può essere installato come sostituto dello sviluppo per SQL Server e ha mostrato come è possibile estendere Python 3 per comunicare correttamente con un'istanza del database SQL Express su quel server. Questo articolo ha anche mostrato come usare SQL Server Management Studio per gestire qualsiasi database SQL Express o SQL Server. Andando oltre, questo articolo ha anche toccato le precauzioni di sicurezza di base e l'uso di una codifica efficiente allo scopo di garantire tempi di esecuzione ragionevoli per le applicazioni basate su SQL Server.