Páginas

Showing posts with label django. Show all posts
Showing posts with label django. Show all posts

Sunday, February 8, 2015

Falcon, Django, PyPy & Docker

Late last year I learned about the Falcon Framework, one more to the list of many Python frameworks out there. Falcon focus on performance, having quite impressive benchmarks.



So I was wondering how can you have super fast API endpoints in your Python web service while still serving some legacy code in Django, or just any other WSGI-compliant framework.

The outcome of my experiment lives in this GitHub repo: rhcarvalho/django-plus-falcon-pypy.

Friday, August 14, 2009

Filtros personalizados no admin do Django

Então ontem estava eu pensando na melhor forma de colocar meu próprio filtro naquela caixa lateral da interface padrão da aplicação 'admin' do Django.

Num ModelAdmin você normalmente pode especificar um nome de um campo que faça parte do seu Model, mas não pode fazer por exemplo um filtro por um valor computado.

Eu já tinha começado por uma abordagem bem rasteira de estender os templates padrão e colocar meu filtro logo abaixo dos outros filtros (guardado numa shelf do Bazaar :P). Mas eis que a melhor solução até agora foi a do Marinho Brandão e do Semente no DjangoSnippets.

Fiz algumas poucas alterações para pegar do banco apenas valores distintos, e apesar de ser um ponto que pode prejudicar o desempenho, gero a lista a cada requisição -- caso contrário, a lista só será recomputada quando o servidor for reiniciado. Talvez fosse mais interessante gerar um lista fixa de opções (implicando nenhum acesso ao banco), ou mesmo ao invés de consultar N linhas do banco e remover valores duplicados, poderia partir de uma lista fixa e chegar se o banco contém resultados para o valor da lista.

# meu_projeto/minha_app/admin/filterspecs.py
from django.contrib.admin.filterspecs import FilterSpec, ChoicesFilterSpec
from django.utils.encoding import smart_unicode
from django.utils.translation import ugettext as _


class AlphabeticFilterSpec(ChoicesFilterSpec):
"""
Adds filtering by first char (alphabetic style) of values in the admin
filter sidebar. Set the alphabetic filter in the model field attribute
'alphabetic_filter'.

my_model_field.alphabetic_filter = True

Based on http://www.djangosnippets.org/snippets/1051/
"""

def __init__(self, f, request, params, model, model_admin):
super(AlphabeticFilterSpec, self).__init__(f, request, params, model,
model_admin)
self.lookup_kwarg = '%s__istartswith' % f.name
self.lookup_val = request.GET.get(self.lookup_kwarg, None)
self.model = model

def choices(self, cl):
yield {'selected': self.lookup_val is None,
'query_string': cl.get_query_string({}, [self.lookup_kwarg]),
'display': _('All')}

f = self.field
values_list = self.model.objects.distinct().values_list(f.name, flat=True)
# getting the first char of values
lookup_choices = sorted(set(val[0].lower() for val in values_list if val))

for val in lookup_choices:
yield {'selected': smart_unicode(val) == self.lookup_val,
'query_string': cl.get_query_string({self.lookup_kwarg: val}),
'display': val.upper()}


# registering the filter
FilterSpec.filter_specs.insert(0, (lambda f: getattr(f, 'alphabetic_filter', False),
AlphabeticFilterSpec))


E para ativar seu filtro para algum campo:

# meu_projeto/minha_app/models.py
# [...]

class Empresa(models.Model):
nome = models.CharField(max_length=64)
nome.alphabetic_filter = True
# [...]


# meu_projeto/minha_app/admin/__init__.py
# [...]
class EmpresaAdmin(admin.ModelAdmin):
list_display = ('nome', 'cnpj', 'inicio_convenio',
'_termino_convenio', 'natureza')
list_display_links = ('nome', 'cnpj')
list_filter = ('natureza', 'termino_convenio', 'inicio_convenio', 'nome')


# meu_projeto/urls.py
# Chama o código que registra o filtro personalizado
import sgtce.admin.filterspecs
# [...]

Saturday, March 14, 2009

Escreva menos, use Python

