56

Postgresql + Python = Power!

Embed Size (px)

Citation preview

Page 1: Postgresql + Python = Power!
Page 2: Postgresql + Python = Power!

2/56

Sobre Python

● Linguagem de programação de altíssimo nível;

● Interpretada e compilada (bytecodes: .pyc ou .pyo);

● Totalmente orientada a objetos;

● Suporta herança múltipla;

● Tipagem dinâmica e forte;

● Objetiva;

● Fácil de aprender;

● Muito Produtiva (faça muito escrevendo pouco);

Page 3: Postgresql + Python = Power!

3/56

Sobre Python

● Comunidade forte e solícita;

● O nome da linguagem foi inspirado no grupo humorístico britânico Monty Python's Flying Circus;

● Criada pelo holandês Guido van Rossum em 1991 no Instituto de Pesquisa Nacional para Matemática e Ciência da Computação de Amsterdam.

Page 4: Postgresql + Python = Power!

4/56

Ambientes Python

● Web: Django, Web2Py, Flask, Plone, Zope, ...;

● GUI: PyGTK, PyQT...;

● Embarcados: MicroPython, Raspberry Pi, Arduino...;

● Scripts;

● Etc...

Page 5: Postgresql + Python = Power!

5/56

Python é Multiplataforma

● Linux;

● BSDs;

● *nix;

● MacOS;

● Windows

Page 6: Postgresql + Python = Power!

6/56

Filosofia: The Zen of Python :)

> import this

The Zen of Python, by Tim Peters

Beautiful is better than ugly.Explicit is better than implicit.Simple is better than complex.Complex is better than complicated.Flat is better than nested.Sparse is better than dense.Readability counts.Special cases aren't special enough to break the rules.Although practicality beats purity.Errors should never pass silently.Unless explicitly silenced.In the face of ambiguity, refuse the temptation to guess.There should be one-- and preferably only one --obvious way to do it.Although that way may not be obvious at first unless you're Dutch.Now is better than never.Although never is often better than *right* now.If the implementation is hard to explain, it's a bad idea.If the implementation is easy to explain, it may be a good idea.Namespaces are one honking great idea -- let's do more of those!

Page 7: Postgresql + Python = Power!

7/56

Quem Utiliza Python?

Page 8: Postgresql + Python = Power!

8/56

Python: Strings, Comentários e Doc Strings

● Uma string pode ser delimitada por apóstrofos ('string') ou aspas ("string");

● '''string''' ou """string""": Um tipo de comentário que gera documentação ou também pode ser utilizado como string ou comentário de múltiplas linhas;

● # : O caractere sustenido é utilizado para fazer comentários de uma única linha.

Page 9: Postgresql + Python = Power!

9/56

Python: Strings, Comentários e Doc Strings

> frase = '''Uma frase qualquerque usa maisde uma linha'''

> print(frase)

Uma frase qualquerque usa maisde uma linha

> # Uma simples soma> print(2 + 5)

7

Page 10: Postgresql + Python = Power!

10/56

Python: Orientação a Objeto

> class Pessoa(object): ''' Classe mãe Pessoa que é base para gerar outros tipos de pessoa por herança. '''

# Atributos

nome = '' # string idade = 0 # int telefones = [] # list

# Métodos

def meu_nome(self): print(self.nome)

def minha_idade(self): print(self.idade)

def meus_telefones(self): for i in self.telefones: print(i)

Diferente de outras linguagens, blocos não são delimitados por chaves, mas sim por endentação, cuja recomendação segundo a PEP8* [1] é de 4 (quatro) espaços

← [1] https://www.python.org/dev/peps/pep-0008/

* Python Enhancement Proposal, a PEP 8 é um Guia de Estilo de Codificação Python para fins de padronização e boas práticas.

Page 11: Postgresql + Python = Power!

11/56

Python: Orientação a Objeto

> # Criação de classe filha> class Funcionario(Pessoa): ''' Classe para funcionários herdada da classe Pessoa '''

matricula = ''

> # Instanciando a classe Funcionario> f1 = Funcionario()

