O documento apresenta o uso do SQLAlchemy para mapear objetos Python a tabelas de um banco de dados. É criada uma engine para conexão com o banco SQLite e uma classe Contato é mapeada à tabela "agenda". Um programa permite adicionar, listar e sair de contatos de forma interativa na sessão.
3. Acesso a banco de dados
Em Python o acesso a banco de dados pode ser feito
de várias maneiras. As principais são:
A partir de módulos compatíveis com a
especificação DB-API;
Utilizando bibliotecas que fazem mapeamento
objeto-relacional (ex: SQLAlchemy);
Nos nossos exemplos utilizamos o SQLite, pois já vem
integrado com Python.
4. Exemplo SQLite3/DB-API
# -*- coding: latin-1 -*-
from sqlite3 import dbapi2 as sqlite
#Abrindo uma conexão
con = sqlite.connect('agenda.db')
#Obtendo um cursor
cursor = con.cursor()
cursor.execute("INSERT INTO agenda(nome, tel) VALUES('Bruno', '123')")
#Salvando os dados da transação
con.commit()
5. SQLAlchemy
É uma biblioteca que facilita a utilização de bancos de
dados por aplicações Python;
O SQLAlchemy faz o mapeamento objecto-relacional entre
os objetos Python a as tabelas de um banco de dados;
SQLAlchemy é suporta qualquer banco de dados que
possua módulo DB-API;
A instalação do SQLAlchemy pode ser feita de duas
formas:
Fazendo o download; http://www.sqlalchemy.org/
Utilizando o setuptools.
6. Setup Tools
Objetivo: Instalação fácil de pacotes Python;
Link para download:
http://pypi.python.org/pypi/setuptools/
Instalação:
Windows:
Execute o arquivo setuptools-0.6c11.win32-
py2.6.exe;
Linux e MacOS:
sudo sh setuptools-0.6c11-py2.6.egg
8. SQLAlchemy
Conceitos básicos:
Você deve criar classes para mapear as tabelas do
banco de dados;
As operações serão feitas apenas sobre as classes,
não necessitando trabalhar com SQL;
A conexão ao banco de dados é feita através de uma
engine;
As operações com o banco de dados são feitas em
uma sessão;
Uma sessão define uma transação.
9. Exemplo
Vamos mostrar antes um exemplo para depois
apresentar os conceitos em maiores detalhes;
Nosso exemplo será uma boa e velha agenda de
contatos telefônicos;
Faça:
Crie um projeto PyDev chamado Agenda;
Crie um módulo chamado contato.
10. Módulo contato
from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Contato(Base):
__tablename__ = 'agenda'
id = Column(Integer, primary_key=True)
nome = Column(String(30))
tel = Column(String(20))
def __init__(self, nome, tel):
self.nome = nome
self.tel = tel
12. # -*- coding: latin-1 -*-
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from contato import Contato, Base
import os
engine = create_engine('sqlite:///agenda.db')
Session = sessionmaker(bind=engine)
session = Session()
if not os.path.exists('agenda.db'):
Base.metadata.create_all(engine)
while True:
opcao = raw_input('Qual a sua opção (a)dicionar, (l)istar ou (s)air: ')
if opcao == 'a':
nome = raw_input('Digite o nome: ')
tel = raw_input('Digite o telefone: ')
session.add(Contato(nome, tel))
session.commit()
elif opcao == 'l':
for contato in session.query(Contato).order_by(Contato.nome):
print contato.nome, contato.tel
elif opcao == 's':
break
else:
print 'Digite uma opção válida !'
13. # -*- coding: latin-1 -*-
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker Imports
from contato import Contato, Base
import os
engine = create_engine('sqlite:///agenda.db')
Session = sessionmaker(bind=engine)
session = Session()
if not os.path.exists('agenda.db'):
Base.metadata.create_all(engine)
while True:
opcao = raw_input('Qual a sua opção (a)dicionar, (l)istar ou (s)air: ')
if opcao == 'a':
nome = raw_input('Digite o nome: ')
tel = raw_input('Digite o telefone: ')
session.add(Contato(nome, tel))
session.commit()
elif opcao == 'l':
for contato in session.query(Contato).order_by(Contato.nome):
print contato.nome, contato.tel
elif opcao == 's':
break
else:
print 'Digite uma opção válida !'
14. # -*- coding: latin-1 -*-
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker Imports
from contato import Contato, Base
import os
Criação da Engine
engine = create_engine('sqlite:///agenda.db')
Session = sessionmaker(bind=engine)
session = Session()
if not os.path.exists('agenda.db'):
Base.metadata.create_all(engine)
while True:
opcao = raw_input('Qual a sua opção (a)dicionar, (l)istar ou (s)air: ')
if opcao == 'a':
nome = raw_input('Digite o nome: ')
tel = raw_input('Digite o telefone: ')
session.add(Contato(nome, tel))
session.commit()
elif opcao == 'l':
for contato in session.query(Contato).order_by(Contato.nome):
print contato.nome, contato.tel
elif opcao == 's':
break
else:
print 'Digite uma opção válida !'
15. # -*- coding: latin-1 -*-
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker Imports
from contato import Contato, Base
import os
Criação da Engine
engine = create_engine('sqlite:///agenda.db')
Session = sessionmaker(bind=engine)
session = Session() Criação da Sessão
if not os.path.exists('agenda.db'):
Base.metadata.create_all(engine)
while True:
opcao = raw_input('Qual a sua opção (a)dicionar, (l)istar ou (s)air: ')
if opcao == 'a':
nome = raw_input('Digite o nome: ')
tel = raw_input('Digite o telefone: ')
session.add(Contato(nome, tel))
session.commit()
elif opcao == 'l':
for contato in session.query(Contato).order_by(Contato.nome):
print contato.nome, contato.tel
elif opcao == 's':
break
else:
print 'Digite uma opção válida !'
16. # -*- coding: latin-1 -*-
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker Imports
from contato import Contato, Base
import os
Criação da Engine
engine = create_engine('sqlite:///agenda.db')
Session = sessionmaker(bind=engine)
session = Session() Criação da Sessão
if not os.path.exists('agenda.db'):
Base.metadata.create_all(engine) Criação do Banco de Dados
while True:
opcao = raw_input('Qual a sua opção (a)dicionar, (l)istar ou (s)air: ')
if opcao == 'a':
nome = raw_input('Digite o nome: ')
tel = raw_input('Digite o telefone: ')
session.add(Contato(nome, tel))
session.commit()
elif opcao == 'l':
for contato in session.query(Contato).order_by(Contato.nome):
print contato.nome, contato.tel
elif opcao == 's':
break
else:
print 'Digite uma opção válida !'
17. # -*- coding: latin-1 -*-
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker Imports
from contato import Contato, Base
import os
Criação da Engine
engine = create_engine('sqlite:///agenda.db')
Session = sessionmaker(bind=engine)
session = Session() Criação da Sessão
if not os.path.exists('agenda.db'):
Base.metadata.create_all(engine) Criação do Banco de Dados
while True:
opcao = raw_input('Qual a sua opção (a)dicionar, (l)istar ou (s)air: ')
if opcao == 'a':
nome = raw_input('Digite o nome: ')
tel = raw_input('Digite o telefone: ')
session.add(Contato(nome, tel)) Adicionando um objeto ao banco
session.commit()
elif opcao == 'l':
for contato in session.query(Contato).order_by(Contato.nome):
print contato.nome, contato.tel
elif opcao == 's':
break
else:
print 'Digite uma opção válida !'
18. # -*- coding: latin-1 -*-
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker Imports
from contato import Contato, Base
import os
Criação da Engine
engine = create_engine('sqlite:///agenda.db')
Session = sessionmaker(bind=engine)
session = Session() Criação da Sessão
if not os.path.exists('agenda.db'):
Base.metadata.create_all(engine) Criação do Banco de Dados
while True:
opcao = raw_input('Qual a sua opção (a)dicionar, (l)istar ou (s)air: ')
if opcao == 'a':
nome = raw_input('Digite o nome: ')
tel = raw_input('Digite o telefone: ')
session.add(Contato(nome, tel)) Adicionando um objeto ao banco
session.commit()
elif opcao == 'l': Listando todos os contatos
for contato in session.query(Contato).order_by(Contato.nome):
print contato.nome, contato.tel
elif opcao == 's':
break
else:
print 'Digite uma opção válida !'
19. # -*- coding: latin-1 -*-
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker Imports
from contato import Contato, Base
import os
Criação da Engine
engine = create_engine('sqlite:///agenda.db')
Session = sessionmaker(bind=engine)
session = Session() Criação da Sessão
if not os.path.exists('agenda.db'):
Base.metadata.create_all(engine) Criação do Banco de Dados
while True:
opcao = raw_input('Qual a sua opção (a)dicionar, (l)istar ou (s)air: ')
if opcao == 'a':
nome = raw_input('Digite o nome: ')
tel = raw_input('Digite o telefone: ')
session.add(Contato(nome, tel)) Adicionando um objeto ao banco
session.commit()
elif opcao == 'l': Listando todos os contatos
for contato in session.query(Contato).order_by(Contato.nome):
print contato.nome, contato.tel
elif opcao == 's':
Ordenados pelo atributo nome
break
else:
print 'Digite uma opção válida !'
20. SQLAlchemy
Mapeando uma classe em uma tabela:
É necessário criar uma classe para cada tabela que se
queira mapear;
As próprias classes podem ser utilizadas para criar as
tabelas, caso elas não existam;
Forma geral:
from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Contato(Base):
__tablename__ = 'agenda' Atributo obrigatório
id = Column(Integer, primary_key=True)
nome = Column(String)
tel = Column(String)
21. SQLAlchemy
Com exceção do Oracle e do Firebird, todos os outros
bancos de dados têm suporte a colunas com auto
incremento (para as chaves primárias);
Para o Oracle e Firebird é necessário especificar uma
seqüência:
class Contato(Base):
__tablename__ = 'agenda'
id = Column(Integer, Sequence('contato_seq'), primary_key=True)
nome = Column(String(30))
tel = Column(String(20))
22. SQLAlchemy
Nome do SGBD
Obtendo uma engine:
engine = create_engine('sqlite:///agenda.db')
URL para o banco de dados
O nome do SGBD é uma forma do SQLAlchemy localizar o módulo
correto, alguns exemplos de nomes são: sqlite, mysql, postgresql, mssql,
oracle, ...
A URL para o banco de dados é dependente do módulo que se está
utilizando, por isso, deve-se ler a documentação do módulo para saber
como construir a URL;
Exemplos: mssql://user:passwd@mydsn, oracle://
scott:tiger@127.0.0.1:1521/sidname, mysql://scott:tiger@localhost/foo, ...
Você pode, na criação, pedir que a engine mostre todos o código SQL
gerado por ela: create_engine('sqlite:///agenda.db', echo=True)
23. SQLAlchemy
Criando automaticamente as tabelas:
Para isto, após a definição do mapeamento, utiliza-
se o atributo metadata da classe Base:
Base.metadata.create_all(engine)
Obtendo uma sessão:
As sessões estão sempre associadas a uma engine:
Session = sessionmaker(bind=engine)
session = Session()
24. SQLAlchemy
Gravando objetos no banco de dados:
session.add(objeto)
session.commit( )
Você pode adicionar uma lista de objetos de uma vez só:
session.add_all([objeto1, objeto2, objeto3])
Atenção: Lembre-se sempre de após um bloco de atualizações
realizar o commit, pois, caso contrário, os dados não serão
persistidos;
Caso algum erro ocorra, você deve chamar o rollback, para
deixar o estado do banco de dados igual ao do último commit
realizado.
25. SQLAlchemy - Consultas
Pela chave primária:
bruno = session.query(Contato).get(5)
Retornando todos os registros:
contatos = session.query(Contato).all( )
Ordenando os resultados:
contatos =
session.query(Contato).order_by(Contato.nome).all( )
Restringindo os resultados:
Você pode usar o método all, para obter todos os
resultados ou first para obter apenas o primeiro.
26. SQLAlchemy - Filtros
Filtrando por um campo:
pedro = session.query(Contato).filter(Contato.nome == ”Pedro”).first( )
Outras operações com filtros:
Pessoa.idade >= 18
Contato.nome != ”Bruno”
Contato.nome == None
Contato.nome != None
Contato.nome.like(”Maria%”)
Endereco.estado.in_[”PB”, “PE”, “RN”]
~Endereco.estado.in_[”PB”, “PE”, “RN”]
and_(Contato.nome == “Bruno”, Contato.telefone == “333”)
or_(Endereco.estado == ‘PB’, Endereco.cidade == ‘Campina Grande’)
27. SQLAlchemy - Filtros
Filtrando por um campo:
pedro = session.query(Contato).filter(Contato.nome == ”Pedro”).first( )
Outras operações com filtros:
Você pode utilizar qualquer operador lógico:
Pessoa.idade >= 18
>, <, >=, <=, ==, !=
Contato.nome != ”Bruno”
Contato.nome == None
Contato.nome != None
Contato.nome.like(”Maria%”)
Endereco.estado.in_[”PB”, “PE”, “RN”]
~Endereco.estado.in_[”PB”, “PE”, “RN”]
and_(Contato.nome == “Bruno”, Contato.telefone == “333”)
or_(Endereco.estado == ‘PB’, Endereco.cidade == ‘Campina Grande’)
28. SQLAlchemy - Filtros
Filtrando por um campo:
pedro = session.query(Contato).filter(Contato.nome == ”Pedro”).first( )
Outras operações com filtros:
Você pode utilizar qualquer operador lógico:
Pessoa.idade >= 18
>, <, >=, <=, ==, !=
Contato.nome != ”Bruno”
Contato.nome == None
Contato.nome != None
Contato.nome.like(”Maria%”)
Você pode ter 2 ou mais
Endereco.estado.in_[”PB”, “PE”, “RN”] argumentos no and e no or
~Endereco.estado.in_[”PB”, “PE”, “RN”]
and_(Contato.nome == “Bruno”, Contato.telefone == “333”)
or_(Endereco.estado == ‘PB’, Endereco.cidade == ‘Campina Grande’)
29. SQLAlchemy
Para alterar um registro é preciso: obtê-lo na sessão,
modificar seus valores e fazer commit da sessão:
contato = session.query(Contato).get(5)
contato.tel = '8888-8888'
session.commit()
Para remover um registro é preciso: obtê-lo, chamar o
método delete na sessão e fazer commit:
contato = session.query(Contato).get(5)
session.delete(contato)
session.commit()
30. Desafio
Exiba o ID do contato assim que ele for criado:
Foi criado um contato com o id X;
Adicione na agenda a possibilidade de procurar um
contato pelo nome;
Adicione as funcionalidades de atualizar e remover:
Dica: Peça pro usuário digitar o id do contato que
ele quer atualizar/remover, procure o contato por
este id e realize a operação desejada.
31. Relacionamentos
É possível definir relacionamentos entre as classes
mapeadas;
Por exemplo:
Contatos podem ter um ou mais números de
telefone;
Empresas podem ter um ou mais funcionários;
Professores lecionam uma ou mais disciplina que,
por sua vez, possui um ou mais alunos matriculados.
32. from sqlalchemy import *
from sqlalchemy.orm import relationship, backref
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Montadora(Base):
__tablename__ = "montadora"
id = Column(Integer, primary_key=True)
nome = Column(String)
def __init__(self, nome):
self.nome = nome
class Carro(Base):
__tablename__ = "carro"
id = Column(Integer, primary_key=True)
modelo = Column(String)
ano = Column(Integer)
montadora_id = Column(Integer, ForeignKey("montadora.id"))
montadora = relationship(Montadora, backref=backref('carros', order_by=id))
def __init__(self, modelo, ano, montadora):
self.modelo = modelo
self.ano = ano
self.montadora = montadora
33. from sqlalchemy import *
from sqlalchemy.orm import relationship, backref
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Montadora(Base):
__tablename__ = "montadora"
id = Column(Integer, primary_key=True)
nome = Column(String)
def __init__(self, nome):
self.nome = nome
class Carro(Base):
__tablename__ = "carro"
Definição da
id = Column(Integer, primary_key=True) chave estrangeira
modelo = Column(String)
ano = Column(Integer)
montadora_id = Column(Integer, ForeignKey("montadora.id"))
montadora = relationship(Montadora, backref=backref('carros', order_by=id))
def __init__(self, modelo, ano, montadora):
self.modelo = modelo
self.ano = ano
self.montadora = montadora
34. from sqlalchemy import *
from sqlalchemy.orm import relationship, backref
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Montadora(Base):
__tablename__ = "montadora"
id = Column(Integer, primary_key=True)
nome = Column(String)
def __init__(self, nome):
self.nome = nome
class Carro(Base):
__tablename__ = "carro"
Definição da
id = Column(Integer, primary_key=True) chave estrangeira
modelo = Column(String)
ano = Column(Integer)
montadora_id = Column(Integer, ForeignKey("montadora.id"))
montadora = relationship(Montadora, backref=backref('carros', order_by=id))
def __init__(self, modelo, ano, montadora): Definição do relacionamento
self.modelo = modelo
self.ano = ano
self.montadora = montadora
35. Função relationship
A função relationship deve estar no lado MUITOS da
relação;
Forma geral:
relationship(ClasseDestino, backref)
Backref - Definição do relacionamento na classe de
destino:
backref(‘atributo a ser criado’, order_by=campo)
A função backref irá criar um atributo com o nome
especificado na classe de destino.
36. Trabalhando com
relacionamentos
Adição:
ford = Montadora('Ford')
fiesta = Carro('Fiesta', 2010, ford)
session.add(fiesta)
session.commit()
Navegação:
ford = session.query(Montadora).filter(Montadora.nome == 'Ford').first()
for carro in ford.carros:
print carro.modelo
Remoção:
del ford.carros[0]
session.commit()
37. Desafio
Façam com que um Contato possa ter mais do que
um número de telefone;
Permitam que na adição o usuário possa digitar
quantos números quiser. Faça uma pergunta:
Adicionar mais um número (s)im ou (n)ão ?
Na listagem dos contatos, exibir, para cada contato,
todos os seus números.
38. CherryPy
Pequeno framework HTTP escrito em Python;
Com ele é possível desenvolver aplicações web
completas;
Porém seu foco é no desenvolvimento rápido de
pequenos aplicativos web;
Site:
http://www.cherrypy.org
39. CherryPy
Instalação:
Faça do download no site da última versão estável
(3.1.2);
No caso do Windows é só executar o instalador;
Nos SOs baseados em Unix você deve
descompactar o arquivo (.tar.gz) e executar:
sudo python setup.py install
40. Hello World
import cherrypy
class HelloWorld:
@cherrypy.expose
def index(self):
return "Hello world!"
cherrypy.quickstart(HelloWorld())
41. Hello World
Abra o endereço http://localhost:8080
no seu navegador
import cherrypy
class HelloWorld:
@cherrypy.expose
def index(self):
return "Hello world!"
cherrypy.quickstart(HelloWorld())
42. Hello World
Abra o endereço http://localhost:8080
no seu navegador
import cherrypy
class HelloWorld: Decorador: diz que o método
@cherrypy.expose pode exposto pelo CherryPy
def index(self):
return "Hello world!"
cherrypy.quickstart(HelloWorld())
43. CherryPy
Conceitos básicos:
Sites são definidos por classes;
O método index do objeto principal publicado será
mapeado para o endereço raiz (/);
Os demais métodos serão mapeados como urls:
/hello
45. Arquivo de configuração
Você pode especificar os parâmetros básicos da sua
aplicação em um arquivo de configuração simples;
Este arquivo pode estar localizado em qualquer lugar
do seu computador (de preferência no mesmo diretório
do seu projeto) e deverá ser informado na função
quickstart;
Formato do arquivo (server.cfg):
[global]
server.socket_port = 8000
server.thread_pool = 10
tools.sessions.on = True
tools.staticdir.root = "/home/site"
[/static]
tools.staticdir.on = True
tools.staticdir.dir = "static"
46. Arquivo de configuração
Você pode especificar os parâmetros básicos da sua
aplicação em um arquivo de configuração simples;
Este arquivo pode estar localizado em qualquer lugar
do seu computador (de preferência no mesmo diretório
do seu projeto) e deverá ser informado na função
quickstart;
Este arquivo pode ter
qualquer nome
Formato do arquivo (server.cfg):
[global]
server.socket_port = 8000
server.thread_pool = 10
tools.sessions.on = True
tools.staticdir.root = "/home/site"
[/static]
tools.staticdir.on = True
tools.staticdir.dir = "static"
47. Exemplo
Crie um projeto (TesteWeb);
Na pasta src crie um arquivo chamado server.cfg;
Crie uma pasta chamada static;
Crie um módulo chamado testeweb;
Na pasta static crie um arquivo chamado teste.html;
52. Executando
Execute o módulo testeweb;
Em um navegador abra os endereços:
http://localhost:8080
http://localhost:8080/static/teste.html
53. Trabalhando com formuários
Formulários são uma das formas em HTML de passar
parâmetros dos clientes para o servidor;
Vamos mudar o arquivo teste.html e o módulo
testeweb para exemplificar o uso de formulários.
56. Executando
Execute o módulo testeweb;
Em um navegador abra os endereços:
http://localhost:8080/static/teste.html
Preencha e submeta o formulário.
57. Tópico interessante
Templates
Você pode usá-los para construir páginas dinâmicas;
Como funcionam:
Você cria um texto com variáveis: $nome_variavel;
Depois você pode substituir estas variáveis por
valores.
58. Exemplo
from string import Template
t = Template('Login: $login - Senha: $senha')
print t.substitute(login='system', senha='123')
59. Desafio
Crie um aplicativo web com o CherryPy que possua
duas funções:
index: Exibe todos os contatos cadastrados;
cadastra: Adiciona mais um contato ao banco;
Utilize os exemplos do SQLAlchemy, CherryPy e do
álbum de fotografias para se inspirar;
Fazer este desafio é muito mais simples do que pode
parecer !