Script Python acessando classes do Django

Há uns dias atrás eu precisei criar um relatório com dados obtidos de um BD MySQL usado por um projeto em Django. Esse relatório deveria ser enviado no mesmo horário todos os dias. Sendo assim, criei um script em Python para executar essa tarefa e adicionei uma chamada à ele no Cron (agendador de tarefas do Linux).
A primeira idéia que eu tive foi usar código SQL para obter os dados. Mas aí veio o desânimo: Eu teria que digitar muito. Pensei que se usasse o ORM do Django, em poucas linhas eu conseguiria os dados que preciso. Foi então que decidi usar as classes do Django, montar a estrutura do relatório a partir de um template HTML e enviar por e-mail.
Utilizando as classes do projeto
Vamos supor nesse exemplo que o nome projeto em Django seja meuprojeto, a aplicação minhaapp e o script relatorio.py. Nomes nada criativos, eu sei, mas se você entendeu… é isso que importa.
Então vamos criar o arquivo /usr/bin/relatorio.py contendo as linhas abaixo:
#!/usr/bin/python from django.core.management import setup_environ import sys, datetime, settings PROJECT_PATH = '/home/usuario/www/django/meuprojeto' sys.path.insert(0, PROJECT_PATH) setup_environ(settings)
O código acima é tudo que se precisa para usar o projeto dentro do script.
Como exemplo, vamos criar a classe Relatorio, importar a aplicação em Django e instanciar as classes que queremos usar:
# Importamos a classe Cliente from minhaapp.models import Cliente # Classe para trabalhar com o projeto em Django class Relatorio(object): def teste_exibir_clientes_cadastrados(self): clientes = Cliente.objects.all() print clientes # Instanciamos a classe Relatorio e executamos o método rel = Relatorio() rel.teste_exibir_clientes_cadastrados()
Agora no terminal:
$ sudo chmod +x /usr/bin/relatorio.py $ relatorio.py
Viram como é fácil trabalhar com o projeto em Django em outro ambiente?!
Agora só falta renderizar um template e enviar o resultado por e-mail.
Renderizando um template
Temos o template relatorio.html localizado no diretório de templates do projeto:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="pt-br">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Relatório de Clientes Cadastrados</title>
</head>
<body>
<h2>Clientes cadastrados</h2>
{% for cliente in clientes %}
Nome: {{ cliente.nome }}<br />
{% endfor %}
</body>
</html>Vamos obter os clientes cadastrados, passar o resultado ao template e enviar um e-mail no formato HTML contendo o template renderizado.
O script final deve ficar assim:
#!/usr/bin/python """Dependendo da distro o caminho para o python acima pode ser outro""" from django.core.management import setup_environ import sys, datetime, settings PROJECT_PATH = '/home/gustavo/www/django/meuprojeto' sys.path.insert(0, PROJECT_PATH) setup_environ(settings) from email.MIMEMultipart import MIMEMultipart from email.MIMEText import MIMEText from email.MIMEImage import MIMEImage from smtplib import SMTP import email.Charset from unicodedata import normalize class Relatorio(object): def __init__(self): """ Construtor da classe """ self.charset = 'utf-8' self.smtp_server = 'smtp.provedor.com.br' self.smtp_user = 'usuario' self.smtp_pass = 'senha' self.sender = 'meu@email.com.br' self.sender_name = 'Meu Nome Completo' def render(self, context, template): """ Retorna o template renderizado """ if template: t = loader.get_template(template) return t.render(Context(context)) def enviar_relatorio(self): clientes = Cliente.objects.all() # Se nao houver clientes cadastrados... if not clientes.count() > 0: print u'Erro: Nenhum cliente cadastrado. O e-mail não foi enviado.' else: # Passa os clientes cadastrados para a variavel clientes no template context = {'clientes': clientes} # Obtem o template renderizado html = self.render(context, 'relatorio.html') # A funcao normalize remove caracteres acentuados html = normalize('NFKD',html).encode('ASCII','ignore') # Mais detalhes sobre o módulo email, consulte a docuemtacao do python msg_principal = MIMEMultipart('related') msg_principal['Subject'] = 'Relatorio de clientes' msg_principal['From'] = '%s <%s>' % (self.sender, self.sender_name) msg_principal['To'] = '%s <%s>' % (recipient, recipient_name) msg_principal.preamble = 'This is a multi-part message in MIME format.' msg_alternativa = MIMEMultipart('alternative') msg_alternativa.attach(MIMEText(html, 'html', _charset=self.charset)) msg_principal.attach(msg_alternativa) msg_alternativa.attach(MIMEText('Essa e uma mensagem alternativa', _charset=self.charset)) msg_alternativa.attach(MIMEText(html, 'html', _charset=self.charset)) # Tenta enviar o email try: smtp = SMTP() smtp.connect(self.smtp_server) smtp.login(self.smtp_user, self.smtp_pass) smtp.sendmail(self.sender, recipient, msg_principal.as_string()) smtp.quit() except: print u'Erro: Não foi possível enviar o e-mail.' # Instanciamos a classe Relatorio e executamos o método rel = Relatorio() rel.enviar_relatorio()
Conclusão
Acessar o ORM do Django por um script Python permite criar muitas coisas interessantes. Imagine criar um projeto no Django e usar suas classes e métodos em um projeto Python + QT por exemplo? E scripts produzindo logs em banco de dados em vez de jogar na tela ou no Syslog, tornando possível a visualização em ambiente web?
A imaginação é o limite.








Olá!
Ótimo artigo… mas vai a dica: Não seria mais interessante fazer uma “manager task” para ser executada a partir do comando “python manage.py build_report”, por exemplo?
A idéia da “manager task” também é interessante e confesso que se quer passou pela minha cabeça. Entretanto, dependendo do tipo de uso e probabilidade de haver implementações no código, poderia ser tornar mais trabalhoso manter como “manager task”.
Obrigado pela dica!