> # Dando valores a atributos > f1.nome = 'Chiquinho da Silva'> f1.matricula = '2014xyz113'> f1.telefones = ['11-91111-1111', '11-5111-1111', '11-3111-1111']

Page 12: Postgresql + Python = Power!

12/56

Python: Orientação a Objeto

> # Executando um método> f1.meu_nome()

Chiquinho da Silva

> # Exibindo em tela o valor de um atributo> print(f1.matricula)

2014xyz113

> # Executando o método para exibir telefones> f1.meus_telefones()11-91111-111111-5111-111111-3111-1111

Page 13: Postgresql + Python = Power!

13/56

Python: Orientação a Objeto

> # A função help exibe uma ajuda sobre um objeto> help(f1)

Help on Funcionario in module __main__ object:

class Funcionario(Pessoa) | Classe para funcionários herdada | da classe Pessoa | | Method resolution order: | Funcionario | Pessoa | __builtin__.object | | Data and other attributes defined here: | | matricula = '' | | ---------------------------------------------------------------------- Continua →

Page 14: Postgresql + Python = Power!

14/56

Python: Orientação a Objeto

| Methods inherited from Pessoa: | | meu_nome(self) | | meus_telefones(self) | | minha_idade(self) | | ---------------------------------------------------------------------- | Data descriptors inherited from Pessoa: | | __dict__ | dictionary for instance variables (if defined) | | __weakref__ | list of weak references to the object (if defined) | | ---------------------------------------------------------------------- Continua →

Page 15: Postgresql + Python = Power!

15/56

Python: Orientação a Objeto

| Data and other attributes inherited from Pessoa: | | idade = 0 | | nome = '' | | telefones = []

Page 16: Postgresql + Python = Power!

16/56

Shell interativo

● python (vem por padrão na maioria das distros Linux);

● ipython;

● DreamPie;

● BPython;

● web [1].

[1] https://www.python.org/shell/

Page 17: Postgresql + Python = Power!

17/56

Python + PostgreSQL = Produtividade e Poder!

Page 18: Postgresql + Python = Power!

18/56

Equivalências...

PostgreSQL Python

Licença PostgreSQL (derivada da BSD)

PSF License (derivada da BSD)

Site global

Site nacional

Lista de

discussão (Br)

Evento

Internacional

PgCon

https://www.pgcon.org/

PyCon

http://www.pycon.org/

Evento

Nacional

PgBr

http://pgbr.postgresql.org.br

Python Brasil ou PyBr

http://www.pythonbrasil.org.br/

Evento

RegionalPgDay GruPy (UF) / Python Day

PyPgDay(Python + PostgreSQL)