Ontem vivenciei uma situação em que tivemos que manter um código legado para inserir mais uma pequena funcionalidade.
Uma "regra" aqui é não se desviar do que você tem que fazer (que possui mais valor de negócio) para cuidar de outras coisas. Outra "regra" é não deixar débito técnico.
A grande dificuldade é encontrar o balanço adequado entre esses dois objetivos, e ainda assim implementar sua nova funcionalidade.

Bem, livre de qualquer "regra", vou aproveitar esse post para introduzir 3 recursos do Python que nem sempre são lembrados, e que nos permitem economizar linhas de código, escrevendo de forma clara e concisa o que queremos fazer.

  1. Método get dos dicionários;
  2. Método split das strings;
  3. Função builtin/global setattr.
Vamos partir do seguinte código legado:

#...
from django.shortcuts import render_to_response
from model import Cadastro
#...

def tratar_cadastro(request):
post = request.POST
#...
cadastro = Cadastro()
#...

if post.has_key('nome'):
cadastro.nome = post['nome']

if post.has_key('endereco'):
cadastro.endereco = post['endereco']

if post.has_key('telefone'):
cadastro.telefone = post['telefone']

if post.has_key('email'):
cadastro.email = post['email']

return render_to_response('cadastro/index.html', {"cadastro": cadastro})


É um esboço de uma View do Django. Como podem ver, espera-se que a view seja requisitada via HTTP POST, com a passagem de diversos parâmetros, que ficam armazenados no dicionário Python request.POST. Esse papo de cadastro, nome, telefone, etc é só para ilustrar.
Então o código que temos é uma sequência de if's que setam atributos de uma instância de Cadastro que é posteriormente passada para o template.

Podemos imaginar que temos um formulário numa página que, ao ser submetido, exibe as informações preenchidas para confirmação.

Agora temos que adicionar mais um campo ao cadastro. Digamos que nosso cliente pediu para guardar também o campo "idade". Por simetria, acabaria acontecendo o seguinte:
#...
from django.shortcuts import render_to_response
from model import Cadastro
#...

def tratar_cadastro(request):
post = request.POST
#...
cadastro = Cadastro()
#...

if post.has_key('nome'):
cadastro.nome = post['nome']

if post.has_key('idade'):
cadastro.idade = post['idade']


if post.has_key('endereco'):
cadastro.endereco = post['endereco']

if post.has_key('telefone'):
cadastro.telefone = post['telefone']

if post.has_key('email'):
cadastro.email = post['email']

return render_to_response('cadastro/index.html', {"cadastro": cadastro})


Pra que mudar o que já funciona? Bem, vamos então ao primeiro dos tópicos.
Usar o dict.has_key é ruim e desnecessário. Esse método é deprecated no Python 2.6 e já foi removido da linguagem no Python 3.0, em favor da forma chave in dicionario. No lugar dele e do if deveríamos usar o dict.get.
Mas o que esse método faz? Ele retorna o valor de dict['key'] se o dicionário tem a chave 'key', senão retorna None ou outro valor padrão que o programador especificar.
Ficaria assim:
#...
from django.shortcuts import render_to_response
from model import Cadastro
#...

def tratar_cadastro(request):
post = request.POST
#...
cadastro = Cadastro()
#...

if post.has_key('nome'):
cadastro.nome = post['nome']

cadastro.idade = post.get('idade', u'Campo não preenchido')

if post.has_key('endereco'):
cadastro.endereco = post['endereco']

if post.has_key('telefone'):
cadastro.telefone = post['telefone']

if post.has_key('email'):
cadastro.email = post['email']

return render_to_response('cadastro/index.html', {"cadastro": cadastro})


Além de economizar linhas, temos um código menos redundante e seguindo o princípio de "It's better to beg forgiveness than to ask permission".
Vamos refatorar o resto do código:
#...
from django.shortcuts import render_to_response
from model import Cadastro
#...

def tratar_cadastro(request):
post = request.POST
#...
cadastro = Cadastro()
#...

cadastro.nome = post.get('nome')
cadastro.idade = post.get('idade')
cadastro.endereco = post.get('endereco')
cadastro.telefone = post.get('telefone')
cadastro.email = post.get('email')

return render_to_response('cadastro/index.html', {"cadastro": cadastro})


