March 4

Python: SqlAlchemy

a biblioteca responsável pela unificação global se chama sqlalchemy. Nela os comandos são universais, ou seja, se está usando MySql, e deseja mudar para PosgreSQL. Você não vai precisar escrever um novo código, o que será necessário é mudar nas configurações de conexão da sua instancia SQLalchemy e colocar que está usando PostgreSql.

Configurações Iniciais:

from sqlalchemy import create_engine, Column, Integer, String, ForegnKey

=> Nessa linha acima estou importando o create_engine que é responsável por fazer a conexão com o banco de dados, além disso estou importando o Column que é responsável por criar Colunas no Banco de dados. Também estou importando o tipo de dados que vou usar para criar as Tabelas de cada Coluna.

from sqlalchemy.ext.declarative import declarative_base

=> O declarative_base que estou importando serve para o SQL mapear todas as Classes em python, assim todas as classes que herdarem o declarative_base são identificadas como Classes do Sql.

from sqlalchemy.orm import sessionmaker

=> sessionmaker é responsável por estabelecer uma conexão persistente com o banco de dados. O sessionmaker deve ser receber um parâmetro de uma conexão ativa com o banco de dados, que é criada usando o create_engine.

Criando uma instância:

def instância():

USUARIO = 'root' #Padrão do usuário SQL

SENHA = '' #Vazia, padrão SQL

HOST = 'localhost' #Local host

DB = 'python' #Nome do banco de dados que será feita a conexão

PORTA = '3306' #Padrão SQL

connect = f"mysql+pymysql://{USUARIO}:{SENHA}@{HOST}:{PORTA}/{DB}"
=>Formato da conexão, mysql é o banco de dados que vamos usar para essa conexão

engine = create_engine(connect, echo=True)
=> Aqui vamos criar uma conexão com o banco de dados. Echo exibe todas as informações, além das essenciais.

Session = sessionmaker(bind=engine)
=> Cria a conexão persistente com o banco de dados, armazenando qualquer alteração que o desenvolvedor faz na sessão, por exemplo, adicionar uma tabela, excluir, etc.

session = Session()
=> O intuito dessa linha é sempre que o session for chamado, automaticamente ele chamar o método __call__ dentro da classe SessionMaker da sqlalchemy. Esse método armazena o que foi feito e acrescenta na sessão.
=> Isso inclui os elementos que estão no banco de dados, por exemplo, quando fazemos a chamada da função query ele consulta dados do banco de dados e retorna esses dados para o sessão para que talvez possam serem alterados e posteriormente salvos.
=> Isso só é possível pois o objeto Session tem uma conexão ativa com o banco de dados, então ele pode consultar e enviar dados a qualquer momento.

return session, engine
=> Retorna o método Session e Engine

Os primeiros comandos

=> Abaixo vamos retornar os dados da função instância em uma variável para facilitar nos próximos comandos.
sessão, engine = instância()

Base = declarative_base()
=> Declara a Base que é a variável responsável por vincular as Classes em python em uma Classe SQL

Criando uma Classe

=> Aqui vamos criar o objeto em python que vai herdar do Base que criarmos anteriormente, assim essa classe será identificada como uma classe SQL
class Pessoa(Base): # Identifiquei essa Classe, como uma classe SQL
__tablename__ = "Pessoa"
=> Variável padrão para o nome da Tabela

id = Column(Integer, primary_key=True, autoincrement=True)
=> Column é a Classe responsável por criar uma tabela no Banco de dados, além disso acrescentei argumentos que foram o primary_key para definir como objeto primário da tabela, e autoincrement para adicionar valores integros de forma automatica quando criada.

nome = Column(String(50))
=> Criação da Coluna nome, String(50) significa que o objeto será da Classe string e poderá ter no máximo 50 caracteres

Base.metadata.create_all(engine)
=> O comando abaixo já está fora da nossa função, ele será responsável em criar no nosso banco de dados as tabelas que colocamos na classe Pessoa

usuario = Pessoa(nome='Roberto')
usuario2 = Pessoa(nome='Humberto')
=> Nosso primeiro Insert; para inserir informações em nossa tabela é bem simples, precisamos apenas chamar a Classe "Pessoa" e adicionar os dados correspondentes, mas calma ainda não salvamos essas informações!

sessão.add(usuario)
=> O comando acima é bem simples, estamos adicionando os dados do usuário que criamos anteriormente a nossa sessão do SQL.

sessão.add_all([usuario1, usuario2])
=> add all é se caso precisarmos adicionar mais de uma usuário em nossa sessão, só precisamos inserir todos os usuários dentro de uma lista, assim torna nosso código mais otimizado se tivermos mais de um usuário.

sessão.rollback()
=> O rollback é para limpar todos os dados da nossa sessão, se esse comando for executado os dois usuários que adicionamos na sessão seriam excluídos.