Último realizado: (https://us.pycon.org/2013/events/pgday/)

Page 19: Postgresql + Python = Power!

19/56

Tipos de Dados (PostgreSQL → Python)

PostgreSQL Python

integer, smallint int

bigint longreal, double floatnumeric Decimalboolean booltext, varchar, char strarray listtipos personalizados dictnull None

Page 20: Postgresql + Python = Power!

20/56

Laboratório de Teste

Criação de uma base de exemplo:

> CREATE DATABASE db_pypg;

Conectar à nova base:

> \c db_pypg

Criação de tabela de teste:

> CREATE TABLE tb_foo( id serial PRIMARY KEY, campo text);

Popular a tabela:

> INSERT INTO tb_foo (campo) VALUES ('foo'), ('bar'), ('baz'), ('spam'), ('eggs');

Page 21: Postgresql + Python = Power!

21/56

Driver PostgreSQL para Python: psycopg2

Site oficial: http://initd.org/psycopg/

___ conecta_pg.py _________________________________________________________

#_*_ encoding: utf8 _*_

# importação do móduloimport psycopg2

# Definição da string de conexão conn_string = """ host='localhost' dbname='db_pypg' user='postgres' password='secret' port='5432' """

________________________________________________________________________Continua →

Page 22: Postgresql + Python = Power!

22/56

Driver PostgreSQL para Python: psycopg2

________________________________________________________________________

# ========= Tratamento de Erros ===================================try: # Faz uma conexão, se não puder ser feita haverá uma exceção conn = psycopg2.connect(conn_string) # conn.cursor retornará um objeto do tipo cursor # que é utilizado para fazer consultas cursor = conn.cursor()

print('Conectado!\n')

except psycopg2.Error as e: print('\nFalha de conexão!\n%s' % (e))

#==================================================================________________________________________________________________________

Continua →

Page 23: Postgresql + Python = Power!

23/56

Driver PostgreSQL para Python: psycopg2

________________________________________________________________________

# Variável de consultasql = "SELECT id, campo FROM tb_foo WHERE campo != 'foo';"

# Execução de uma consultacursor.execute(sql)

# Tuplas buscadas dentro de uma listarecords = cursor.fetchall()

# Loop para buscar os valores na lista recordsfor _id, _campo in records: print('%s -> %s' % (_id, _campo))

# Fechando a conexãoconn.close()

#=================================================================________________________________________________________________________

Page 24: Postgresql + Python = Power!

24/56

Driver PostgreSQL para Python: psycopg2

No shell do sistema operacional executa o arquivo:

$ python conecta_pg.py

2 -> bar3 -> baz4 -> spam5 -> eggs

Page 25: Postgresql + Python = Power!

25/56

SQLAlchemy

SQLAlchemy é um dos mais conhecidos ORMs (Object Relational Mapper - Mapeador Objeto-Relacional) para Python.

Site oficial do projeto: http://www.sqlalchemy.org

___ sqlalchemy_pg.py _________________________________________________________

#_*_ encoding: utf-8 _*_

from sqlalchemy import *

# URL => driver://username:password@host:port/databaseengine = create_engine('postgresql://postgres@localhost:5432/db_pypg')

# Torna acessíveis os metadadosmetadata = MetaData(engine)

________________________________________________________________________Continua →

Page 26: Postgresql + Python = Power!

26/56

SQLAlchemy

____________________________________________________________________________

# Exibe na tela o que é feito no bancometadata.bind.echo = True

# Definição de estrutura de tabelatabela_carro = Table('tb_carro', metadata, Column('id', Integer, primary_key = True), Column('nome', Text), Column('ano', SmallInteger))

# Criação da tabelatabela_carro.create()

# Criação de um objeto para inserir dadosins = tabela_carro.insert()

________________________________________________________________________Continua →

Page 27: Postgresql + Python = Power!

27/56

SQLAlchemy

____________________________________________________________________________

# Executa o INSERT conforme o dicionário passado como parâmetroins.execute( {'nome': 'Fiat 147', 'ano': 1980}, {'nome': 'VW Kombi', 'ano': 1977}, {'nome': 'Ford Corcel', 'ano': 1985})

# Criação de um objeto para consultasel = tabela_carro.select()

# Consulta feita e resultado armazenadores = sel.execute()

________________________________________________________________________Continua →

Page 28: Postgresql + Python = Power!

28/56

SQLAlchemy

____________________________________________________________________________

# Loop para buscar os resultadosfor i in res.fetchall(): print('Carro: %s - Ano: %s' % (i['nome'], i['ano']))

# Fecha a conexão com o banco engine.dispose()________________________________________________________________________

No shell do sistema operacional executar:

$ python sqlalchemy_pg.py

Carro: Fiat 147 - Ano: 1980Carro: VW Kombi - Ano: 1977Carro: Ford Corcel - Ano: 1985

Page 29: Postgresql + Python = Power!

29/56

PL/Python

● Pode utilizar todas as bibliotecas Python;

● Faz tudo o que Python pode fazer;

● Python é bem mais amigável do que PL/pgSQL;

● É uma linguagem procedural que possibilita escrever funções em Python no PostgreSQL.

Site oficial:

http://www.postgresql.org/docs/current/static/plpython.html

Page 30: Postgresql + Python = Power!

30/56

PL/Python: PL/PythonU

O “U” da Questão...

“U” de “untrusted” (não confiável), ou seja, uma linguagem não confiável…

O que significa que não é oferecida qualquer forma de restrição para usuários fazer.

Permite executar ações fora do banco de dados.

No entanto, somente superusers podem criar funções em linguagens como plpythonu.

Page 31: Postgresql + Python = Power!

31/56

PL/Python: Versões de Python

Suporta ambas as variantes de versões de Python (Python 2 e Python 3);

Python 2 → PLPYTHON2U;

Python 3 → PLPYTHON3U;

Pouca diferença entre ambas as versões.

Page 32: Postgresql + Python = Power!

32/56

PL/Python: Preparação

● A instalação de PL/Python pode ser feita via pacote, de acordo com sua distro ou sistema operacional ou via código-fonte;

● Pode-se optar por instalar entre as versões 2 e 3 de Python ou mesmo ambas;

● Evite instalar versões diferentes de PL/Python em uma base, pois não poderão ser usadas na mesma sessão.

No shell acessar a base via psql:

$ psql db_pypg

Page 33: Postgresql + Python = Power!

33/56

PL/Python: Preparação

Verificando se há linguagens procedurais Python disponíveis:

> SELECT tmplname AS nome, tmpllibrary AS biblioteca FROM pg_pltemplate WHERE tmplname ~ 'python';

nome | biblioteca ------------+------------------- plpythonu | $libdir/plpython2 plpython2u | $libdir/plpython2 plpython3u | $libdir/plpython3

Foram constatadas 3 (três) ocorrências, sendo que plpythonu e plpython2u são para Python 2 e plpython3u para Python 3.

Page 34: Postgresql + Python = Power!

34/56

PL/Python: Preparação

Quais são as linguagens procedurais instaladas na base atual:

> SELECT lanname FROM pg_language;

lanname ---------- internal c sql Plpgsql

Para os testes utilizaremos a versão 2 de Python:

> CREATE LANGUAGE plpython2u;

Page 35: Postgresql + Python = Power!

35/56

PL/Python: Preparação

Simples teste:

> DO LANGUAGE plpython2u$$import sysplpy.info('\n\nVersão -> %s\n\n' % sys.version)$$;

INFO:

Versão -> 2.7.9 (default, Mar 1 2015, 13:01:26) [GCC 4.9.2]

Page 36: Postgresql + Python = Power!

36/56

PL/Python vs PL/pgSQL: Blocos Anônimos

PL/pgSQL:

> DOLANGUAGE plpgsql$marcador$DECLARE var1 INT2 := 21; var2 INT2 := 3;BEGIN RAISE NOTICE 'O número é %', (var1 / var2);END;$marcador$;

Page 37: Postgresql + Python = Power!

37/56

PL/Python vs PL/pgSQL: Blocos Anônimos

PL/Python:

> DOLANGUAGE plpython2u$marcador$var1 = 21var2 = 3plpy.notice('O número é %i' % (var1 / var2))$marcador$;

Page 38: Postgresql + Python = Power!

38/56

PL/Python vs PL/pgSQL: Tratamento de Exceção

PL/pgSQL:

> DO$marcador$DECLAREvar1 INT2 := 21;var2 INT2 := 0;BEGIN RAISE NOTICE 'O número é %', (var1/var2); EXCEPTION WHEN division_by_zero THEN RAISE EXCEPTION 'Não dividirás por zero!!!';END;$marcador$ LANGUAGE plpgsql;

Page 39: Postgresql + Python = Power!

39/56

PL/Python vs PL/pgSQL: Tratamento de Exceção

PL/Python:

> DO$marcador$var1 = 21var2 = 0try: plpy.notice('O número é %i' % (var1/var2))except ZeroDivisionError: plpy.info('Não dividirás por zero!!!')$marcador$ LANGUAGE plpython2u;

Page 40: Postgresql + Python = Power!

40/56

PL/Python vs PL/pgSQL: Criação de Função

PL/pgSQL:

> CREATE OR REPLACE FUNCTION fc_hello_world()RETURNS TEXT AS$longliverockinroll$BEGIN RETURN 'Hello, World!';END; $longliverockinroll$ LANGUAGE plpgsql;

Page 41: Postgresql + Python = Power!

41/56

PL/Python vs PL/pgSQL: Criação de Função

PL/Python:

> CREATE OR REPLACE FUNCTION fc_py_hello_world()RETURNS TEXT AS$longliverockinroll$return 'Hello, World!'$longliverockinroll$ LANGUAGE plpython2u;

Page 42: Postgresql + Python = Power!

42/56

PL/Python vs PL/pgSQL: Função com Parâmetro

PL/pgSQL:

> CREATE OR REPLACE FUNCTION fc_teste(preco NUMERIC, qtd SMALLINT)RETURNS NUMERIC(7, 2) AS $heavymetalneverdie$DECLARE total NUMERIC (7, 2) := (preco * qtd);BEGIN IF total <= 4999.99 THEN RAISE NOTICE 'Sem taxação'; ELSIF total <= 9999.99 THEN RAISE NOTICE 'Taxação em 30%%'; total := total * 1.3; ELSE RAISE NOTICE 'Taxação em 80%%'; total := total * 1.8; END IF; RETURN round(total, 2);END; $heavymetalneverdie$ LANGUAGE plpgsql;

Page 43: Postgresql + Python = Power!

43/56

PL/Python vs PL/pgSQL: Função com Parâmetro

PL/Python:

> CREATE OR REPLACE FUNCTION fc_py_teste(preco NUMERIC, qtd SMALLINT)RETURNS NUMERIC(7, 2) AS $heavymetalneverdie$import decimaltotal = decimal.Decimal(preco * qtd)if total <= 4999.99: plpy.notice('Sem taxação')elif total <= 9999.99: plpy.notice('Taxação em 30%') total *= decimal.Decimal(1.3)else: plpy.notice('Taxação de 80%') total *= decimal.Decimal(1.8)return round(total, 2)$heavymetalneverdie$ LANGUAGE plpython2u;

Page 44: Postgresql + Python = Power!

44/56

PL/Python: Triggers

Para os testes a seguir consideremos a tabela de produtos (tb_produto) e para os testes temos 2 (duas) regras de negócio:

1) Não podem ser aceitos valores negativos para o campo de preço (preco);