Assim, caso um dos campos não venha no POST, será atribuído o valor None ao atributo do objeto cadastro.
Notem que, assim como antes, existe bastante duplicação de código (por razões práticas, fiz um exemplo com poucas chaves do dicionário, mas o caso que me deparei eram mais que dez...).
Agora então é a hora de introduzir os outros dois conceitos, o split e o setattr.
O primeiro transforma uma string em lista através de um separador. Exemplo:
>>>  print "um, dois, tres".split(", ")
['um', 'dois', 'tres']

Isso pode ser usado para declararmos uma lista sem precisar de muitas aspas e vírgulas... basta colocar todos os itens da lista em uma string e usar o split.
O outro faz o mesmo que objeto.atributo = valor, porém atributo pode ser algo dinâmico, definido em tempo de execução.
Com essas mudanças, nosso código ganha uma tremenda compressão, sem perder legibilidade:
#...
from django.shortcuts import render_to_response
from model import Cadastro
#...

def tratar_cadastro(request):
post = request.POST
#...
cadastro = Cadastro()
#...

for attr in 'nome idade endereco telefone email'.split():
setattr(cadastro, attr, post.get(attr))


return render_to_response('cadastro/index.html', {"cadastro": cadastro})

Thursday, September 4, 2008

Django 1.0 e Google Chrome

Como todos que estão ao meu redor sabem, estou super empolgado com o lançamento oficial do Django 1.0. Eu realmente gostaria de estar lá em Mountain View no dia 6 para comemorar!

O Ian Ozsvald, co-fundador do ShowMeDo, postou no grupo django-users um screencast de 1 minuto para mostrar como o Django é legal! Hora de trazer os amigos!
O post original no site do Ian: http://ianozsvald.com/2008/09/04/django-in-under-a-minute-screencast/


O screencast também está disponível no Vimeo, ShowMeDo e YouTube.

Além do mais, o Ian usou o novíssimo Google Chrome nas "filmagens". Eu instalei o Chrome ontem em uma máquina virtual para testar. A primeira impressão foi boa, apesar de algumas estranhezas com a renderização. Bem, nada que me faça largar o Firefox :D, não neste momento.

Engraçado foi descobrir que o Chrome desconfia até mesmo da página do Gmail! Vejam só o que aconteceu:

Google Chrome on Gmail

Tuesday, August 26, 2008

Django Templates + Dreamweaver

Resolvi fazer uma rápida pesquisa para saber o que é possível fazer para editar Templates do Django num editor como o Dreamweaver, depois que uma amiga expressou a vontade de usar tal ferramenta para criar o layout de um site.

Para começar, por filosofia, a linguagem de templates do Django não tenta ser amigável a editores WYSIWYG, como o Dreamweaver:

http://www.djangoproject.com/documentation/design_philosophies/

Assume designer competence

The template system shouldn’t be designed so that templates necessarily are displayed nicely in WYSIWYG editors such as Dreamweaver. That is too severe of a limitation and wouldn’t allow the syntax to be as nice as it is. Django expects template authors are comfortable editing HTML directly.


No meu caso, eu assumo competência para usar um editor de textos normal, ou usar o Dreamweaver em modo Code View (pelas funcionalidades do editor, como code-completion, code-folding, referência à mão, busca e substituição, etc), porém, de fato, nem todos os desenvolvedores trabalham assim.

Li o post do Mark Ramm sobre "user-friendly templates", e confirmei que temos aí uma grande questão de opinião. Uns defendendo linguagens que utilizam atributos em tags, como estou acostumado a usar no TurboGears, e outros mostrando vantagens notadamente no sistema do Django, com marcação própria. Eu estou aprendendo a gostar da abordagem do Django, pela quantidade reduzida de código que preciso escrever. Tudo bem que os templates em Kid geralmente são feitos copiando um já existente para garantir o código repetitivo de doctypes, xml namespaces, e resto da estrutura básica, mas a simplicidade de escrever arquivos pequenos, só com aquilo que realmente interessa, é tentador!