sessão.commit()
=> Envia as alterações que foram adicionadas na sessão ao banco de dados, ou seja os dois usuários foram salvos após esse comando.

Consultando, Filtrando e retornando os dados

sessão => Como foi mencionado anteriormente, a sessão é uma conexão ativa com o banco de dados, ela também serve para realizar consultas, e esses dados são posteriormente salvos em nossa sessão.

query => Função responsável em realizar a consulta no banco de dados, recebe uma tabela ou uma classe que esteja associada ao banco de dados SQL

dados = sessão.query(Pessoa.nome)
=> Aqui vamos fazer uma consulta e retornar todos os elementos da Coluna nome dentro da Classe Pessoa.

dados = sessão.query(Pessoa).filter_by(id=1, nome='dvd')
=> Faz um filtro de tabelas com valores especifíco no banco de dados.

dados = sessão.query(Pessoa).filter(Pessoa.idade.is_(None))
dados = sessão.query(Pessoa).filter(Pessoa.idade.isnot())
=> Para verificar a igualdade/desigualdade, especialmente útil para verificar contra None.

dados = sessão.query(Pessoa).filter(Pessoa.idade.between(1,18))
=> Between é uma função que seria como um filtro, consulte apenas as pessoas que tem a idade entre 1 a 18.

dados = sessão.query(Pessoa).filter(Pessoa.nome.like('%dv%')
=> Filtra todos os resultados da Coluna nome que tem as letras dv. O like é sensível a letras maiúsculas e minúsculas, caso queira ignorar isso, use iLIKE. Algo bem importante em mencionar, é que o % é um curinga, ele serve como uma expressão regular, nesse caso se fosse necessário filtrar um nome que tenha o final com as letras dv usariamos %dv, ou só o começo seria dv%.

dados = sessão.query(Pessoa.nome).filter(Pessoa.nome.in_(['Maia'], ['Caio']))
=> Retorna todos os nome que estão dentro da lista.

dados = sessão.query(Pessoa.nome).filter(~Pessoa.nome.in_(['DVD'], ['CAIO']))
=> Retorna todos os nomes que não estão dentro da Pessoa

Condições com SQLalchemy

from sqlalchemy import or_, and_, not_
=> Importando as funções de condições da biblioteca Sqlalchemy

or_ e and_, not_
=> São funções da biblioteca sqlalchemy de condição, sempre usaremos elas com o filter.

dados = sessão.query(Pessoa).filter(or_(Pessoa.id==1,Pessoa.nome=='Caio'))
=> É uma condição clara. retorne a Pessoa se ela tiver o ID 1 ou o nome 'Caio'.

dados = sessão.query(Pessoa).filter(and_(Pessoa.id==1,Pessoa.nome=='Big'))
=> Retorne somente a Pessoa se ela tiver o ID 1 e o nome dela for 'Big'.

dados = sessão.query(Pessoa).filter(not_(Pessoa.nome == 'João))
=> Retorna todos os objetos Pessoas que não possuem o nome João.

Importante: Em todos os casos desse tópico são retornos, esses dados ainda não estão sendo filtrados, existem funções para isso, vamos ver no próximo tópico o retorno adequado dos dados.

Extraindo o retorno dos dados

dados = sessão.query(Pessoa)
=> Criando um consulta em uma tabela especifica, podemos acrescentar uma coluna dessa tabela se necessário.

dados = dados.all()
O all() retorna uma lista dos dados que estão armazenados na Tabela da Classe Pessoa.

for i in dados:
nome = i.nome
=> Os dados do método all() vem em um formato de lista, por isso é necessário iterar sobre eles.

dados[0].nome = 'Novo Nome'
=> Aqui estamos fazendo um Update do valor nome do primeiro item da lista. Como informei, para visualizar os dados e consequentemente trata-los precisamos iterar sobre a lista, assim podendo fazer condições com o python. Nesse caso não sei o nome do primeiro item da lista, apenas estou alterando ele

dados.one()
=> One é só quando queremos receber apenas um objeto de retorno, se caso recebemos mais de um objeto receberemos um erro do python.

dados.one_or_none()
=> Retorna o objeto que foi feito no filtro, esse filtro deve retornar um objeto apenas, caso ele retorne mais de um objeto será retornado o None, assim não precisamos usar o try except.

dados.first()
=> Informamos que só queremos receber os dados do primeiro objeto encontrado, mesmo se ele retornar mais de um objeto. caso ele não encontre nenhum retornará None.

dados.first(Pessoa.nome == 'Big')
=> Esse método pode retornar True/False. Nesse exemplo ele vai selecionar o primeiro objeto que foi retornado no first e verificar se o nome desse elemento é 'Big', se for retornará True.

dados.count()
=> Retorna a quantidade de elementos encontrados de uma Tabela, ou Coluna especifica.

dados.get(0)
=> Essa função é apenas para filtrar no banco de dados a chave primária especificada e retornar o objeto da chave em questão, que nesse caso é o ID. Caso o objeto não seja encontrado, será retornado um valor None como resposta.

Manipulando dados

dados = sessão.query(Pessoa).join(Endereço, Pessoa.id == Endereço.user_id)
=> A função "join" é usada para juntar tabelas do nosso banco de dados. Nesse caso específico, nossa tabela endereço está separada da tabela Pessoa, mas temos uma chave estrangeira em Endereço que conseguimos vincular a Pessoa a este endereço.
=> Assim o banco de dados é unificado. É importante observar que caso o endereço.user_id não exista em alguma linha do tabela Endereço, essa Pessoa não será inclusa na nova tabela, então tome cuidado.

dados = sessão.query(Pessoa).outerjoin(Endereço, Pessoa.id == Endereço.user_id)
=> A função Outerjoin faz quase a mesma coisa, porém os objetos que ela não conseguir vincular de um Tabela com a outra o valor será None, nesse caso se o usuário não tem o valor user_id na tabela Endereço, o valor na nova tabela será None.

Antes dos comandos:

Tabela Pessoa:

| id | nome | |----|-------| | 1 | João | | 2 | Maria | | 3 | Pedro | | 4 | Carla |

Tabela Endereço:

| id | user_id | endereco | |----|---------|--------------| | 1 | 1 | Rua A | | 2 | 1 | Rua B | | 3 | 2 | Rua C | | 4 | 3 | Rua D |

Depois do comandos

Depois do primeiro comando (utilizando `join`):

| id | nome | id_endereco | endereco | |----|-------|-------------|----------| | 1 | João | 1 | Rua A | | 1 | João | 2 | Rua B | | 2 | Maria | 3 | Rua C | | 3 | Pedro | 4 | Rua D |

=> Veja que o comando join unificou as duas tabelas e uma só, porém observe que a usuária "Carla" não existe mais, pois ela não estava na tabela de endereços.

Depois do segundo comando (utilizando `outerjoin`):

| id | nome | id_endereco | endereco | |----|--------|-------------|----------| | 1 | João | 1 | Rua A | | 1 | João | 2 | Rua B | | 2 | Maria | 3 | Rua C | | 3 | Pedro | 4 | Rua D | | 4 | Carla | NULL | NULL |

=> Note que quando usamos o "outerjoin" a usuária "Carla" continua existindo, porém a tabela endereço ficaram com os valores NULL, mas ela não foi excluída.

dados = sessão.query(Pessoa).filter(Pessoa.nome == 'DVD', Pessoa.id=1).delete()
=> Deleta um, ou todos os dados que retornaram.

sessão.delete(dados)
=> Estamos deletando da sessão esse objeto, mas tenha em mente que é somente da sessão, não estamos deletando ele do banco de dados, como expliquei no início a sessão é apenas uma forma de armazenar os dados antes de envia-los ao banco de dados.

Importante: Ressaltando novamente a questão de salvar os dados; É necessário usar .commit() ao final do código para enviar as alterações ao banco de dados, se não qualquer alteração feita ficará apenas na sessão.

Update Dos Dados

pessoa = session.query(Pessoa).filter(Pessoa.nome == "João").first()
if pessoa: pessoa.idade = 30
=> Suponhamos que você queira atualizar a idade do João para 30

session.query(Pessoa).filter(Pessoa.idade < 20).update({Pessoa.idade: 20})
=> Aqui vamos alterar a idade de todas as Pessoas que tem menos de 20 anos para 20 anos

Usando o metodo Update:

stmt = (update(Pessoa).where(Pessoa.nome == "João").values(idade=35))
session.execute(stmt)
=> Aqui estamos usando o método update. Nesse caso como não estamos usando o ORM do python e sim usando funções mais complexas precisamos executar a função execute para inserir instruções na sessão ao banco de dados dessa alteração.

Importante: Novamente é importante lembrar que para salvar as alterações feitas acima precisamos usar o comando commit(). Além disso, é uma boa prática fechar a sessão com session.close() quando terminar.

Comandos SQL puro com PYTHON

Aqui vamos usar a biblioteca Pymysql, já não mais a sqlachemy. A pymysql é a biblioteca é responsável por inserir os comandos em nosso banco de dados.

=> Abaixo vamos iniciar a instalação da biblioteca e importa-la em nosso código
pip install pymysql
import pymysql.cursors

=> Aqui vamos iniciar uma conexão com o banco de dados.
connection = pymysql.connect(
host="localhost",
user="root",
password="",
db="python",
charset='utf8mb4',
cursorclass=pymysql.cursors.DictCursor
)

=> Após iniciar a conexão vamos atribuir essa conexão a função cursor da biblioteca e a função execute será a responsável por executar os comandos.
with connection.cursor() as cursor:
cursor.execute("SELECT * FROM PESSOAS")