2) Para produtos não ativos (campo “ativo”) não serão aceitas modificações a não ser que também seja mudado seu valor para ativo (true) novamente.

O item 1 poderia ser feito facilmente por uma constraint CHECK, mas ambos serão via trigger.

Page 45: Postgresql + Python = Power!

45/56

PL/Python: Triggers

Criação de tabela de teste:

> CREATE TABLE tb_produto( id serial PRIMARY KEY, nome varchar(50), descricao TEXT, ativo boolean DEFAULT true, preco numeric(7, 2));

Comentário de coluna (apenas para fins informativos):

> COMMENT ON COLUMN tb_produto.ativo IS 'Produto ativo ou não';

Page 46: Postgresql + Python = Power!

46/56

PL/Python: Triggers

Criação da função para o gatilho:

> CREATE OR REPLACE FUNCTION fc_tg_py_ckvalues() RETURNS TRIGGER AS $sabbracadabra$

if TD['event'] == 'INSERT':

from decimal import Decimal

if Decimal(TD['new']['preco']) < 0: plpy.error('Não é permitido preço com valor negativo!')

if TD['event'] == 'UPDATE':

if not TD['old']['ativo'] and not TD['new']['ativo']: plpy.error('Produto não ativo!')

$sabbracadabra$ LANGUAGE plpython2u;