A propósito, a breve discussão do Mark com o Simon Willison sobre como eles estão usando AJAX me trouxe várias idéias! Já faz algum tempo que não brinco com AJAX pra valer, entretanto recentemente dei uma opinada no código de uma amiga que está desenvolvendo uma aplicação de redes sociais, e agora tenho novidades para acrescentar.

Ainda não tenho experiência suficiente no Django para saber se na hora de manter o código essa simplificação trará alguma dificuldade. A princípio, creio que não, já que geralmente me encontro usando o Firebug contra páginas servidas localmente (nunca abri um arquivo .kid no Firefox para 'debugar' e não consigo imaginar alguém fazendo isso). Além do mais, o Kid também tem herança, e com isso os arquivos que manipulamos não são exatamente o que teremos de output na tela, pensando na visualização do layout com CSS, por exemplo.

Por fim, o Beshr Kayali desenvolveu uma extensão para o Dreamweaver, DjangoExt, que promete facilitar a vida. Eu não tive paciência de me cadastrar no site da Adobe só para baixar essa extensão. Se alguém tiver o arquivo, por favor entre em contato.

Saturday, August 23, 2008

Mais Django!

Definitivamente vou começar a usar Django.
Tudo que vi recentemente tem me empolgado bastante, e acabei reunindo algumas informações úteis:

Django, documentação 100% em sincronia com o código no repositório svn!
http://www.djangoproject.com/documentation/

Um livro, escrito pelos autores do framework, gratuito:
http://www.djangobook.com

Django roda no Jython!
http://code.djangoproject.com/wiki/DjangoAndJython

Várias aplicações prontas (código que se integra ao seu projeto para desempenhar funções corriqueiras)!
http://pinax.hotcluboffrance.com/apps/

Novidades no meu delicious.com:
http://delicious.com/rhcarvalho/django

E tem mais:

Django é muito mais popular que o Turbogears!
turbogears

0.07
django

1.00


http://google.com/trends?q=turbogears%2C+django%2C+&ctab=0&geo=all&date=all&sort=1


Django é tão *hot* quanto J2EE, e em ritmo crescente, enquanto o Java está decadente!
j2ee

7.00
django

1.00


http://google.com/trends?q=j2ee%2C+django%2C+&ctab=0&geo=all&date=all&sort=1


Acredito que dá para juntar templates e aplicações prontas para acelerar o desenvolvimento.
O projeto Pinax é muito interessante nesse sentido.

Até a próxima!

Monday, August 11, 2008

Instalando Django

Finalmente comecei a brincar com o Django. Está sendo uma experiência muito satisfatória até o momento!
A instalação foi extremamente fácil. No ambiente do Ubuntu, tudo que precisei fazer foi:
1) Pegar uma cópia do repositório com a versão mais atual (eu salvei no meu /home por enquanto)
svn co http://code.djangoproject.com/svn/django/trunk/ django
2) Criar alguns symlinks para que o Python acesse os arquivos do Django
sudo ln -s `pwd`/django/django /usr/lib/python2.5/site-packages/django
sudo ln -s `pwd`/django/django/bin/django-admin.py /usr/local/bin


Dependendo do seu sistema, o site-packages pode estar em outro lugar.
Para descobrir onde, rode o seguinte comando no console:
python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()"

Pronto! Agora basta seguir para o tutorial em:
http://www.djangoproject.com/documentation/tutorial01/

As instruções oficiais estão em http://www.djangoproject.com/documentation/install/.

Dada a absurda facilidade para instalar, a diversão fica aumentada!
Para ambiente Windows, basta usar algum programa como o RapidSVN ou TortoiseSVN para fazer o checkout do repositório, e colocar o diretório do Django diretamente em C:\Python25\Lib\site-packages\django e copiar o arquivo django-admin.py para C:\Python25\Scripts.

Thursday, August 7, 2008

Primeiras impressões no Django

De alguma forma, eu não tinha bons olhos pro Django. Parece que depois que comecei a usar o Turbogears eu me adaptei ao estilo de trabalho, e quando o Django fazia diferente acabava parecendo algo ruim.

Tirei isso da minha cabeça. Afinal, um bom tempo se passou, tanto o TG quanto o Django mudaram. O TG, aliás, é algo absolutamente diferente com a versão 2.0. Fui no site do Django e tive boas surpresas. Fiquei empolgado com o que vi. Django começou na frente.

