O documento discute a implementação de acesso a múltiplos bancos de dados no Django. A solução proposta permite indexar as configurações dos bancos de dados adicionais no arquivo settings.py e cria uma classe Manager personalizada que direciona as consultas aos bancos corretos. Isso corrige problemas na abordagem anterior que acessava os bancos diretamente nas views.
1. Implementação de Acesso a múltiplos Bancos de
dados no Django.
Rômulo Jales - rjales@fitec.org.br
2. Agenda
● O que é Django?
● Um rápido Tutorial
● A necessidade
● A solução
3. O que é Django?
● Um framework web de alto nível
● Escrito em Python
● Estimula o desenvolvimento rápido e limpo
● Implementa o conceito DRY
“Um framework para perfeccionistas com
deadlines”
5. Um rápido tutorial
● Crie um projeto:
● django-admin startproject NOME_PRJ
● São criados 4 arquivos:
– __init__.py
– settings.py
– manage.py
– urls.py
6. Um rápido tutorial
● settings.py
● Armazena as configurações do serviço
● O banco de dados é configurado aqui!
● urls.py
● Realiza o roteamento das urls para os métodos e
recursos
● manager.py
● Gerenciador local do django.
8. Um rápido tutorial
● models.py → acesso aos dados
● views.py → Tratamento das requisições da
aplicação.
9. Um rápido tutorial
● Código para models.py
from django.db import models
class Artigo(models.Model):
titulo = models.CharField(max_length=100)
conteudo = models.TextField()
publicacao = models.DateTimeField()
10. Um rápido tutorial
● Crie um arquivo chamado admin.py
from django.contrib import admin
from models import Artigo
admin.site.register(Artigo)
● Edite a urls.py
(r'^$', 'django.views.generic.date_based.archive_index',
{'queryset': Artigo.objects.all(), 'date_field': 'publicacao'}),
(r'^admin/(.*)', admin.site.root),
14. Quem usa o django?
● globo.com
● washingtonpost.com
● E outros 3575 cadastrados no
http://www.djangosites.org/!
15. A necessidade
● Acessar vários banco de dados usando a
versão em produção 1.1 do cliente.
16. A necessidade
● Solução de acesso atual (na view.py)
mssql = _mssql.connect('IP_DO_SERVER','linux','SENHA')
query = "SELECT OP FROM CONSULTA_OP WHERE
NUMEROSERIE = '"+serial+"'"
mssql.query(query)
17. A necessidade
● Solução quebra a arquitetura!!!!!!
● Não é DRY!
● Não é simples!
● Se o IP do banco mudar?
● Se o backend do banco mudar?
18. A solução
● Premissas e restrições:
● Não pode mudar o que já funciona!
● Usar o conceito de DRY
● Funcionar com todos os backends nativos do
Django (mysql, postgres, sqlite3) e o **SQL
SERVER**
20. A solução - análise
● Models
● Tem um atributo _default_manager (Manager)
● Manager
● Tem um método central get_query_set (QuerySet).
Método que acessa o backend
● QuerySet
● Tem um atributo query do tipo Query, que é
instancia de DataBaseWrapper, que é uma classe
abstrata definida pela parametrização contida em
settings.py
21. A solução
● Permitir indexação dos parâmetros
DATABASE_*
● Modificar a classe QuerySet!!
22. A solução
from django.db.models import sql
from django.db.models.sql.where import WhereNode
from utils import getConnection
class MultiBdQuery(sql.Query):
def __init__(self, model, banco):
self.banco = banco
self.connection = getConnection(self.banco)
super(MultiBdQuery, self).__init__(model, self.connection, WhereNode)
def __setstate__(self, obj_dict):
obj_dict['select_fields'] = [
name is not None and obj_dict['model']._meta.get_field(name) or None
for name in obj_dict['select_fields']
]
self.__dict__.update(obj_dict)
self.connection = getConnection(self.banco)
23. A solução
from django.conf import settings
from django.db import load_backend
def getConnection(banco):
engine = settings.SECONDARY_DB[banco]['DATABASE_ENGINE']
if engine == "sql_server.pyodbc":
backend = __import__(engine+'.base', {}, {}, ['base'])
else:
backend = load_backend(engine)
return backend.DatabaseWrapper(settings.SECONDARY_DB[banco])
24. A solução
from django.db.models import sql
from django.db.models.manager import Manager
from django.db.models.query import QuerySet
from query import MultiBdQuery
class MultiBdManager(Manager):
use_for_related_fields = True
def __init__(self, banco, *args, **kwargs):
super(MultiBdManager, self).__init__(*args, **kwargs)
self.banco = banco
def get_query_set(self):
#Obtem um novo query a partir das configuracoes de banco
query = MultiBdQuery(self.model, self.banco)
return QuerySet(self.model, query)
25. A solução – visão do usuário
● Adicionar no settings.py um dicionário
chamado
SECONDARY_DB contendo as configurações
dos outros bancos
● Novos modelos
● Modificar o Manager padrão, passando o nome do
banco de qual a classe está associada.
26. Correção da gambiarra - antes
def get_op_from_scf(serial):
import _mssql
mssql = _mssql.connect('IP_DO_SERVER','linux','SENHA')
query = "SELECT OP FROM CONSULTA_OP WHERE NUMEROSERIE = '"+serial+"'"
mssql.query(query)
ret = mssql.fetch_array()
If ret[0][1] == 0:
raise Exception("Nao existe nenhuma OP para o numero de serie informado: " + str(serial))
op = ret[0][2][0][0]
return op.strip()
27. Correção da gambiarra - depois
class OPLEGADO(Model):
class Meta:
db_table = "CONSULTA_OP"
managed = False
_default_manager = MultiBdManager("scf")
op = CharField(max_length=13,primary_key=True)
numeroserie = CharField(max_length=26)
def get_op_from_scf(serial):
op = OPLEGADO.objects.get(numeroserie=serial)
If op:
return op
else:
raise Exception(“"Nao existe nenhuma OP para o numero de serie
informado: " + str(serial))
28. Limitações
● Não sincroniza todos os bancos
simultaneamente!
● Inicialização mais demorada
● Funciona apenas para versões >= 1.1 do
Django.