Python cresceu explosivamente em popularidade nos últimos 5 anos. Muitos novos programadores são atraídos por ela por causa de sua curva de aprendizado suave em relação a outras linguagens de programação. Programadores experientes são atraídos por ele por causa de sua extensibilidade e poder. No entanto, um dos principais impulsionadores dessa adoção em massa é a facilidade com que o Python pode trabalhar com bancos de dados. Este tutorial de programação Python explorará como começar a usar o Python para se comunicar com o SQL Express usando o Python 3.
Python e programação de banco de dados
Um iniciante em Python que deseja integrar a funcionalidade de banco de dados em qualquer projeto de software escrito em qualquer linguagem deve ter uma compreensão básica de pelo menos dois línguas. O primeiro é, obviamente, Python e o segundo é a linguagem de consulta estruturada específica (SQL) usada pelo banco de dados. Embora o SQL seja padronizado, na prática não é uma linguagem universal, mas as implementações entre vários bancos de dados são próximas o suficiente para que a mudança de um banco de dados para outro não seja um grande desafio, uma vez que se tenha um nível confortável de experiência em desenvolvimento de aplicativos de banco de dados.
Outra consideração importante para bancos de dados é que todos eles requerem software de gerenciamento. Essas ferramentas de gerenciamento podem ajudar na configuração de acesso e privilégios de segurança em um banco de dados. Eles também podem ser usados para depurar aplicativos de banco de dados, pois podem permitir que um desenvolvedor faça coisas como:
- Criação e gerenciamento do conteúdo de tabelas, visualizações e outros objetos de banco de dados. Isso inclui os relacionamentos entre tabelas, bem como a configuração de regras de integridade.
- Interação com o banco de dados diretamente via entrada de código SQL.
- Depuração da sintaxe SQL.
- Desfazer (até certo ponto) os danos causados por instruções SQL codificadas incorretamente no aplicativo.
Mesmo que um desenvolvedor opte por usar um banco de dados baseado em noSQL, como o MongoDB, ainda haverá desafios para aprender a sintaxe de codificação específica do banco de dados necessária para fazer essa solução funcionar. Claro, estes não são impedimentos, são apenas fatores técnicos que devem ser considerados em qualquer projeto de desenvolvimento de software.
Python e drivers de banco de dados
Python, como qualquer outra linguagem de programação, não pode se comunicar nativamente com um determinado banco de dados. Requer módulos extras a serem adicionados para um servidor de banco de dados específico. Do ponto de vista das melhores práticas, é melhor usar um módulo de driver de banco de dados específico para o servidor de banco de dados escolhido para o projeto de software. Isso garante que o Python possa acessar todos os recursos do servidor de banco de dados, mesmo que isso tenha o custo adicional de usar uma sintaxe de programação específica para o módulo. Embora algumas tentativas tenham sido feitas para criar módulos de driver de banco de dados “universais” que podem se conectar a vários servidores de banco de dados, isso geralmente ocorre à custa da perda de acesso a determinados recursos de um servidor de banco de dados específico.
O que é SQL Express?
O SQL Server é a solução de servidor de banco de dados para Windows há décadas. Embora esteja o mais longe possível de uma solução de servidor de banco de dados gratuita, a Microsoft fornece uma variante simplificada e de custo zero do SQL Server chamada SQL Express. O SQL Express é uma ferramenta de aprendizado ideal para iniciantes, pois oferece suporte à mesma sintaxe SQL que o SQL Server usa. Tanto o SQL Express quanto o SQL Server usam uma extensão personalizada do SQL chamada “Transact-SQL”, também conhecida como “T-SQL”. Tanto o SQL Express quanto o SQL Server suportam o uso de contas de usuário do Windows e sistemas tradicionais de nome de usuário e senha para gerenciamento de acesso.
Python se comunica com SQL Express ou SQL Server com um módulo chamado PyODBC . Tanto o SQL Server quanto o SQL Express são gerenciados por um aplicativo Windows separado e de custo zero chamado “SQL Server Management System”, popularmente conhecido como “SSMS”. No momento da redação deste artigo, o SQL Express e o SSMS são downloads separados da Microsoft:
- Download do SSMS
- Download do SQL Express
Como configurar o SQL Express para desenvolvimento em Python
O SQL Express, como o SQL Server, oferece suporte a dois tipos de autenticação. A primeira é a autenticação baseada na conta de usuário do Windows de um usuário, também conhecida como “Conexão Confiável”. A segunda é a autenticação tradicional baseada em nome de usuário e senha, implementada no que é chamado de “Autenticação de modo misto”. A autenticação de modo misto dá suporte à autenticação baseada em conta de usuário do Windows e à autenticação baseada em nome de usuário e senha. Não há como oferecer suporte à autenticação baseada em nome de usuário e senha por si só no SQL Server ou no SQL Express.
A Microsoft está se afastando da autenticação de modo misto, pois uma das principais vantagens do uso de conexões confiáveis é que as credenciais do banco de dados não precisam ser armazenadas no código do aplicativo. A demonstração neste artigo também não será usada.
NÃO copiando a cadeia de conexão após a instalação
Um ponto de discórdia para desenvolvedores de aplicativos de nível iniciante é a confusão inicial em torno das cadeias de conexão do SQL Server. Se alguém estiver instalando o SQL Express, o programa de instalação fornecerá a cadeia de conexão para a pós-instalação da instância do SQL Express criada. Infelizmente, a string de conexão fornecida provavelmente não funcionará com PyODBC . Embora seja tentador ser “acalentado” por uma sensação de segurança com esse “brinde”, isso causará mais problemas do que vale a pena.
Figura 1 – Obtendo a string de conexão do SQL Express Installer
Observe que, no momento da redação deste artigo, o programa de instalação do SQL Express também inclui um link para fazer download do programa de instalação do SSMS.
Como criar um banco de dados no SQL Express
Depois que o SQL Express e o SSMS estiverem instalados, é hora de criar um banco de dados básico com restrições de acesso adequadas. A maneira mais fácil de iniciar o SSMS é clicar no botão Iniciar botão no Windows, digite “ssms” na barra de pesquisa, aguarde “Microsoft SQL Server Management Studio 18” aparecer no canto superior direito e clique no botão Abrir link no lado direito do painel do menu Iniciar:
Figura 2 – Iniciando o SSMS
Ao iniciar o SSMS, é recebido pela seguinte caixa de diálogo:
Figura 3 – Caixa de diálogo de abertura do SSMS
Com a Autenticação do Windows, não há necessidade de inserir credenciais. A Conta de usuário do Windows sob a qual o SQL Express foi instalado tem privilégios administrativos para a instância do SQL Express. Basta clicar em Conectar para prosseguir.
No lado esquerdo da Janela do aplicativo SSMS , haverá o Explorador de Objetos . Para criar um novo banco de dados, clique com o botão direito do mouse em Bancos de dados e selecione Criar banco de dados do Contexto cardápio:
Figura 4 – Criando um novo banco de dados – Parte 1 de 2
Clicando em Novo banco de dados… irá abrir uma nova janela de diálogo que permite que os detalhes do novo banco de dados sejam inseridos. Para esta demonstração, o banco de dados será chamado de RazorDemo , como um retorno a um artigo anterior sobre o desenvolvimento de aplicativos baseados em Razor em C#. Digite o nome do banco de dados na caixa de texto ao lado de Nome do banco de dados e clique em OK botão na parte inferior da janela de diálogo. Observe que, na ilustração abaixo, as colunas para o Nome lógico dos arquivos foram ligeiramente alargados para que os Nomes lógicos completos dos arquivos de banco de dados que estão sendo criados foram expostos:
Figura 5 – Criando um novo banco de dados – Parte 2 de 2
O novo banco de dados aparecerá no Object Explorer em Bancos de dados pasta:
Figura 6 – O recém-criado banco de dados “RazorDemo”
Como criar tabelas no SQL Express
Um banco de dados relacional não é realmente útil sem tabelas para armazenar os dados, e a maneira mais direta de criar essas tabelas é usar o código SQL. Observe que, embora seja possível usar o Assistente de criação de tabela para criar uma tabela, usar código SQL é mais rápido, fácil e muito mais direto. Comece clicando com o botão direito do mouse em RazorDemo entrada do banco de dados e clique com o botão esquerdo em Nova consulta opção na janela de contexto:
Figura 7 – Abrindo uma nova janela de consulta
Uma janela do editor de consulta semelhante à abaixo aparecerá à direita do Explorador de objetos :
Figura 8 – A janela do editor de consultas
O código de criação da tabela é mostrado na listagem abaixo:
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
Observe que, ao criar uma Janela do Editor de Consulta do banco de dados geralmente garante que o banco de dados selecionado será aquele contra o qual o código é executado, é uma boa idéia sempre usar explicitamente o banco de dados pretendido no início do código. O uso O comando seleciona explicitamente o nome do banco de dados que o segue.
Pressionando F5 ou clicando no botão Executar botão irá executar as instruções contra o RazorDemo base de dados. Se a execução for bem-sucedida, uma mensagem indicando isso aparecerá no campo “Mensagens caixa abaixo:
Figura 9 – Criação de tabela bem-sucedida
As tabelas recém-criadas e suas colunas podem ser vistas no Object Explorer também. Observe que, às vezes, o Atualizar A opção do menu de contexto que aparece ao clicar com o botão direito do mouse no banco de dados pode precisar ser selecionada para mostrar novos objetos dentro de um banco de dados:
Figura 10 – Atualizando o Pesquisador de Objetos
Figura 11 – As novas tabelas e suas colunas
Neste ponto, o SSMS pode ser fechado com segurança.
Observe que o SSMS também funciona da mesma maneira com qualquer banco de dados SQL Server. É sempre uma prática recomendada salvar todo o código de criação de tabela, independentemente do servidor de banco de dados que está sendo usado. Embora o SQL Server e o SQL permitam a recuperação desses scripts, ambos também permitem o uso de criptografia em tais instruções e, nesses casos, o código não pode ser recuperado.
Python e SQL Express
Normalmente, uma discussão sobre a segurança do SQL Server seria necessária aqui, mas como Conexões confiáveis será usado, desde que o processo em execução que executa o código Python seja de propriedade de um usuário do Windows que já tem acesso a um banco de dados que está sendo acessado, essa discussão não será necessária. Lembre-se de que o SQL Server e o SQL Express oferecem personalizações relacionadas à segurança muito robustas, mas elas estão além do escopo de um artigo destinado a iniciantes.
Observe que não negligencie a segurança de banco de dados adequada para qualquer aplicativo executado em um ambiente de produção! Certifique-se de que apenas os privilégios mínimos possíveis sejam concedidos à conta de usuário que acessaria um banco de dados em um ambiente de projeção.
A versão do Python usada para esses exemplos de código é 3.10 e foi instalada por meio da Microsoft Store no Windows. A instalação do Python com este método adicionará os executáveis Python e PIP3 ao caminho do sistema, para que os caminhos completos para esses comandos não precisem ser digitados no Prompt de comando janelas. Para entrada de código, um bom editor de texto de custo zero é o Notepad++.
Abrindo janelas de prompt de comando
A execução do código Python é melhor feita por meio do Prompt de comando . Para acessar o Prompt de comando , clique em Iniciar botão no Windows e digite cmd na barra de pesquisa. Aguarde o Prompt de Comando para aparecer e, em seguida, clique no botão Abrir link no lado direito do Menu Iniciar :
Figura 12 – Abrindo um prompt de comando
Um Prompt de Comando típico janela fica assim:
Figura 13 – Um prompt de comando típico
Como instalar o PyODBC
PyODBC é o módulo Python que permite que o Python acesse o SQL Server e o SQL Express. Após a instalação do Python por meio da Microsoft Store, PyODBC pode ser adicionado ao Python através do comando:
pip3 install pyodbc
Figura 14 – Instalação bem-sucedida do PyODBC
Observe que, se houver várias versões do Python instaladas, por exemplo, Python 2 e Python 3, pode ser necessário prefixar o pip3 command com o caminho completo do Windows para o comando da versão apropriada do Python.
Observe também que se apenas o Python 3 estiver instalado, o pip3 comando ainda deve ser usado sobre o mais genérico pip comando, pois esta é a convenção apropriada.
Escrevendo código Python
Agora que o banco de dados está configurado e PyODBC estão instalados, o banco de dados pode ser preenchido. No caso de um banco de dados que cataloga artistas e álbuns, alguns nomes de bandas e álbuns gerados aleatoriamente serão suficientes. O código Python para conectar ao banco de dados também está incluído, mas as inserções (ainda não):
# 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
Isso dá a seguinte saída:
Figura 15 – Nomes de banda gerados aleatoriamente
Observe o uso do cd comando para mudar para o diretório onde o código Python é salvo. O PyODBC conectar() falhará se a Conta de usuário do Windows atualmente conectada não está listado como tendo acesso no SQL Express. Isso só é um problema se o banco de dados for criado com uma Conta de usuário do Windows mas o código é executado em uma Conta de usuário do Windows diferente .
A pior maneira de inserir dados em SQL e PyODBC
Muitos desenvolvedores Python iniciantes são tentados a fazer chamadas para PyODBC chamadas para INSERIR instruções nas seguintes seções do código e no contexto do que será declarado a seguir, isso não é uma má ideia:
Figura 16 – A maneira “quase” errada de trabalhar com um banco de dados
O motivo pelo qual usar chamadas para PyODBC para executar INSERTs , SELEÇÕES , e outras funções relacionadas ao banco de dados, como UPDATE ou EXCLUIR , dentro de loops pode ser ruim porque há sobrecarga que vem com cada uma dessas chamadas. Dentro de um loop que poderia iterar centenas de vezes, milhares de vezes ou até mais, isso pode resultar em uma quantidade significativa de tempo (minutos ou mais) para executar um script. Para aplicativos da Web que usam essa abordagem, os problemas de desempenho aumentam ainda mais, pois muitos servidores da Web impõem limites rígidos na quantidade de tempo que um script pode ser executado. Nunca, sob nenhuma circunstância, passe entradas criadas pelo usuário diretamente para um banco de dados. Sempre verifique a entrada para certificar-se de que ela não quebrará a funcionalidade do banco de dados ou causará um problema de segurança por meio de um ataque de injeção de SQL.
O ideal seria usar os loops acima para criar um SQL Batch (uma lista de instruções) e então ter PyODBC executado nesse único lote, mas isso seria uma péssima ideia se os dados não fossem higienizados.
Por que os dados precisam ser higienizados? O motivo se resume à segurança, pois a entrada do usuário nunca pode ser confiável. Sanitizar os dados significa representá-los de uma forma que impeça a execução de qualquer coisa que não seja a instrução SQL criada pelo desenvolvedor do programa. Um usuário pode passar uma string construída de forma maliciosa que permitiria a execução de código SQL criado arbitrariamente. Isso é conhecido como um ataque de injeção de SQL. Embora os valores de dados que entram em um lote possam ser higienizados, o processo para fazer isso está além do escopo de um tutorial introdutório.
PyODBC fornece um mecanismo para proteger o banco de dados contra ataques de injeção de SQL ao limpar as entradas do usuário. Elas envolvem o uso de instruções parametrizadas , também conhecido como declarações preparadas . A segurança deve sempre ser uma prioridade, mesmo que custe a velocidade ou outras métricas de desempenho.
A conta de usuário do Windows que tem acesso a esse banco de dados tem, por padrão, privilégios de sysadmin. Isso significa que, caso ocorra um ataque de injeção de SQL, um usuário mal-intencionado pode obter acesso a todos os dados em todos os bancos de dados do servidor. Na prática, nenhuma conta com privilégios sysadmin deve acessar qualquer banco de dados do código Python.
A listagem abaixo aumenta o primeiro exemplo de código Python usando PyODBC cursores para inserir os dados:
# 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
A seguinte consulta pode ser executada no SSMS para verificar a saída do código:
Figura 17 – A inserção bem-sucedida dos dados
Selecionando dados no SQL Express e Python
Agora que há dados no banco de dados, seria bom consultá-los. Abaixo está um script simples que aceita os dados do usuário do teclado e os passa para o banco de dados via consulta parametrizada:
# 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
Mesmos resultados. Um inclui até um exemplo de um ataque bruto de injeção de SQL:
Figura 18 – Resultados da Consulta. Observe o último valor de pesquisa.
Considerações finais sobre o desenvolvimento de banco de dados Python
Realmente não há limite para quais aplicativos orientados ao SQL Server podem ser desenvolvidos usando Python. Um desenvolvedor é limitado apenas pelo conhecimento de SQL, e esperamos que os conceitos fundamentais apresentados neste artigo possam apontar um desenvolvedor de nível iniciante na direção certa em termos de ampliar a compreensão de SQL e construir aplicativos mais complexos.
Este tutorial de programação de banco de dados Python apresentou meios pelos quais o SQL Express pode ser instalado como um substituto de desenvolvimento para o SQL Server e mostrou como o Python 3 pode ser estendido para se comunicar adequadamente com uma instância de banco de dados SQL Express nesse servidor. Este artigo também mostrou como o SQL Server Management Studio deve ser usado para gerenciar qualquer banco de dados SQL Express ou SQL Server. Indo além, este artigo também abordou as precauções básicas de segurança e o uso de codificação eficiente para garantir tempos de execução razoáveis para aplicativos controlados pelo SQL Server.