Minhas percepções superficiais:
  • Gostei da ferramenta admin, que gera automaticamente uma inferface para classes do Model para seu projeto, e serve como scaffolding do Rails, ou quase isso. O TG não faz nada nesse nível out of the box. Existe o módulo tgcrud que supostamente faria o scaffolding, mas parece ser um projeto pouco ativo. No Turbogears temos uma interface de administração, mas ela não é tão facilmente imbutível no projeto em si. Nela, o CatWalk permite criar, ler, editar e remover itens do banco de dados via interface amigável, porém, para disponibilizar essa ferramenta na sua própria aplicação é preciso fazer algumas magias, e ainda assim não fica apresentável *.
  • Ainda em relação ao admin site, facilidades como: filtro automático em tabelas por determinado valor em uma coluna, busca baseado em uma ou mais colunas, e ordenação automática (basta clicar no título da coluna), são muito interessantes para permitir ações comumente realizadas em planilhas eletrônicas.
  • Enquanto o TG 2.0 não tem previsão de lançamento, o Django 1.0 vem no início de Setembro. É um ótimo momento de começar e já ir usando a versão que está no repositório SVN.
  • A filosofia do Django também me pareceu interessante, embora seja estranho eles quererem se chamar MTV ao invés de MVC. O que é View no MVC é Template no Django, e o que é Controller no MVC é View no Django. Só mesmo o Model se salvou.
  • Outro ponto que me causou desconforto desde o primeiro contato no início do ano passado foram os templates. Eu gosto do Kid Templating usado no TG pela cara de HTML e a limpeza visual do código. Todavia, não parece que o template do Django seja tão ruim assim, é mais questão de costume. O sistema de templating do Django suporta auto-escape, herança, e, hmm... *não* suporta código Python arbitrário! Isso parece ótimo, forçando o programador a não colocar lógica no Template (View do MVC...).

Depois que eu instalar o Django e ter percepções reais, posto o resultado!

* Se alguém se interessar em saber como colocar o CatWalk dentro de um projeto do Turbogears (sem usar o tg-admin toolbox), eu posso publicar a respeito.

Links:
http://www.djangoproject.com/documentation/tutorial02/
http://www.djangoproject.com/documentation/design_philosophies/
http://www.djangoproject.com/documentation/templates/

Python ou Ruby? Turbogears x Django x Rails

Bem, não sou o primeiro a fazer esse questionamento. Comecei a procurar a opinião das pessoas, notadamente em blogs, e a olhar os sites de tutorias de cada opção para formar a minha.

Faz um ano que venho desenvolvendo aplicações web usando o Turbogears, que considero muito legal, comparado a coisas como Java para web, PHP, e qualquer outra coisa mais "burocrática".
O TG me permite trabalhar de forma simples e that just works.

Lembro que antes de usar o TG, depois de algum tempo me divertindo com Python, eu queria experimentar ele ou o Django, que eram os frameworks para Python falados na época. Claro, foi também quando fui ver o que era o tal do Zope (nome que vi no dia que resolvi aprender Python...).

Ultimamente o Ruby on Rails tem cada vez mais "batido à porta", e não falta vontade de cair dentro e desenvolver com ele. Até agora eu só vi screencasts, li bastante, ouvi podcasts e mexi em aplicações prontas.

Mas afinal, porquê todo esse papo furado? Hoje temos mais um projeto de desenvolvimento web. Um projeto que deve durar em torno de dois anos no "rítmo universitário". Para ele, a equipe parou pra pensar no que usaríamos. Essa é uma boa hora para ver algo diferente do Turbogears, conhecer novos mundos... mas, o quão novos? Continuar no Python ou pular pro Ruby?
Eu adoro Python, sim, amo mesmo. Deixamos as outras opções de lado e vamos nos decidir entre TG, Django e RoR.

Nos próximos posts vou relatar minha percepção da relação entre Django x TG e RoR x TG.
Como não tenho experiência prática nos frameworks da esquerda, comentários são muito bem vindos! O que pode parecer bom num primeiro momento, pode esconder fraquezas a longo prazo.