Page 47: Postgresql + Python = Power!

47/56

PL/Python: Triggers

Criação do trigger (gatilho):

> CREATE TRIGGER tg_ckvalues BEFORE INSERT OR UPDATE ON tb_produtoFOR EACH ROW EXECUTE PROCEDUREfc_tg_py_ckvalues();

Populando a tabela e utilizando RETURNING:

> INSERT INTO tb_produto (nome, ativo, preco) VALUES('foo', 't', 10.00),('bar', 't', 5.40),('baz', 'f', 3.00)RETURNING id, nome;

id | nome ----+------ 1 | foo 2 | bar 3 | baz

Page 48: Postgresql + Python = Power!

48/56

PL/Python: Triggers

Provocando um erro com valor inválido (preço negativo):

> INSERT INTO tb_produto (nome, ativo, preco) VALUES ('spam', 't', -30);

ERROR: plpy.Error: Não é permitido preço com valor negativo!

. . .

Provocando erro com valor inválido (produto não ativo):

> UPDATE tb_produto SET preco = preco * 1.2 WHERE nome = 'baz';

ERROR: plpy.Error: Produto não ativo!

. . .

Page 49: Postgresql + Python = Power!

49/56

PL/Python: Triggers

Teste para um novo INSERT, mas com valor positivo para preco:

> INSERT INTO tb_produto (nome, ativo, preco) VALUES ('spam', TRUE, 30);

Teste para um novo UPDATE, mas reativando o produto que estava desativado:

> UPDATE tb_produto SET (preco, ativo) = (preco * 1.2, TRUE) WHERE nome = 'baz';

Page 50: Postgresql + Python = Power!

50/56

Variáveis de Triggers: PL/pgSQL → PL/Python

PL/pgSQL PL/Python Tipo Descrição

TG_OP TD['event']

string

Qual tipo de evento disparou o trigger.Possíveis valores: INSERT, UPDATE, DELETE, ou TRUNCATE.

TG_WHEN TD['when']Quando o gatilho (trigger) foi disparado.Possíveis valores: BEFORE, AFTER, ou INSTEAD OF.

TG_LEVEL TD['level'] Qual o nível, por linha ou por comando.Possíveis valores: ROW ou STATEMENT.

NEW TD['new']

record

Variável que contém a nova linha para INSERT ou UPDATE em nível de linha (ROW level). É nula para DELETE ou nível STATEMENT.

OLD TD['old']Para nível em linha (ROW level), contém a linha antiga para UPDATE ou DELETE. É nula para o vível STATEMENT ou para INSERT.

TG_NAME TD['name'] name Nome do gatilho.

Continua →

Page 51: Postgresql + Python = Power!

51/56

Variáveis de Triggers: PL/pgSQL → PL/Python

PL/pgSQL PL/Python Tipo Descrição

TG_TABLE_NAME /TG_RELNAME (depreciado)

TD['table_name']name

Nome da tabela que disparou o gatilho.

TG_TABLE_SCHEMATD['table_schem

a']Nome do esquema (schema)

da tabela que disparou o gatilho.

TG_RELID TD['relid'] oidOID da tabela que disparou o

gatilho.

TG_ARGV[] TD['args']arrayDe

string

Se o comando CREATE TRIGGER inclui argumentos para a função eles estão disponíveis de TD['args'][0] a TD['args'][n - 1].

Índices de array inválidos (menor do que zero ou maior do que TG_NARGS) resultam em um valor nulo.

Continua →

Page 52: Postgresql + Python = Power!

52/56

Variáveis de Triggers: PL/pgSQL → PL/Python

PL/pgSQL PL/Python Tipo Descrição

TG_NARGS len(TD['args']) inteiro A quantidade de argumentos passados à função do gatilho.

Continua →

Se TD['when'] for BEFORE ou INSTEAD OF e TD['level'] for ROW, você pode retornar None ou "OK" da função Python para indicar que a linha não foi modificada, "SKIP" para abortar o evento de se TD['event'] for INSERT ou UPDATE você pode retornar "MODIFY" para indicar que você modificou nova linha.

Caso contrário o valor de retorno será ignorado.

Page 53: Postgresql + Python = Power!

53/56

Conclusão

Com Python, que é uma linguagem poderosa, fácil de aprender, fácil de desenvolver e dar manutenção faz com que se tenha uma produtividade maior.

Tal produtividade ficou bem clara nas comparações com PL/pgSQL, de uma forma mais clara e escrevendo menos foram feitas as funções procedurais PL/Python.

Seja utilizando Python como linguagem procedural ou qualquer outra forma, a união com PostgreSQL é poderosa e dá muito mais fluidez e opções para seu projeto.

Page 54: Postgresql + Python = Power!

54/56

O Elefante precisa de você!

Contribua! :)

http://www.postgresql.org/about/donate/

Doe!

Page 55: Postgresql + Python = Power!

55/56

Save our planet!Save our planet!

Page 56: Postgresql + Python = Power!

56/56

Até a próxima!!! :)

Juliano Atanazio

[email protected]

http://www.slideshare.net/spjuliano

https://speakerdeck.com/julianometalsp

https://juliano777.wordpress.com