<?xml version="1.0" encoding="UTF-8"?> <rss version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
><channel><title>Francisco Souza &#187; django</title> <atom:link href="http://www.franciscosouza.com.br/tag/django/feed/" rel="self" type="application/rss+xml" /><link>http://www.franciscosouza.com.br</link> <description>Open source, Python, Django, Java, agile e outras coisas mais</description> <lastBuildDate>Tue, 24 Jan 2012 04:20:18 +0000</lastBuildDate> <language>en</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <generator>http://wordpress.org/?v=3.3.1</generator> <item><title>Como foi a #qconsp 2011?</title><link>http://www.franciscosouza.com.br/2011/09/13/como-foi-a-qconsp-2011/</link> <comments>http://www.franciscosouza.com.br/2011/09/13/como-foi-a-qconsp-2011/#comments</comments> <pubDate>Tue, 13 Sep 2011 22:13:17 +0000</pubDate> <dc:creator>Francisco Souza</dc:creator> <category><![CDATA[eventos]]></category> <category><![CDATA[desenvolvimento de software]]></category> <category><![CDATA[django]]></category> <category><![CDATA[palestras]]></category> <category><![CDATA[python]]></category> <category><![CDATA[splinter]]></category> <category><![CDATA[TDD]]></category> <category><![CDATA[testes de interface]]></category><guid isPermaLink="false">http://www.franciscosouza.com.br/?p=1208</guid> <description><![CDATA[Rolou no último final de semana a segunda edição do QCONSP, principal evento de arquitetos e desenvolvedores do Brasil. O evento é organizado pela InfoQ Brasil em parceria com a Caelum.]]></description> <content:encoded><![CDATA[<p>Rolou no último final de semana a segunda edição do <a title="QCONSP" href="http://www.qconsp.com" target="_blank">QCONSP</a>, principal evento de arquitetos e desenvolvedores do Brasil. O evento é organizado pela <a title="InfoQ BR" href="http://www.infoqbr.com" target="_blank">InfoQ Brasil</a> em parceria com a <a title="Caelum" href="http://www.caelum.com.br" target="_blank">Caelum</a>.</p><p>O evento foi realmente muito bom, com um nível técnico praticamente inquestionável. A organização também é incrível. A forma como a Caelum coleta feedback deveria ser um exemplo para todo mundo, sem falar na comida em abundância :) Pena ter faltado um pouco de tomadas e alguns problemas com a rede wifi (é, fazer rede wireless pra 850 pessoas não é fácil&#8230;), mas o evento foi realmente muito bom.<span id="more-1208"></span></p><p>No domingo apresentei, junto ao <a title="Andrews Medina" href="http://www.andrewsmedina.com" target="_blank">Andrews Medina</a>, a palestra <a title="Os complicados testes de interfaces e componentes web" href="http://www.qconsp.com/palestra/andrews-medina/os-complicados-testes-de-interfaces-e-componentes-web" target="_blank">Os complicados testes de interfaces e componentes web</a>. Na palestra, apresentamos um breve histórico e alguns conceitos envolvidos com os testes de interfaces e componentes ricos. Mostramos algumas características do <a title="QUnit" href="http://docs.jquery.com/Qunit" target="_blank">QUnit</a>, <a title="Jasmine" href="http://pivotal.github.com/jasmine/" target="_blank">Jasmine</a>, <a title="jasmine-splinter-runner" href="https://github.com/cobrateam/jasmine-splinter-runner" target="_blank">jasmine-splinter-runner</a>, <a title="Splinter" href="http://splinter.cobrateam.info" target="_blank">Splinter</a>, <a title="Selenium" href="http://seleniumhq.org" target="_blank">Selenium</a>, <a title="Sikuli" href="http://sikuli.org/" target="_blank">Sikuli</a>, dentre outras ferramentas. Incluímos ainda alguns exemplos com código ao vivo com o Jasmine, Splinter e Sikuli.</p><p>Os slides estão aqui:</p><div id="__ss_9215230" style="width: 100%; text-align: center;"><strong style="display: block; margin: 12px 0 4px;"><a title="Os complicados testes de interface" href="http://www.slideshare.net/franciscosouza/os-complicados-testes-de-interface" target="_blank">Os complicados testes de interface</a></strong> <iframe src="http://www.slideshare.net/slideshow/embed_code/9215230" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" width="425" height="355"></iframe></div><p>&nbsp;</p><p>E os códigos que apresentamos estão no Github: <a title="Exemplos de código da palestra na QConSP 2011" href="https://github.com/fsouza/palestra-qconsp-2011" target="_blank">https://github.com/fsouza/palestra-qconsp-2011</a>.</p> ]]></content:encoded> <wfw:commentRss>http://www.franciscosouza.com.br/2011/09/13/como-foi-a-qconsp-2011/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>QConSP 2011: eu vou, e vou palestrar!</title><link>http://www.franciscosouza.com.br/2011/07/11/qconsp-2011-eu-vou-e-vou-palestrar/</link> <comments>http://www.franciscosouza.com.br/2011/07/11/qconsp-2011-eu-vou-e-vou-palestrar/#comments</comments> <pubDate>Mon, 11 Jul 2011 21:00:24 +0000</pubDate> <dc:creator>Francisco Souza</dc:creator> <category><![CDATA[desenvolvimento de softwares]]></category> <category><![CDATA[eventos]]></category> <category><![CDATA[desenvolvimento de software]]></category> <category><![CDATA[django]]></category> <category><![CDATA[python]]></category> <category><![CDATA[qconsp]]></category> <category><![CDATA[splinter]]></category> <category><![CDATA[TDD]]></category> <category><![CDATA[testes de interface]]></category><guid isPermaLink="false">http://www.franciscosouza.com.br/?p=1201</guid> <description><![CDATA[Em Setembro vai rolar a segunda edição da QConSP, versão brasileira do principal evento de arquitetos e desenvolvedores do mundo. O evento é organizado pelo InfoQ Brasil em parceria com a Caelum. No ano passado o evento bombou, e tenho certeza que este ano não será diferente.]]></description> <content:encoded><![CDATA[<p>Em Setembro vai rolar a segunda edição da QConSP, versão brasileira do <a title="QCon" href="http://qconferences.com/" rel="nofollow" target="_blank">principal evento de arquitetos e desenvolvedores do mundo</a>. O evento é organizado pelo <a title="InfoQ Brasil" href="http://infoq.com/br/" rel="nofollow" target="_blank">InfoQ Brasil</a> em parceria com a <a title="Caelum" href="http://www.caelum.com.br" rel="nofollow" target="_blank">Caelum</a>. No <a title="Como foi a QConSP 2010?" href="http://blog.caelum.com.br/qconsp-2010-como-foi-o-principal-evento-de-arquitetos-e-desenvolvedores-no-brasil/" rel="nofollow" target="_blank">ano passado o evento bombou</a>, e tenho certeza que este ano não será diferente.</p><p>Marcarei presença apresentando, junto ao <a title="Andrews Medina" href="http://www.andrewsmedina.com" rel="nofollow" target="_blank">Andrews Medina</a>, a palestra <a title="Os complicados testes de interfaces e componentes web" href="http://www.qconsp.com/palestra/andrews-medina/os-complicados-testes-de-interfaces-e-componentes-web" rel="nofollow" target="_blank">&#8220;Os complicados testes de interfaces e componentes web&#8221;</a>, na trilha <a title="QConSP 2011 | O browser como plataforma" href="http://www.qconsp.com/track/browser" rel="nofollow" target="_blank">&#8220;O browser como plataforma&#8221;</a>. Na palestra, apresentaremos algumas técnicas para testar interfaces web, seja de forma integrada ou de forma unitária (componentes isolados), usando ferramentas como <a title="Splinter" href="http://splinter.cobrateam.info" target="_blank">Splinter</a> e <a title="Jasmine" href="http://pivotal.github.com/jasmine/" rel="nofollow" target="_blank">Jasmine</a>.</p><p>Além da nossa palestra, o evento também contará com apresentações de feras como <a title="Guilherme Silveira" href="http://www.qconsp.com/palestrante/guilherme-silveira" rel="nofollow" target="_blank">Guilherme Silveira</a>, <a title="Jim Webber" href="http://www.qconsp.com/palestrante/jim-webber" rel="nofollow" target="_blank">Jim Webber</a>, <a title="Evan Weaver" href="http://www.qconsp.com/palestrante/evan-weaver" rel="nofollow" target="_blank">Evan Weaver</a> e <a title="Palestrantes da QConSP 2011" href="http://www.qconsp.com/palestrantes" rel="nofollow" target="_blank">outros</a>. A QConSP 2011 vai acontecer nos dias 10 e 11 de Setembro, no Centro Fecomercio de Eventos, em São Paulo. Ainda da tempo de se <a title="Inscrições na QConSP 2011" href="http://www.qconsp.com/inscricoes" rel="nofollow" target="_blank">inscrever</a>!</p><p>Nos vemos lá! :)</p> ]]></content:encoded> <wfw:commentRss>http://www.franciscosouza.com.br/2011/07/11/qconsp-2011-eu-vou-e-vou-palestrar/feed/</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>FISL 12: eu fui, e foi foda!</title><link>http://www.franciscosouza.com.br/2011/07/04/fisl-12-eu-fui-e-foi-foda/</link> <comments>http://www.franciscosouza.com.br/2011/07/04/fisl-12-eu-fui-e-foi-foda/#comments</comments> <pubDate>Mon, 04 Jul 2011 22:51:37 +0000</pubDate> <dc:creator>Francisco Souza</dc:creator> <category><![CDATA[open source]]></category> <category><![CDATA[cobrateam]]></category> <category><![CDATA[desenvolvimento de software]]></category> <category><![CDATA[django]]></category> <category><![CDATA[eventos]]></category> <category><![CDATA[fisl]]></category> <category><![CDATA[python]]></category> <category><![CDATA[splinter]]></category><guid isPermaLink="false">http://www.franciscosouza.com.br/?p=1195</guid> <description><![CDATA[Certa vez li em algum lugar que o primeiro FISL nunca se esquece, e começo a acredita que seja verdade. Além da agradável temperatura abaixo de 10 graus em Porto Alegre, o evento foi uma oportunidade incrível de conhecer algumas figurinhas da comunidade e muito aprendizado.]]></description> <content:encoded><![CDATA[<p>Certa vez li em algum lugar que o primeiro FISL nunca se esquece, e começo a acredita que seja verdade. Além da agradável temperatura abaixo de 10 graus em Porto Alegre, o evento foi uma oportunidade incrível de conhecer algumas figurinhas da comunidade e muito aprendizado.</p><p>Participei apenas de dois dias do evento. Na sexta-feira, apresentei junto com o <a title="Andrews Medina" href="http://www.andrewsmedina.com" target="_blank">Andrews Medina</a> a palestra <a title="Testando interfaces web com Splinter" href="http://www.slideshare.net/franciscosouza/testando-interfaces-web-com-splinter-8481476" target="_blank">&#8220;Testando interfaces web com Splinter&#8221;</a> e gastei um tempinho no stand da <a title="Globo.com" href="http://globo.com" target="_blank">Globo.com</a>, onde também apresentei o <a title="django-htmlmin" href="https://github.com/cobrateam/django-htmlmin" target="_blank">django-htmlmin</a>, na sessão de lighting talks da comunidade <a title="Python Brasil" href="http://www.python.org.br" target="_blank">Python Brasil</a> (<a title="django-htmlmin" href="http://www.slideshare.net/franciscosouza/djangohtmlmin-reduzindo-tamanho-do-response-com-python" target="_blank">você pode ver os slides aqui</a>).<span id="more-1195"></span></p><p><img class="aligncenter size-full wp-image-1199" title="Lighting talk da comunidade Python Brasil no stand da Globo.com" src="http://cdn.souza.cc/2011/07/js-lighting-talk-pythonbrasil1.jpg" alt="Lighting talk da comunidade Python Brasil no stand da Globo.com" width="600" height="450" /></p><p>No sábado eu e o Andrews fomos convidados pelo <a title="Igor Macaubas" href="http://www.macaubas.com" target="_blank">Igor Macaubas</a> para falar rapidamente sobre o <a title="Posts sobre splinter" href="http://www.franciscosouza.com.br/tag/splinter">Splinter</a> na palestra &#8220;Por que amamos open source na Globo.com&#8221;. O Igor mandou muito bem na palestra, e a galera pode ver um pouco do que consumimos e produzimos de open source dentro e fora da Globo.com.</p><p><img class="aligncenter size-full wp-image-1198" title="Andrews Medina, Francisco Souza e Igor Macaúbas no FISL 12" src="http://cdn.souza.cc/2011/07/andrews-francisco-igor-fisl.jpg" alt="Andrews Medina, Francisco Souza e Igor Macaúbas no FISL 12" width="600" height="450" /></p><p>Como não deveria deixar de ser, o networking do evento foi excelente. Eu esperava bem menos do FISL, principalmente no quesito técnico, e fiquei totalmente impressionado com o evento. Após altos papos e um excelente churrasco na casa do <a title="Xiru" href="http://blog.xiru.org/" target="_blank">Xiru</a>, estou pensando seriamente em desistir de não ir à <a title="Python Brasil" href="http://www.pythonbrasil.org.br" target="_blank">Python Brasil</a> esse ano :) Aliás, se você pode ir ao evento, não perca!</p><p>No mais, é isso! Fica agora a vontade de querer estar no FISL no ano que vem, agitando a comunidade e comendo muito churrasco!</p> ]]></content:encoded> <wfw:commentRss>http://www.franciscosouza.com.br/2011/07/04/fisl-12-eu-fui-e-foi-foda/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Vai ao FISL?! Junte-se ao #cobrateam em mais um splinter sprint!</title><link>http://www.franciscosouza.com.br/2011/06/27/vai-ao-fisl-junte-se-ao-cobrateam-em-mais-um-splinter-sprint/</link> <comments>http://www.franciscosouza.com.br/2011/06/27/vai-ao-fisl-junte-se-ao-cobrateam-em-mais-um-splinter-sprint/#comments</comments> <pubDate>Mon, 27 Jun 2011 05:20:45 +0000</pubDate> <dc:creator>Francisco Souza</dc:creator> <category><![CDATA[desenvolvimento de softwares]]></category> <category><![CDATA[cobrateam]]></category> <category><![CDATA[desenvolvimento de software]]></category> <category><![CDATA[django]]></category> <category><![CDATA[eventos]]></category> <category><![CDATA[open source]]></category> <category><![CDATA[python]]></category> <category><![CDATA[splinter]]></category> <category><![CDATA[sprints]]></category><guid isPermaLink="false">http://www.franciscosouza.com.br/?p=1167</guid> <description><![CDATA[Isso mesmo! Estamos organizando mais um splinter sprint, projeto open source do #cobrateam. Vai acontecer entre os dias 29 de Junho e 02 de Julho, durante o FISL - Fórum Internacional de Software Livre -, em Porto Alegre. Se você não vai ao evento, sem problemas, junte-se a nós remotamente! :) Vou aproveitar o espaço para prover algumas informações sobre o Splinter, roubando do post do Andrews Medina...]]></description> <content:encoded><![CDATA[<p><img class="alignleft size-full wp-image-1169" title="FISL - Fórum Internacional de Software Livre" src="http://cdn.souza.cc/2011/06/fisl-logo1.jpg" alt="FISL - Fórum Internacional de Software Livre" width="200" height="199" />Isso mesmo! Estamos organizando mais um <a title="Splinter" href="http://splinter.cobrateam.info" target="_blank">splinter</a> sprint, projeto open source do <a title="#cobrateam" rel="nofollow" href="http://cobrateam.info" target="_blank">#cobrateam</a>. Vai acontecer entre os dias 29 de Junho e 02 de Julho, durante o <a title="FISL - Fórum Internacional de Software Livre" rel="nofollow" href="http://fisl.softwarelivre.org" target="_blank">FISL</a> &#8211; Fórum Internacional de Software Livre -, em Porto Alegre. Se você não vai ao evento, sem problemas, junte-se a nós remotamente! :) Vou aproveitar o espaço para prover algumas informações sobre o Splinter, roubando do post do <a title="Andrews Medina" href="http://andrewsmedina.com" target="_blank">Andrews Medina</a>&#8230;<br /> <span id="more-1167"></span></p><h3>O que é o splinter?</h3><p>Splinter é uma ferramenta <a title="Posts sobre open source" href="http://www.franciscosouza.com.br/tag/open-source">open source</a> para testes de aplicações web utilizando <a title="Posts sobre Python" href="http://www.franciscosouza.com.br/tag/python" target="_blank">Python</a>. Com ele, é possível automatizar ações do browser, como visitar URLs e interagir com formulários e elementos HTML.</p><p>Página do splinter no github: <a title="Splinter at Github" rel="nofollow" href="https://github.com/cobrateam/splinter" target="_blank">https://github.com/cobrateam/splinter</a></p><p>Site e documentação do splinter: <a title="Splinter - automate web application actions using python" rel="nofollow" href="http://splinter.cobrateam.info" target="_blank">http://splinter.cobrateam.info</a></p><h3>O que é um splinter sprint?</h3><p>Um splinter sprint é uma desculpa para desenvolvedores reunirem-se , focados por um tempo para melhorar o splinter, corrigindo <a title="Splinter issues" rel="nofollow" href="http://github.com/splinter/cobrateam/issues" target="_blank">bugs</a>, adicionando features e melhorando a <a title="Splinter docs" rel="nofollow" href="http://splinter.cobrateam.info/docs/" target="_blank">documentação</a>.</p><p>Qualquer um, em qualquer lugar do mundo, pode participar e contribuir. Se você nunca contribuiu com o splinter antes, esta é a oportunidade perfeita para você começar!</p><h3>Onde e quando?</h3><p>Durante todos os dias do evento (de 29 de junho a 02 de julho). Você pode participar presencialmente, em Porto Alegre, ou remotamente.</p><h3>Como contribuir?</h3><ol><li>Escolha uma <a title="Splinter issues" rel="nofollow" href="http://github.com/cobrateam/splinter/issues" target="_blank">issue</a></li><li>Faça um fork</li><li>Faça commit e push da sua contribuição</li><li>Envie um &#8220;pull request&#8221;. <em>Não se esqueça: todas as novas features devem ser testadas e documentadas.</em></li></ol><h3>Preparando-se para o sprint</h3><p>Instale um <a title="IRC" rel="nofollow" href="http://en.wikipedia.org/wiki/Internet_Relay_Chat" target="_blank">IRC</a> client, para que você possa juntar-se a nós no canal #cobrateam no Freenode. Se estiver afim de participar, seja presencialmente no FISL, ou online, sua ajuda será bem vinda!</p><p>Para mais informações, acesse a <a title="Splinter sprint 29 de junho a 02 de julho" rel="nofollow" href="https://github.com/cobrateam/splinter/wiki/sprint29jun2011" target="_blank">página do sprint no wiki</a>.</p> ]]></content:encoded> <wfw:commentRss>http://www.franciscosouza.com.br/2011/06/27/vai-ao-fisl-junte-se-ao-cobrateam-em-mais-um-splinter-sprint/feed/</wfw:commentRss> <slash:comments>2</slash:comments> </item> <item><title>Curso de Django em Vitória, na Giran</title><link>http://www.franciscosouza.com.br/2010/12/03/curso-de-django-em-vitoria-na-giran/</link> <comments>http://www.franciscosouza.com.br/2010/12/03/curso-de-django-em-vitoria-na-giran/#comments</comments> <pubDate>Fri, 03 Dec 2010 16:42:10 +0000</pubDate> <dc:creator>Francisco Souza</dc:creator> <category><![CDATA[desenvolvimento de softwares]]></category> <category><![CDATA[cursos]]></category> <category><![CDATA[django]]></category> <category><![CDATA[giran]]></category> <category><![CDATA[python]]></category><guid isPermaLink="false">http://www.franciscosouza.com.br/?p=1127</guid> <description><![CDATA[Hoje lançamos o novo site da Giran, que construímos utilizando Flask e o Google App Engine. Foi uma grande oportunidade de aprendizado para o time, e um momento de imersão em práticas como TDD e integração contínua para os membros do time que ainda não tinham experiência com tais práticas.]]></description> <content:encoded><![CDATA[<p>Hoje lançamos o <a title="Novo site da Giran" rel="nofollow" href="http://blog.giran.com.br/2010/12/novo-site-da-giran/" target="_blank">novo site da Giran</a>, que construímos utilizando <a title="Flask" rel="nofollow" href="http://flask.pocoo.org" target="_blank">Flask</a> e o <a title="Google App Engine" href="http://appengine.google.com" target="_blank">Google App Engine</a>. Foi uma grande oportunidade de aprendizado para o time, e um momento de imersão em práticas como <a title="Posts sobre TDD" href="http://www.franciscosouza.com.br/tag/tdd">TDD</a> e integração contínua para os membros do time que ainda não tinham experiência com tais práticas.</p><p><img class="aligncenter size-large wp-image-1128" title="Django" src="http://d3r4tlsmw3jkg0.cloudfront.net/2010/12/django-logo-transparent-1024x356.png" alt="Django" width="640" height="222" /></p><p>Junto ao lançamento do novo site, incluímos uma novidade bem bacana: <a title="Giran Fast Tracks" href="http://www.giran.com.br/cursos" target="_blank">Giran Fast Tracks</a>, que são cursos rápidos e imersivos sobre alguma-tecnologia-que-alguém-do-time-escolher. E entre os cursos que começarão a acontecer em Janeiro de 2011, está o primeiro <strong>curso de <a title="Posts sobre Django" href="http://www.franciscosouza.com.br/tag/django">Django</a> no Espírito Santo</strong>, o framework web para perfeccionistas com prazos. Além do curso de Django, temos também os cursos  de <a title="CodeIgniter" rel="nofollow" href="http://codeigniter.com" target="_blank">CodeIgniter</a>, <a title="VRaptor" rel="nofollow" href="http://vraptor.caelum.com.br" target="_blank">VRaptor</a> e <a title="Posts sobre Scrum" href="http://www.franciscosouza.com.br/tag/scrum">Scrum</a>. São cursos com 16 horas de carga horária e uma dinâmica focada no aprendizado prático do uso das ferramentas abordadas :)</p><p>Ficou interessado? Quer saber mais detalhes sobre o <a title="Curso de Django" rel="nofollow" href="http://www.giran.com.br/cursos/django" target="_blank">curso de Django</a>? Entre em <a title="Entre em contato com a Giran" href="http://www.giran.com.br/contato" target="_blank">contato com a Giran</a>!</p> ]]></content:encoded> <wfw:commentRss>http://www.franciscosouza.com.br/2010/12/03/curso-de-django-em-vitoria-na-giran/feed/</wfw:commentRss> <slash:comments>10</slash:comments> </item> <item><title>Livro Python e Django</title><link>http://www.franciscosouza.com.br/2010/11/02/livro-python-e-django/</link> <comments>http://www.franciscosouza.com.br/2010/11/02/livro-python-e-django/#comments</comments> <pubDate>Tue, 02 Nov 2010 21:44:03 +0000</pubDate> <dc:creator>Francisco Souza</dc:creator> <category><![CDATA[desenvolvimento de softwares]]></category> <category><![CDATA[django]]></category> <category><![CDATA[livros]]></category> <category><![CDATA[python]]></category><guid isPermaLink="false">http://www.franciscosouza.com.br/?p=1112</guid> <description><![CDATA[Na última semana recebi o excelente livro Python e Django - Desenvolvimento ágil de aplicações web, escrito por Osvaldo Santana e Thiago Galesi, da Triveos. O livro foi lançado pela editora Novatec, que me enviou um exemplar para avaliação. De forma sucinta, posso dizer que o livro é totalmente sensacional e vale cada centavo que custa. Os autores estão de parabéns pelo material de qualidade sobre Python e Django.]]></description> <content:encoded><![CDATA[<p><a title="Python e Django" href="http://novatec.com.br/livros/pythonedjango/" target="_blank"><img class="alignright size-full wp-image-1113" title="Python e Django" src="http://d3r4tlsmw3jkg0.cloudfront.net/2010/11/capa_ampliada9788575222478.jpg" alt="Python e Django" width="234" height="315" /></a>Na última semana recebi o excelente livro Python e Django &#8211; Desenvolvimento ágil de aplicações web, escrito por <a title="Osvaldo Santana" rel="nofollow" href="http://twitter.com/osantana" target="_blank">Osvaldo Santana</a> e Thiago Galesi, da <a title="Triveos" href="http://www.triveos.com.br/" target="_blank">Triveos</a>. O livro foi lançado pela editora <a title="Novatec" href="http://www.novatec.com.br/" target="_blank">Novatec</a>, que me enviou um exemplar para avaliação. De forma sucinta, posso dizer que o livro é totalmente sensacional e vale cada centavo que custa. Os autores estão de parabéns pelo material de qualidade sobre <a title="Python" href="http://www.python.org" target="_blank">Python</a> e <a title="Django" href="http://www.djangoproject.com" target="_blank">Django</a>.</p><p>A primeira parte do livro apresenta a linguagem Python de forma simples e direta, incluindo também um pequeno manual de referência das funções builtin, uma lista com os módulos da biblioteca padrão e fazendo uma rápida (e ótima) menção ao uso do <a title="virtualenv" rel="nofollow" href="http://virtualenv.openplans.org/" target="_blank">virtualenv</a> para desenvolvimento com ambientes isolados.</p><p>A partir do oitavo capítulo, o livro apresenta o framework Django, contando um pouco da história do mesmo, explicando sua arquitetura, forma de instalação e o seu uso num projeto de agenda de eventos. Diferente da abordagem adotada por <a title="Marinho Brandão" rel="nofollow" href="http://www.marinhobrandao.com" target="_blank">Marinho Brandão</a> no livro <a title="Aprendendo Django no Planeta Terra" rel="nofollow" href="http://www.aprendendodjango.com" target="_blank">Aprendendo Django no Planeta Terra</a>, aqui os autores estão um pouco mais preocupados com os conceitos por trás do framework (até por se tratar de públicos diferentes).</p><p>Alguns pontos que gostaria de destacar:</p><ol><li>Trata-se do melhor material introdutório de Django em português que eu conheço;</li><li>Além de excelente material introdutório, também é um ótimo manual de referência da linguagem Python e do próprio framework Django;</li><li>Achei foda incluírem referência ao desenvolvimento utilizando o virtualenv;</li><li>Os autores foram muito felizes com o detalhamento do Django ORM no capítulo 14 e a demonstração da Django Debug Toolbar no capítulo 16;</li><li>O capítulo que trata sobre Deploy e implantação também foi muito feliz ao abordar deploy com virtualenv, mas acho que outro servidor além do Apache também poderia ser abordado;</li><li>Ponto para o Apêndice A que fala sobre depuração em Python utilizando o Python debugger;</li><li>O Apêndice B fala da instalação do Python no Windows, mas o livro deixa um pouco a desejar quanto ao uso de Django no Windows. Na minha opinião não chega a ser um problema: só por que você pode desenvolver em Python no Windows não significa que você deva =P</li></ol><p>Na <a title="Python e Django" rel="nofollow" href="http://novatec.com.br/livros/pythonedjango/" target="_blank">página do livro no site da Novatec</a> é possível fazer o download da introdução e do sumário do livro. Ficou interessado no livro? Você pode comprá-lo com 20% de desconto utilizando o código promocional <strong>FSOUZA</strong> até Dezembro! :)</p> ]]></content:encoded> <wfw:commentRss>http://www.franciscosouza.com.br/2010/11/02/livro-python-e-django/feed/</wfw:commentRss> <slash:comments>7</slash:comments> </item> <item><title>II Liberdade Interativa: foi show!</title><link>http://www.franciscosouza.com.br/2010/08/20/ii-liberdade-interativa-foi-show/</link> <comments>http://www.franciscosouza.com.br/2010/08/20/ii-liberdade-interativa-foi-show/#comments</comments> <pubDate>Fri, 20 Aug 2010 17:26:43 +0000</pubDate> <dc:creator>Francisco Souza</dc:creator> <category><![CDATA[eventos]]></category> <category><![CDATA[comunidade]]></category> <category><![CDATA[django]]></category> <category><![CDATA[palestras]]></category> <category><![CDATA[python]]></category><guid isPermaLink="false">http://www.franciscosouza.com.br/?p=1096</guid> <description><![CDATA[Ontem aconteceu a segunda edição do Liberdade Interativa, um evento da comunidade Tux-ES. O evento contou com três apresentações: na primeira, a Rapha falou sobre o Bacula, uma ferramenta para backups de servidores em redes. Logo depois da Rapha, fui desafiado apresentei a palestra "Django: o framework web para perfeccionistas com prazo". Fechando o evento, o colega M3nd3s apresentou a palestra "Iptables - Entendendo como fazer um firewall pessoal".]]></description> <content:encoded><![CDATA[<p>Ontem aconteceu a segunda edição do <a title="Liberadade Interativa" rel="nofollow" href="http://www.tux-es.org/liberdadeinterativa/" target="_blank">Liberdade Interativa</a>, um evento da comunidade <a title="Tux ES" rel="nofollow" href="http://www.tux-es.org" target="_blank">Tux-ES</a>. O evento contou com três apresentações: na primeira, a <a title="Rapha" rel="nofollow" href="http://twitter.com/ra_pha" target="_blank">Rapha</a> falou sobre o Bacula, uma ferramenta para backups de servidores em redes. Logo depois da Rapha, <span style="text-decoration: line-through;">fui desafiado</span> apresentei a palestra &#8220;Django: o framework web para perfeccionistas com prazo&#8221;. Fechando o evento, o colega <a title="Almir M3nd3s" rel="nofollow" href="http://twitter.com/m3nd3s" target="_blank">M3nd3s</a> apresentou a palestra &#8220;Iptables &#8211; Entendendo como fazer um firewall pessoal&#8221;.<span id="more-1096"></span></p><p>Excetuando o fato que estourei um pouquinho o tempo na minha apresentação, o momento foi super válido, pois pude contar com a colaboração do público com muitas perguntas e comentários, e acredito que pelo menos algumas pessoas ficaram interessadas em dar uma olhada no Django. Também foi bacana o momento pós-evento onde puder trocar uma ideia com algumas pessoas, inclusive um aluno da UFES que está utilizando Django para uma aplicação de backend em seu trabalho de conclusão de curso.</p><p>Disponibilizei os slides da apresentação no SlideShare:</p><div id="__ss_5015915" style="width: 425px;"><strong style="display: block; margin: 12px 0 4px;"><a title="Django: O Framework web para perfeccionistas com prazos" rel="nofollow" href="http://www.slideshare.net/franciscosouza/django-o-framework-web-para-perfeccionistas-com-prazos" target="_blank">Django: O Framework web para perfeccionistas com prazos</a></strong><object id="__sse5015915" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="425" height="355" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowScriptAccess" value="always" /><param name="src" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=apresentacaodjango-100819211400-phpapp01&amp;stripped_title=django-o-framework-web-para-perfeccionistas-com-prazos" /><param name="name" value="__sse5015915" /><param name="allowfullscreen" value="true" /><embed id="__sse5015915" type="application/x-shockwave-flash" width="425" height="355" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=apresentacaodjango-100819211400-phpapp01&amp;stripped_title=django-o-framework-web-para-perfeccionistas-com-prazos" name="__sse5015915" allowscriptaccess="always" allowfullscreen="true"></embed></object>&nbsp;</p><div style="padding: 5px 0 12px;">View more <a rel="nofollow" href="http://www.slideshare.net/" target="_blank">presentations</a> from <a rel="nofollow" href="http://www.slideshare.net/franciscosouza" target="_blank">Francisco Souza</a>.</div></div><p>Como os slides não fazem muito sentido sem as notas, também disponibilizo os slides com as notas, para quem tiver interesse:</p><div id="__ss_5020424" style="width: 477px;"><strong style="display: block; margin: 12px 0 4px;"><a title="Django: o framework web para perfeccionistas com prazos" rel="nofollow" href="http://www.slideshare.net/franciscosouza/django-o-framework-web-para-perfeccionistas-com-prazos" target="_blank">Django: o framework web para perfeccionistas com prazos</a></strong><object id="__sse5020424" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="477" height="510" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowScriptAccess" value="always" /><param name="src" value="http://static.slidesharecdn.com/swf/doc_player.swf?doc=101pdfsamapresentacaodjango-100820121739-phpapp02&amp;stripped_title=django-o-framework-web-para-perfeccionistas-com-prazos-5020424" /><param name="name" value="__sse5020424" /><param name="allowfullscreen" value="true" /><embed id="__sse5020424" type="application/x-shockwave-flash" width="477" height="510" src="http://static.slidesharecdn.com/swf/doc_player.swf?doc=101pdfsamapresentacaodjango-100820121739-phpapp02&amp;stripped_title=django-o-framework-web-para-perfeccionistas-com-prazos-5020424" name="__sse5020424" allowscriptaccess="always" allowfullscreen="true"></embed></object>&nbsp;</p><div style="padding: 5px 0 12px;">View more <a rel="nofollow" href="http://www.slideshare.net/" target="_blank">documents</a> from <a rel="nofollow" href="http://www.slideshare.net/franciscosouza" target="_blank">Francisco Souza</a>.</div></div><p>Novamente, gostaria muito de feedback, então se você tem alguma crítica, comentário, dúvida ou qualquer coisa, entre em contato comigo, ou faça um comentário aqui :) E no mais, é isso! Nos vemos no III Liberdade Interativa! ;)</p><p></p><p></p> ]]></content:encoded> <wfw:commentRss>http://www.franciscosouza.com.br/2010/08/20/ii-liberdade-interativa-foi-show/feed/</wfw:commentRss> <slash:comments>6</slash:comments> </item> <item><title>Voando com o Django no Google App Engine</title><link>http://www.franciscosouza.com.br/2010/08/02/voando-com-o-django-no-google-app-engine/</link> <comments>http://www.franciscosouza.com.br/2010/08/02/voando-com-o-django-no-google-app-engine/#comments</comments> <pubDate>Mon, 02 Aug 2010 20:00:34 +0000</pubDate> <dc:creator>Francisco Souza</dc:creator> <category><![CDATA[desenvolvimento de softwares]]></category> <category><![CDATA[appengine]]></category> <category><![CDATA[django]]></category> <category><![CDATA[frameworks]]></category> <category><![CDATA[google app engine]]></category> <category><![CDATA[python]]></category><guid isPermaLink="false">http://www.franciscosouza.com.br/?p=1088</guid> <description><![CDATA[O Google App Engine é uma ferramenta sensacional para desenvolvedores web. Uma ferramente certamente útil e que deveria pelo menos ser experimentada por todo desenvolvedor web que se preza :) A primeira linguagem de programação suportada pelo App Engine foi o Python, e hoje a linguagem Java também é suportada. Python é uma linguagem com muitos frameworks web (muitos mesmo), e você pode usar alguns deles no App Engine. Resolvi, então, criar uma série com três posts sobre como usar três frameworks web Python no Google App Engine: Django, Flask e web2py.]]></description> <content:encoded><![CDATA[<p>O <a title="Google App Engine" rel="nofollow" href="http://appengine.google.com" target="_blank">Google App Engine</a> é uma ferramenta sensacional para desenvolvedores web. Uma ferramente certamente útil e que deveria pelo menos ser experimentada por todo desenvolvedor web que se preza :) A primeira linguagem de programação suportada pelo App Engine foi o <a title="Python" rel="nofollow" href="http://www.python.org" target="_blank">Python</a>, e hoje a linguagem Java também é suportada. Python é uma linguagem com muitos frameworks web (muitos mesmo), e você pode usar alguns deles no App Engine. Resolvi, então, criar uma série com três posts sobre como usar três frameworks web Python no Google App Engine: <a title="Django Project" rel="nofollow" href="http://www.djangoproject.com" target="_blank">Django</a>, <a title="Flask" rel="nofollow" href="http://flask.pocoo.org" target="_blank">Flask</a> e <a title="web2py" rel="nofollow" href="http://www.web2py.com" target="_blank">web2py</a>.</p><p>Em todos os casos, desenvolverei uma aplicação simples, que será enviada para o Google App Engine e disponibilizada publicamente :) Nesta primeira parte, abordarei o Django, o mais famoso dentre os frameworks Python para a web.<span id="more-1088"></span></p><p>O principal recurso do Django é a camada de modelo, chamada <em>Django models</em>. Trata-se de uma camada de abstração de dados composta por um poderoso framework de mapeamento objeto-relacional, que suporta diversos sistemas de gerenciamento de banco de dados relacionais. O único problema é que o Google App Engine não usa um banco de dados relacional, o App Engine usa o BigTable, um poderoso banco de dados para armazenamento de dados em larga escala. Assim, a poderosa camada de modelo do Django é simplesmente inútil, pelo menos nativamente falando.</p><p>Não há motivo para pânico, existe um fork do Django, o projeto <a title="django-nonrel project" href="http://www.allbuttonspressed.com/projects/django-nonrel" target="_blank">django-nonrel</a>, que visa trazer o poder do <em>Django models</em> para bancos de dados não-relacionais, como o BigTable. Na aplicação deste post, foi utilizado o subprojeto <a title="djangoappengine project" href="http://www.allbuttonspressed.com/projects/djangoappengine" target="_blank">djangoappengine</a>, que une o Django ao BigTable de forma descomplicada.</p><p>A aplicação de exemplo não será nenhum exemplo de criatividade: um blog extremamente simples, com apenas uma página para listar os posts e outra para cadastrá-los (protegida por login). Vamos utilizar a autenticação nativa do Django, e não a API de autenticação fornecida pelo App Engine.</p><p>O primeiro passo é deixar o ambiente pronto para receber nosso código (e voar em produção =P). De acordo com a <a title="Instalação do djangoappengine" rel="nofollow" href="http://www.allbuttonspressed.com/projects/djangoappengine#installation" target="_blank">documentação oficial do djangoappengine</a>, é necessário baixar quatro arquivos e juntá-los. Comecei pela aplicação, baixei o arquivo <em>django-testapp.zip</em>, extraí e renomeei o diretório de <em>django-testapp</em> para <em>blog_gae</em>. Depois eu baixei as outras bibliotecas, de forma que a estrutura do projeto ficou da seguinte forma:</p><p><img class="aligncenter size-full wp-image-1089" title="Estrutura do projeto" src="http://d3r4tlsmw3jkg0.cloudfront.net/2010/07/screenshot1.png" alt="Estrutura do projeto" width="176" height="213" />O diretório &#8220;django&#8221; foi extraído do pacote <em>django-nonrel.zip</em>, o diretório &#8220;djangoappengine&#8221; do pacote <em>djangoappengine.zip </em>e o &#8220;djangotoolbox&#8221; extraído do pacote <em>djangotoolbox.zip</em>. Junto à estrutura de exemplo da aplicação está presente o arquivo <em>app.yaml</em>, que é o arquivo de configuração da aplicação no Google App Engine. Neste arquivo, é necessário apenas mudar o ID da aplicação (primeira linha). Veja como fica o arquivo após a mudança:</p><div class="codecolorer-container yaml default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="yaml codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: green;">application</span><span style="font-weight: bold; color: brown;">: </span>gaeseries<span style="color: green;"><br /> version</span><span style="font-weight: bold; color: brown;">: </span><span style="">1</span><span style="color: green;"><br /> runtime</span><span style="font-weight: bold; color: brown;">: </span>python<span style="color: green;"><br /> api_version</span><span style="font-weight: bold; color: brown;">: </span><span style="">1</span><br /> <span style="color: green;"><br /> default_expiration</span><span style="font-weight: bold; color: brown;">: </span>'365d'<br /> <span style="color: #007F45;"><br /> handlers</span>:<span style="color: green;"><br /> - url</span><span style="font-weight: bold; color: brown;">: </span>/remote_api<span style="color: green;"><br /> &nbsp; script</span><span style="font-weight: bold; color: brown;">: </span>$PYTHON_LIB/google/appengine/ext/remote_api/handler.py<span style="color: green;"><br /> &nbsp; login</span><span style="font-weight: bold; color: brown;">: </span>admin<br /> <span style="color: green;"><br /> - url</span><span style="font-weight: bold; color: brown;">: </span>/_ah/queue/deferred<span style="color: green;"><br /> &nbsp; script</span><span style="font-weight: bold; color: brown;">: </span>djangoappengine/deferred/handler.py<span style="color: green;"><br /> &nbsp; login</span><span style="font-weight: bold; color: brown;">: </span>admin<br /> <span style="color: green;"><br /> - url</span><span style="font-weight: bold; color: brown;">: </span>/media/admin<span style="color: green;"><br /> &nbsp; static_dir</span><span style="font-weight: bold; color: brown;">: </span>django/contrib/admin/media/<br /> <span style="color: green;"><br /> - url</span><span style="font-weight: bold; color: brown;">: </span>/.*<span style="color: green;"><br /> &nbsp; script</span><span style="font-weight: bold; color: brown;">: </span>djangoappengine/main/main.py</div></div><p>A cada parte da série de posts eu vou fazer o deploy de uma versão diferente, assim, todas as versões ficarão disponíveis online dentro de uma mesma aplicação do Google App Engine. Como esta é a primeira parte, então será a primeira versão =P Outro ponto é o arquivo de configuração do Django (<em>settings.py</em>), neste momento, a única mudança neste arquivo é &#8220;descomentar&#8221; a linha <em>django.contrib.auth</em> na tupla <em>INSTALLED_APPS</em>, para que possamos utilizar a aplicação de autenticação nativa do Django.</p><p>Agora nosso ambiente está devidamente configurado, podemos iniciar uma nova aplicação Django e começar a codar. A forma de iniciar a aplicação é a mesma de sempre (daqui pra frente, tudo se parecerá naturalmente Django): basta rodar o startapp.</p><pre>$ ./manage.py startapp core</pre><p>Criamos a aplicação principal do projeto, chamada <em>core</em>. É criado o diretório da aplicação com os módulos Python &#8220;normais&#8221;: <em>models</em>, <em>views</em> e <em>tests</em>. Vamos então criar o model Post. Note que tudo está em inglês, pois esta aplicação foi desenvolvida em inglês :) Eis o código do módulo <em>models.py</em>, com a classe Post:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">db</span> <span style="color: #ff7700;font-weight:bold;">import</span> models<br /> <span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">contrib</span>.<span style="color: black;">auth</span>.<span style="color: black;">models</span> <span style="color: #ff7700;font-weight:bold;">import</span> User<br /> <br /> <span style="color: #ff7700;font-weight:bold;">class</span> Post<span style="color: black;">&#40;</span>models.<span style="color: black;">Model</span><span style="color: black;">&#41;</span>:<br /> &nbsp; &nbsp; title <span style="color: #66cc66;">=</span> models.<span style="color: black;">CharField</span><span style="color: black;">&#40;</span>max_length <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">200</span><span style="color: black;">&#41;</span><br /> &nbsp; &nbsp; content <span style="color: #66cc66;">=</span> models.<span style="color: black;">TextField</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br /> &nbsp; &nbsp; date <span style="color: #66cc66;">=</span> models.<span style="color: black;">DateTimeField</span><span style="color: black;">&#40;</span>auto_now_add <span style="color: #66cc66;">=</span> <span style="color: #008000;">True</span><span style="color: black;">&#41;</span><br /> &nbsp; &nbsp; <span style="color: #dc143c;">user</span> <span style="color: #66cc66;">=</span> models.<span style="color: black;">ForeignKey</span><span style="color: black;">&#40;</span>User<span style="color: black;">&#41;</span></div></div><p>Agora basta instalar a aplicação <em>core</em> colocando-a dentro da tupla <em>INSTALLED_APPS</em> no arquivo <em>settings.py</em>. Como vamos utilizar a aplicação de autenticação do Django, vamos criar um usuário de administração (super-usuário) para gerenciar toda a bagunça:</p><pre>$ ./manage.py createsuperuser</pre><p>Após informar o nome de usuário, o e-mail e a senha, o usuário estará criado no banco de dados. Agora vamos fazer nossa aplicação estar preparada para fazer login e logout. Primeiro, vamos mapear as URLs de login e logout no arquivos <em>urls.py</em>:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">conf</span>.<span style="color: black;">urls</span>.<span style="color: black;">defaults</span> <span style="color: #ff7700;font-weight:bold;">import</span> *<br /> <br /> urlpatterns <span style="color: #66cc66;">=</span> patterns<span style="color: black;">&#40;</span><span style="color: #483d8b;">''</span><span style="color: #66cc66;">,</span><br /> &nbsp; &nbsp; <span style="color: black;">&#40;</span><span style="color: #483d8b;">'^$'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'django.views.generic.simple.direct_to_template'</span><span style="color: #66cc66;">,</span><br /> &nbsp; &nbsp; <span style="color: black;">&#123;</span><span style="color: #483d8b;">'template'</span>: <span style="color: #483d8b;">'home.html'</span><span style="color: black;">&#125;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br /> &nbsp; &nbsp; <span style="color: black;">&#40;</span><span style="color: #483d8b;">'^login/$'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'django.contrib.auth.views.login'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br /> &nbsp; &nbsp; <span style="color: black;">&#40;</span><span style="color: #483d8b;">'^logout/$'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'django.contrib.auth.views.logout'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br /> <span style="color: black;">&#41;</span></div></div><p>Note as linhas 6 e 7, onde as URLs são mapeadas. Você pode também conferir os códigos dos templates <a title="Template login.html" rel="nofollow" href="http://gist.github.com/502967" target="_blank">registration/login.html</a> e <a title="Template logged_out.html" rel="nofollow" href="http://gist.github.com/502969" target="_blank">registration/logged_out.html</a>. Finalmente, basta adicionar três linhas ao <em>settings.py</em>:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">LOGIN_URL <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'/login/'</span><br /> LOGOUT_URL <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'/logout/'</span><br /> LOGIN_REDIRECT_URL <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'/'</span></div></div><p>Agora as URLs de login e logout já estão prontas para voar, podemos criar nossas views de cadastro e listagem dos posts. Vamos primeiro criar a view de cadastro, que contará com um formulário composto pelos campos <em>title</em> (título) e <em>content</em> (conteúdo). Será um <em>ModelForm</em> atrelado ao modelo <em>Post</em>. Além dos campos title e content, o atributo <em>user</em> do modelo Post será preenchido automaticamente com o usuário logado. Eis o código completo do formulário:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">class</span> PostForm<span style="color: black;">&#40;</span>forms.<span style="color: black;">ModelForm</span><span style="color: black;">&#41;</span>:<br /> &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">class</span> Meta:<br /> &nbsp; &nbsp; &nbsp; &nbsp; model <span style="color: #66cc66;">=</span> Post<br /> &nbsp; &nbsp; &nbsp; &nbsp; exclude <span style="color: #66cc66;">=</span> <span style="color: black;">&#40;</span><span style="color: #483d8b;">'user'</span><span style="color: #66cc66;">,</span><span style="color: black;">&#41;</span><br /> <br /> &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> save<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> <span style="color: #dc143c;">user</span><span style="color: #66cc66;">,</span> commit <span style="color: #66cc66;">=</span> <span style="color: #008000;">True</span><span style="color: black;">&#41;</span>:<br /> &nbsp; &nbsp; &nbsp; &nbsp; post <span style="color: #66cc66;">=</span> <span style="color: #008000;">super</span><span style="color: black;">&#40;</span>PostForm<span style="color: #66cc66;">,</span> <span style="color: #008000;">self</span><span style="color: black;">&#41;</span>.<span style="color: black;">save</span><span style="color: black;">&#40;</span>commit <span style="color: #66cc66;">=</span> <span style="color: #008000;">False</span><span style="color: black;">&#41;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; post.<span style="color: #dc143c;">user</span> <span style="color: #66cc66;">=</span> <span style="color: #dc143c;">user</span><br /> <br /> &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> commit:<br /> &nbsp; &nbsp; &nbsp; &nbsp; post.<span style="color: black;">save</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br /> <br /> &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> post</div></div><p>Agora basta criar a <em>view</em> para cadastro de um post. Vamos criar também a <em>view</em> de listagem com um simples redirecionamento para que as coisas funcionem bem aqui. Eis o código do módulo <em>views.py</em>:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">contrib</span>.<span style="color: black;">auth</span>.<span style="color: black;">decorators</span> <span style="color: #ff7700;font-weight:bold;">import</span> login_required<br /> <span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">shortcuts</span> <span style="color: #ff7700;font-weight:bold;">import</span> render_to_response<br /> <span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">template</span> <span style="color: #ff7700;font-weight:bold;">import</span> RequestContext<br /> <span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">http</span> <span style="color: #ff7700;font-weight:bold;">import</span> HttpResponseRedirect<br /> <span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">core</span>.<span style="color: black;">urlresolvers</span> <span style="color: #ff7700;font-weight:bold;">import</span> reverse<br /> <span style="color: #ff7700;font-weight:bold;">from</span> forms <span style="color: #ff7700;font-weight:bold;">import</span> PostForm<br /> <br /> <span style="color: #66cc66;">@</span>login_required<br /> <span style="color: #ff7700;font-weight:bold;">def</span> new_post<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>:<br /> &nbsp; &nbsp; form <span style="color: #66cc66;">=</span> PostForm<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br /> &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> request.<span style="color: black;">method</span> <span style="color: #66cc66;">==</span> <span style="color: #483d8b;">'POST'</span>:<br /> &nbsp; &nbsp; &nbsp; &nbsp; form <span style="color: #66cc66;">=</span> PostForm<span style="color: black;">&#40;</span>request.<span style="color: black;">POST</span><span style="color: black;">&#41;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> form.<span style="color: black;">is_valid</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; form.<span style="color: black;">save</span><span style="color: black;">&#40;</span>request.<span style="color: #dc143c;">user</span><span style="color: black;">&#41;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> HttpResponseRedirect<span style="color: black;">&#40;</span>reverse<span style="color: black;">&#40;</span><span style="color: #483d8b;">'core.views.list_posts'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br /> &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> render_to_response<span style="color: black;">&#40;</span><span style="color: #483d8b;">'new_post.html'</span><span style="color: #66cc66;">,</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">locals</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> context_instance<span style="color: #66cc66;">=</span>RequestContext<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span style="color: black;">&#41;</span><br /> <br /> <span style="color: #ff7700;font-weight:bold;">def</span> list_posts<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>:<br /> &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> HttpResponseRedirect<span style="color: black;">&#40;</span><span style="color: #483d8b;">'/'</span><span style="color: black;">&#41;</span></div></div><p>Agora, para que a <em>view</em> <em>new_post</em> funcione, precisamos apenas mapear as duas <em>views</em> acima para suas respectivas URLs, e criar o template <em>new_post.html</em>. Eis o mapeamento de URLs:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: black;">&#40;</span><span style="color: #483d8b;">'^posts/new/$'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'core.views.new_post'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br /> <span style="color: black;">&#40;</span><span style="color: #483d8b;">'^posts/$'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'core.views.list_posts'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span></div></div><p>Confira também o template <a title="Template new_post.html" rel="nofollow" href="http://gist.github.com/502976" target="_blank">new_post.html</a>.</p><p>Finalmente já podemos ver alguma coisa através do navegador. Rodando o comando <em>./manage.py runserver</em> no terminal temos o servidor inicializado, então é possível acessar a URL <em>http://localhost:8000/posts/new</em> no navegador para ver um formulário de cadastro de posts, onde os posts podem ser escritos e salvos (lembre-se apenas que é necessário efetuar login para acessar esta <em>view</em>). Agora, precisamos apenas listar todos os posts na URL <em>/posts/</em>. A <em>view</em> <em>list_posts</em> já está mapeada para a URL <em>/posts/</em>, então a única coisa a fazer é escrever o código da <em>view</em>, que lista todos os posts do banco de dados e envia para um template:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">def</span> list_posts<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>:<br /> &nbsp; &nbsp; posts <span style="color: #66cc66;">=</span> Post.<span style="color: black;">objects</span>.<span style="color: #008000;">all</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br /> &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> render_to_response<span style="color: black;">&#40;</span><span style="color: #483d8b;">'list_posts.html'</span><span style="color: #66cc66;">,</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">locals</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> context_instance<span style="color: #66cc66;">=</span>RequestContext<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span style="color: black;">&#41;</span></div></div><p>Confira o template <a title="Template list_posts.html" rel="nofollow" href="http://gist.github.com/502981" target="_blank">list_posts.html</a>. Finalmente nossa aplicação está pronta para o deploy, mas como fazemos isso? De uma maneira muito difícil:</p><pre>$ ./manage.py deploy</pre><p>Pronto, aplicação em produção :) Para poder ter acesso como usuário na aplicação, basta apenas rodar o comando para criar o usuário remotamente:</p><pre>$ ./manage.py remote createsuperuser</pre><p>Você pode ver esta aplicação voando neste link: <a title="Veja a aplicação voando no Google App Engine" rel="nofollow" href="http://1.latest.gaeseries.appspot.com/" target="_blank">http://1.latest.gaeseries.appspot.com/</a> (para logar-se no sistema, utilize o nome de usuário <strong>demo</strong> com a senha <strong>demo</strong>)</p><p>E também ver o código fonte completo neste link: <a title="Código fonte da aplicação" rel="nofollow" href="http://github.com/fsouza/gaeseries/tree/django" target="_blank">http://github.com/fsouza/gaeseries/tree/django</a></p><p>Nos vemos na próxima parte ;)</p> ]]></content:encoded> <wfw:commentRss>http://www.franciscosouza.com.br/2010/08/02/voando-com-o-django-no-google-app-engine/feed/</wfw:commentRss> <slash:comments>7</slash:comments> </item> <item><title>II Liberdade Interativa</title><link>http://www.franciscosouza.com.br/2010/07/29/ii-liberdade-interativa/</link> <comments>http://www.franciscosouza.com.br/2010/07/29/ii-liberdade-interativa/#comments</comments> <pubDate>Fri, 30 Jul 2010 00:59:24 +0000</pubDate> <dc:creator>Francisco Souza</dc:creator> <category><![CDATA[eventos]]></category> <category><![CDATA[django]]></category> <category><![CDATA[open source]]></category> <category><![CDATA[palestras]]></category> <category><![CDATA[python]]></category><guid isPermaLink="false">http://www.franciscosouza.com.br/?p=1081</guid> <description><![CDATA[II Liberdade Interativa, em Vitória.]]></description> <content:encoded><![CDATA[<p><a title="II Liberdade Interativa" rel="nofollow" href="http://www.tux-es.org/liberdadeinterativa/" target="_blank"><img class="alignleft size-full wp-image-1087" title="II Liberdade Interativa" src="http://img.franciscosouza.com.br/2010/07/scale_li.png" alt="II Liberdade Interativa" width="664" height="939" /></a></p> ]]></content:encoded> <wfw:commentRss>http://www.franciscosouza.com.br/2010/07/29/ii-liberdade-interativa/feed/</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>Atividades em Agosto</title><link>http://www.franciscosouza.com.br/2010/07/19/atividades-em-agosto/</link> <comments>http://www.franciscosouza.com.br/2010/07/19/atividades-em-agosto/#comments</comments> <pubDate>Mon, 19 Jul 2010 15:13:01 +0000</pubDate> <dc:creator>Francisco Souza</dc:creator> <category><![CDATA[desenvolvimento de softwares]]></category> <category><![CDATA[agile]]></category> <category><![CDATA[comunidade]]></category> <category><![CDATA[django]]></category> <category><![CDATA[eventos]]></category> <category><![CDATA[palestras]]></category> <category><![CDATA[php]]></category> <category><![CDATA[python]]></category> <category><![CDATA[scrum]]></category><guid isPermaLink="false">http://www.franciscosouza.com.br/?p=1074</guid> <description><![CDATA[Agosto se aproxima e com ele alguns eventos bacanas de informática no Espírito Santo. No dia 19 de agosto, acontecerá o II Liberdade Interativa, na Faesa da Av. Vitória. Na ocasião, apresentarei a palestra "Django: o framework web para perfeccionistas com prazos".]]></description> <content:encoded><![CDATA[<p><img class="alignright" title="Django" src="http://img.franciscosouza.com.br/2010/07/Django1.png" alt="Django" width="208" height="94" />Agosto se aproxima e com ele alguns eventos bacanas de informática no Espírito Santo. No dia 19 de agosto, acontecerá o <a title="Liberdade Interativa" rel="nofollow" href="http://www.tux-es.org/liberdadeinterativa/" target="_blank">II Liberdade Interativa</a>, na <a title="Faesa" rel="nofollow" href="http://site.faesa.br/" target="_blank">Faesa da Av. Vitória</a>. Na ocasião, apresentarei a palestra &#8220;Django: o framework web para perfeccionistas com prazos&#8221;.</p><p>A programação completa do II Liberdade Interativa é a seguinte:</p><ul><li><strong>19h00:</strong> Bacula &#8211; O backup da meia-noite (<a title="Rapha" rel="nofollow" href="http://www.rapha.net.br" target="_blank">Raphaela M. Rocha</a>);</li><li><strong>19h50:</strong> Django: O framework web para perfeccionistas com prazos (<a title="Francisco Souza" rel="nofollow" href="http://twitter.com/franciscosouza" target="_blank">Francisco Souza</a>);</li><li><strong>20h40:</strong> Iptables: Entendendo como fazer um firewall pessoal (<a title="Almir M3nd3s" rel="nofollow" href="http://www.almirmendes.net" target="_blank">Almir M3nd3s</a>)</li></ul><p>O evento acontecerá no mini-auditório da Faesa da Av. Vitória, no dia 19 de agosto de 2010 e começará às 19 horas. A inscrição será feita &#8220;na hora&#8221;, então não é necessário fazer inscrição previamente, basta aparecer :)</p><p><img class="alignleft size-full wp-image-1078" title="codeigniter" src="http://img.franciscosouza.com.br/2010/07/codeigniter.png" alt="CodeIgniter" width="77" height="106" />Já no dia 28 de agosto, acontecerá o <a title="II Workshop de PHP do Espírito Santo" rel="nofollow" href="http://www.phpes.org/ii-workshop-php-es/" target="_blank">II Workshop de PHP do Espírito Santo</a> na <a title="UVV" rel="nofollow" href="http://www.uvv.br" target="_blank">UVV</a>, em Vila Velha. Neste evento eu apresentarei junto ao colega de trabalho <a title="André Tagliati" rel="nofollow" href="http://www.tagliati.com.br" target="_blank">André Tagliati</a> a palestra &#8220;CodeIgniter: turbinando a produtividade com MVC&#8221; e junto ao time da Giran um hands-on de Scrum entitulado &#8220;Aprendendo a começar um projeto com Scrum&#8221;.</p><p>A programação completa do II Workshop de PHP do Espírito Santo é a seguinte:</p><ul><li><strong>09h10:</strong> PHPZeiro: Adote um framework! (<a title="Leo Hackin" rel="nofollow" href="http://www.leohackin.com.br" target="_blank">Leo &#8220;Hackin&#8221;</a>)</li><li><strong>10h10:</strong> CodeIgniter: Turbinando a produtividade com MVC (<a title="Francisco Souza" href="http://www.franciscosouza.net" target="_blank">Francisco Souza</a> e <a title="André Tagliati" rel="nofollow" href="http://twitter.com/tagliati" target="_blank">André Tagliati</a>)</li><li><strong>11h10:</strong> Técnicas simples e eficazes para tirar o máximo do seu servidor MySQL (<a title="Marcelo Raposo" rel="nofollow" href="http://twitter.com/mmqraposo" target="_blank">Marcelo Raposo</a>)</li><li><strong>14h00:</strong> PHP Data Object &#8211; Interface Única de Comunicação com SGBDs (<a title="Almir M3nd3s" rel="nofollow" href="http://twitter.com/m3nd3s" target="_blank">Almir M3nd3s</a>)</li><li><strong>15h10:</strong> Moodle: fazendo EAD de qualidade com PHP (<a title="Lucas Coradini" rel="nofollow" href="http://www.lucascoradini.com" target="_blank">Lucas Coradini</a>)</li><li><strong>16h10:</strong> Aprendendo a começar um projeto com Scrum (<a title="Time da Giran" rel="nofollow" href="http://www.giran.com.br/empresa/time" target="_blank">Time da Giran</a>)</li></ul><p>Neste caso, a inscrição é gratuita e obrigatória, e deve ser feita <a title="Formulário de inscrição no II Workshop de PHP do ES" rel="nofollow" href="http://spreadsheets.google.com/viewform?hl=en&amp;formkey=dHJibnkyZEs2VzhFNmhHYTEyYXRwV0E6MQ#gid=0" target="_blank">neste formulário</a>. O evento acontecerá no dia 28 de agosto, a partir da 8:30 na UVV, campus Boa Vista.</p><p>Será um mês de boas atividades, e eu espero ver muita gente em todas elas =P</p> ]]></content:encoded> <wfw:commentRss>http://www.franciscosouza.com.br/2010/07/19/atividades-em-agosto/feed/</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>BDD em Django: Desenvolvimento web mais divertido com qualidade usando Freshen</title><link>http://www.franciscosouza.com.br/2010/06/24/bdd-em-django-desenvolvimento-web-mais-divertido-com-qualidade-usando-freshen/</link> <comments>http://www.franciscosouza.com.br/2010/06/24/bdd-em-django-desenvolvimento-web-mais-divertido-com-qualidade-usando-freshen/#comments</comments> <pubDate>Thu, 24 Jun 2010 12:23:49 +0000</pubDate> <dc:creator>Francisco Souza</dc:creator> <category><![CDATA[desenvolvimento de softwares]]></category> <category><![CDATA[agile]]></category> <category><![CDATA[BDD]]></category> <category><![CDATA[django]]></category> <category><![CDATA[frameworks]]></category> <category><![CDATA[freshen]]></category> <category><![CDATA[python]]></category> <category><![CDATA[TDD]]></category><guid isPermaLink="false">http://www.franciscosouza.com.br/?p=955</guid> <description><![CDATA[Freshen é um framework Python para construção de testes de aceitação, baseado no Cucumber e tem o mesmo objetivo do Cucumber: fazer o desenvolvimento de softwares com BDD mais divertido. Podemos aplicar os conceitos do BDD escrevendo testes de aceitação em alto nível e graças à integração com o Nose, podemos ainda usar testes unitários para testar unidades de código.]]></description> <content:encoded><![CDATA[<p>Freshen é um framework Python para construção de testes de aceitação, baseado no <a title="Cucumber" rel="nofollow" href="http://cukes.info" target="_blank">Cucumber</a> e tem o mesmo objetivo do Cucumber: fazer o desenvolvimento de softwares com BDD mais divertido. Podemos aplicar os conceitos do BDD escrevendo testes de aceitação em alto nível e graças à integração com o Nose, podemos ainda usar testes unitários para testar unidades de código.<span id="more-955"></span></p><p>Este post tem como objetivo fazer uma apresentação básica do uso do Django com o Freshen. Antes de começar, vamos preparar as ferramentas necessárias :) Como o Freshen é um plugin do Nose, para utilizar o Freshen, precisamos do Nose. Já que vamos integrar o Django também no esquema, precisamos também adicionar um plugin para testar o Django, NoseDjango, e também o próprio Django. Por fim, para isolar os tests do banco de dados, um framework de Mock é uma boa pedida. Vamos utilizar o Ludibrio, que foi feito por brasileiros, é simples e bom o suficiente. Se você tem o PIP instalado, um simples comando instala tudo que nós precisamos:</p><div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span><span style="color: #c20cb9; font-weight: bold;">sudo</span> pip <span style="color: #c20cb9; font-weight: bold;">install</span> django nose nosedjango freshen ludibrio</div></div><p>Agora que temos todas as ferramentas necessárias, podemos começar a trabalhar no nosso &#8220;sisteminha&#8221;. Nosso exemplo vai ser um delivery de medicamentos para a farmácia do tio-avô da ex-namorada do meu primo de segundo grau. Vamos implementar apenas uma funcionalidade, que será descrita em um arquivo de funcionalidade.</p><p>Primeiramente, vamos criar o projeto do Django para a farmácia:</p><div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span>django-admin.py startproject farmacia</div></div><p>Criado o projeto, vamos iniciar a aplicação <em>delivery</em>, que vai ser o que vamos testar:</p><div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span>django-admin.py startapp delivery</div></div><p>Não há muita coisa a ser configurada no projeto, vamos apenas configurar o banco de dados, definir o diretório dos templates (no projeto) e adicionar nossa aplicação ao <em>INSTALLED_APPS</em>:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">DATABASES <span style="color: #66cc66;">=</span> <span style="color: black;">&#123;</span><br /> <span style="color: #483d8b;">'default'</span>: <span style="color: black;">&#123;</span><br /> <span style="color: #483d8b;">'ENGINE'</span>: <span style="color: #483d8b;">'django.db.backends.sqlite3'</span><span style="color: #66cc66;">,</span><br /> <span style="color: #483d8b;">'NAME'</span>: <span style="color: #483d8b;">'dados.db'</span><span style="color: #66cc66;">,</span><br /> <span style="color: #483d8b;">'USER'</span>: <span style="color: #483d8b;">''</span><span style="color: #66cc66;">,</span><br /> <span style="color: #483d8b;">'PASSWORD'</span>: <span style="color: #483d8b;">''</span><span style="color: #66cc66;">,</span><br /> <span style="color: #483d8b;">'HOST'</span>: <span style="color: #483d8b;">''</span><span style="color: #66cc66;">,</span><br /> <span style="color: #483d8b;">'PORT'</span>: <span style="color: #483d8b;">''</span><span style="color: #66cc66;">,</span><br /> <span style="color: black;">&#125;</span><br /> <span style="color: black;">&#125;</span><br /> <br /> <span style="color: #808080; font-style: italic;">#Outras configurações aqui...</span><br /> <br /> <span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span><br /> ROOT <span style="color: #66cc66;">=</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">dirname</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">abspath</span><span style="color: black;">&#40;</span>__file__<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br /> TEMPLATE_DIRS <span style="color: #66cc66;">=</span> <span style="color: black;">&#40;</span><br /> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span>ROOT<span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'templates'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br /> <span style="color: black;">&#41;</span><br /> <br /> INSTALLED_APPS <span style="color: #66cc66;">=</span> <span style="color: black;">&#40;</span><br /> <span style="color: #483d8b;">'django.contrib.auth'</span><span style="color: #66cc66;">,</span><br /> <span style="color: #483d8b;">'django.contrib.contenttypes'</span><span style="color: #66cc66;">,</span><br /> <span style="color: #483d8b;">'django.contrib.sessions'</span><span style="color: #66cc66;">,</span><br /> <span style="color: #483d8b;">'django.contrib.sites'</span><span style="color: #66cc66;">,</span><br /> <span style="color: #483d8b;">'django.contrib.messages'</span><span style="color: #66cc66;">,</span><br /> <span style="color: #483d8b;">'delivery'</span><span style="color: #66cc66;">,</span><br /> <span style="color: #808080; font-style: italic;"># Uncomment the next line to enable the admin:</span><br /> <span style="color: #808080; font-style: italic;"># 'django.contrib.admin',</span><br /> <span style="color: black;">&#41;</span></div></div><p>Agora que temos nosso projeto iniciado e configurado, com nossa aplicação iniciada e instalada, vamos começar a desenvolver, mas primeiro vamos escrever as funcionalidades do Freshen. As funcionalidades do Freshen são compostas por cenários que por sua vez são compostos por passos. As funcionalidades de um sistema são definidas dentro de arquivos de funcionalidades. Vamos criar a funcionalidade Listar remédios, que será a listagem de remédios para serem selecionados para compra e entrega.</p><p>Vamos criar dentro da aplicação <em>delivery</em> um diretório chamado <em>features</em> e dentro deste diretório criaremos o arquivo listar_remedios.feature, com o seguinte conteúdo:</p><div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Funcionalidade: Listar remédios<br /> Para poder comprar um remédio<br /> Como um usuário do sistema<br /> Eu gostaria de ver uma lista contendo os remédios para eu escolher<br /> <br /> Cenário: Listando todos os remédios<br /> Dado que existem 10 remédios cadastrados no banco de dados<br /> Quando eu vou para a página de listagem de remédios<br /> Então eu deveria ver a listagem com o nome dos 10 remédios</div></div><p>O &#8220;código&#8221; da funcionalidade é totalmente simples: a funcionalidade tem um nome, no nosso caso &#8220;Listar remédios&#8221;, um cabeçalho, que é uma descrição livre da funcionalidade (você realmente pode escrever o que quiser no cabeçalho). Para cada história, definimos cenários, utilizando a palavra chave &#8220;Cenário:&#8221;. O cenário é composto por um nome e alguns passos, definidos pelas palavras-chave Given, When, Then, And e But (em português: Dado, Quando, Então, E e Mas). Para entender mais a organização do BDD e a definição de funcionalidades, cenários e passos, dê uma olhada em algum <a title="Behaviour-Driven Development" rel="nofollow" href="http://pt.wikipedia.org/wiki/Behavior_Driven_Development" target="_blank">material sobre BDD</a> (vastidão de material apenas em inglês =P).</p><p>Após criar a funcionalidade e o cenário, temos que definir os passos do nosso cenário. O cenário <em>&#8220;Listando todos os remédios&#8221;</em> possui três passos: <em>&#8220;Dado que existem 10 remédios cadastrados no banco de dados&#8221;</em>, <em>&#8220;Quando eu vou para a página de listagem de remédios&#8221;</em> e <em>&#8220;Então eu deveria ver a listagem com o nome dos 10 remédios&#8221;</em>. Precisamos definir o que o Freshen fará em cada um desses passos para garantir o funcionamento da funcionalidade especificada.</p><p>Vamos rodar o Freshen, para ver a saída neste momento. Quem sabe nosso teste passa sem fazermos nada =D</p><div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ nosetests <span style="color: #660033;">--with-freshen</span> <span style="color: #660033;">--with-django</span> <span style="color: #660033;">--language</span>=pt <span style="color: #660033;">-v</span><br /> Listar remédios: Listando todos os remédios ... UNDEFINED: <span style="color: #ff0000;">&quot;que existem 10 remédios cadastrados no banco de dados&quot;</span> <span style="color: #666666; font-style: italic;"># delivery/features/listar_remedios.feature:7</span><br /> Tests that <span style="color: #000000;">1</span> + <span style="color: #000000;">1</span> always equals <span style="color: #000000;">2</span>. ... ok<br /> <span style="color: #660033;">----------------------------------------------------------------------</span><br /> Ran <span style="color: #000000;">2</span> tests <span style="color: #000000; font-weight: bold;">in</span> 0.175s<br /> OK <span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #007800;">UNDEFINED</span>=<span style="color: #000000;">1</span><span style="color: #7a0874; font-weight: bold;">&#41;</span><br /> Destroying <span style="color: #7a0874; font-weight: bold;">test</span> database <span style="color: #ff0000;">'default'</span>...</div></div><p>Vamos entender o comando executado: executamos o comando <strong>nosetests</strong>, que é o comando do Nose. Como o Freshen é um plugin do Nose, precisamos habilitá-lo utilizando a opção <strong>&#8211;with-freshen</strong>, e habilitamos o Django com a opção <strong>&#8211;with-django</strong>. Uma vez que escrevemos nossa funcionalidade em Português, temos que avisar ao Freshen que ele deve lê-la em português, por isso utilizamos a opção <strong>&#8211;language=pt</strong>. Por fim, utilizamos a opção <strong>-v</strong> para habilitar o modo verbose.</p><p>Agora, vamos entender a saída do comando: veja na primeira linha, que o Freshen encontrou funcionalidade &#8220;Listar remédios&#8221;, e dentro desta funcionalidade encontrou o cenário &#8220;Listando todos os remédios&#8221; com o passo &#8220;que existem 10 remédios cadastrados no banco de dados&#8221; indefinido. Então, vamos definir tal passo e executar o teste novamente. Os passos são definidos em um módulo chamado steps.py presente no mesmo diretório do arquivo da funcionalidade (features, dentro da aplicação).</p><p>Para definir os passos, utilizamos decorators correspondentes a estes passos: para o passo Given/Dado, utilizamos o decorator @Given; para o passo When/Quando, utilizanmos o decorator @When; e para o passo Then/Então, utilizamos o decorator @Then. Vamos definir nosso primeiro passo, utilizando o decorator @Given, e ver o que acontece ao executar o Freshen novamente após tal definição:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #66cc66;">@</span>Given<span style="color: black;">&#40;</span><span style="color: #483d8b;">'que existem (<span style="color: #000099; font-weight: bold;">\\</span>d+) remédios cadastrados no banco de dados'</span><span style="color: black;">&#41;</span><br /> <span style="color: #ff7700;font-weight:bold;">def</span> gravar_remedios<span style="color: black;">&#40;</span>quantidade_remedios<span style="color: black;">&#41;</span>:<br /> scc.<span style="color: black;">remedios</span> <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span><br /> <span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">&#40;</span><span style="color: #008000;">int</span><span style="color: black;">&#40;</span>quantidade_remedios<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>:<br /> remedio <span style="color: #66cc66;">=</span> Remedio<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br /> remedio.<span style="color: black;">nome</span> <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'Remédio %d'</span> %<span style="color: black;">&#40;</span>i + <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span><br /> scc.<span style="color: black;">remedios</span>.<span style="color: black;">append</span><span style="color: black;">&#40;</span>remedio<span style="color: black;">&#41;</span></div></div><p>Note que nós recebemos um parâmetro para nossa função a partir do texto que define o passo. O formato deste parâmetro é definido por uma expressão regular. Atenção: o parâmetro passado para a função sempre é uma string, se você precisar trabalhar este parâmetro como um inteiro, ele deverá ser convertido (como aconteceu no nosso caso).</p><p>Na função acima, nós criamos uma lista chamada <em>remedios</em> dentro de um objeto chamado <em>scc</em>. Trata-se do contexto de armazenamento de objetos do Freshen. O Freshen da suporte a três tipos de contexto: <em>glc</em>, o contexto global, que nunca é apagado; <em>ftc</em>, o contexto de funcionalidade, que é apagado no início da execução de cada funcionalidade; e o <em>scc</em>, que é o contexto de cenário, apagado no início da execução de cada cenário. Quando armazenamos nossa lista de remédios dentro do contexto de cenário, significa que esta lista estará disponível em todos os passos deste cenário.</p><p>Agora, vamos executar novamente o Freshen para ver o que acontece:</p><div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ nosetests <span style="color: #660033;">--with-freshen</span> <span style="color: #660033;">--with-django</span> <span style="color: #660033;">--language</span>=pt <span style="color: #660033;">-v</span><br /> Listar remédios: Listando todos os remédios ... ERROR<br /> Tests that <span style="color: #000000;">1</span> + <span style="color: #000000;">1</span> always equals <span style="color: #000000;">2</span>. ... ok<br /> ======================================================================<br /> ERROR: Listar remédios: Listando todos os remédios<br /> <span style="color: #660033;">----------------------------------------------------------------------</span><br /> Traceback <span style="color: #7a0874; font-weight: bold;">&#40;</span>most recent call <span style="color: #c20cb9; font-weight: bold;">last</span><span style="color: #7a0874; font-weight: bold;">&#41;</span>:<br /> File <span style="color: #ff0000;">&quot;/home/francisco/Projetos/blog/farmacia/delivery/features/steps.py&quot;</span>, line <span style="color: #000000;">9</span>, <span style="color: #000000; font-weight: bold;">in</span> gravar_remedios<br /> remedio = Remedio<span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #7a0874; font-weight: bold;">&#41;</span><br /> NameError: global name <span style="color: #ff0000;">'Remedio'</span> is not defined<br /> <span style="color: #000000; font-weight: bold;">&amp;</span>gt;<span style="color: #000000; font-weight: bold;">&amp;</span>gt; <span style="color: #000000; font-weight: bold;">in</span> <span style="color: #ff0000;">&quot;que existem 10 remédios cadastrados no banco de dados&quot;</span> <span style="color: #666666; font-style: italic;"># delivery/features/listar_remedios.feature:7</span><br /> <span style="color: #660033;">----------------------------------------------------------------------</span><br /> Ran <span style="color: #000000;">2</span> tests <span style="color: #000000; font-weight: bold;">in</span> 0.162s<br /> FAILED <span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #007800;">errors</span>=<span style="color: #000000;">1</span><span style="color: #7a0874; font-weight: bold;">&#41;</span><br /> Destroying <span style="color: #7a0874; font-weight: bold;">test</span> database <span style="color: #ff0000;">'default'</span>...</div></div><p>E o feedback obtido foi: não encontrei esse classe Remedio. Isto ocorre por que nós não definimos nosso model Remedio. Então, vamos definí-lo, com apenas um nome, dentro do módulo <em>models.py</em> da aplicação <em>delivery</em>, e executar novamente o teste:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">db</span> <span style="color: #ff7700;font-weight:bold;">import</span> models<br /> <span style="color: #ff7700;font-weight:bold;">class</span> Remedio<span style="color: black;">&#40;</span>models.<span style="color: black;">Model</span><span style="color: black;">&#41;</span>:<br /> nome <span style="color: #66cc66;">=</span> models.<span style="color: black;">CharField</span><span style="color: black;">&#40;</span>max_length<span style="color: #66cc66;">=</span><span style="color: #ff4500;">255</span><span style="color: black;">&#41;</span></div></div><p>Agora, vamos novamente rodar nossos testes e ver o que acontece:</p><div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ nosetests <span style="color: #660033;">--with-freshen</span> <span style="color: #660033;">--with-django</span> <span style="color: #660033;">--language</span>=pt <span style="color: #660033;">-v</span><br /> Listar remédios: Listando todos os remédios ... UNDEFINED: <span style="color: #ff0000;">&quot;eu vou para a página de listagem de remédios&quot;</span> <span style="color: #666666; font-style: italic;"># delivery/features/listar_remedios.feature:8</span><br /> Tests that <span style="color: #000000;">1</span> + <span style="color: #000000;">1</span> always equals <span style="color: #000000;">2</span>. ... ok<br /> <br /> <span style="color: #660033;">----------------------------------------------------------------------</span><br /> Ran <span style="color: #000000;">2</span> tests <span style="color: #000000; font-weight: bold;">in</span> 0.198s<br /> <br /> OK <span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #007800;">UNDEFINED</span>=<span style="color: #000000;">1</span><span style="color: #7a0874; font-weight: bold;">&#41;</span><br /> Destroying <span style="color: #7a0874; font-weight: bold;">test</span> database <span style="color: #ff0000;">'default'</span>...</div></div><p>Nossa definição de passo agora passou, porém recebemos o feedback de outro passo indefinido: &#8220;eu vou para a página de listagem de remédios&#8221;. Vamos então definir este passo, utilizando o decorator @When:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #66cc66;">@</span>When<span style="color: black;">&#40;</span><span style="color: #483d8b;">'eu vou para a página de listagem de remédios'</span><span style="color: black;">&#41;</span><br /> <span style="color: #ff7700;font-weight:bold;">def</span> visitar_pagina_listagem<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br /> <span style="color: #ff7700;font-weight:bold;">with</span> Mock<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">as</span> delivery:<br /> <span style="color: #ff7700;font-weight:bold;">from</span> delivery.<span style="color: black;">models</span> <span style="color: #ff7700;font-weight:bold;">import</span> Remedio<br /> Remedio.<span style="color: black;">objects</span>.<span style="color: #008000;">all</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> &amp;gt<span style="color: #66cc66;">;</span>&amp;gt<span style="color: #66cc66;">;</span> scc.<span style="color: black;">remedios</span><br /> client <span style="color: #66cc66;">=</span> Client<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br /> scc.<span style="color: black;">response</span> <span style="color: #66cc66;">=</span> client.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/delivery/remedios'</span><span style="color: black;">&#41;</span><br /> delivery.<span style="color: black;">validate</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></div><p>Vamos entender o que foi feito aqui: o método começa fazendo um mock da aplicação do Django, tudo isso para garantir o mock interno do model. Assim, separamos nossa definição do banco de dados: quando o método Remedio.objects.all() for chamado, o objeto de mock, gerenciado pelo Ludibrio, retornará a nossa lista de remédios armazenadas no contexto do cenário. Pulando um pouco, na última linha da função validamos o nosso mock, ou seja, verificamos se o comportamento que esperávamos dele realmente aconteceu. Traduzindo: esta última linha garante que o método realmente foi invocado, mas onde?</p><p>Na linha superior, utilizamos o <a title="Making requests in Django testing" rel="nofollow" href="http://docs.djangoproject.com/en/dev/topics/testing/#making-requests" target="_blank">Client do Django</a> para fazer uma requisição do tipo GET na URL &#8216;/delivery/models&#8217; do nosso projeto. A resposta desta requisição é armazenada dentro do nosso contexto de cenário, em um objeto chamado <em>response</em>. Vamos novamente executar o Freshen e ver o que acontece:</p><div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ nosetests <span style="color: #660033;">--with-freshen</span> <span style="color: #660033;">--with-django</span> <span style="color: #660033;">--language</span>=pt <span style="color: #660033;">-v</span><br /> Listar remédios: Listando todos os remédios ... ERROR<br /> Tests that <span style="color: #000000;">1</span> + <span style="color: #000000;">1</span> always equals <span style="color: #000000;">2</span>. ... ok<br /> <br /> ======================================================================<br /> ERROR: Listar remédios: Listando todos os remédios<br /> <span style="color: #660033;">----------------------------------------------------------------------</span><br /> Traceback <span style="color: #7a0874; font-weight: bold;">&#40;</span>most recent call <span style="color: #c20cb9; font-weight: bold;">last</span><span style="color: #7a0874; font-weight: bold;">&#41;</span>:<br /> File <span style="color: #ff0000;">&quot;/home/francisco/Projetos/blog/farmacia/delivery/features/steps.py&quot;</span>, line <span style="color: #000000;">22</span>, <span style="color: #000000; font-weight: bold;">in</span> visitar_pagina_listagem<br /> scc.response = client.get<span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #ff0000;">'/delivery/remedios'</span><span style="color: #7a0874; font-weight: bold;">&#41;</span><br /> File <span style="color: #ff0000;">&quot;/usr/local/lib/python2.6/dist-packages/django/test/client.py&quot;</span>, line <span style="color: #000000;">290</span>, <span style="color: #000000; font-weight: bold;">in</span> get<br /> response = self.request<span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #000000; font-weight: bold;">**</span>r<span style="color: #7a0874; font-weight: bold;">&#41;</span><br /> File <span style="color: #ff0000;">&quot;/usr/local/lib/python2.6/dist-packages/django/core/handlers/base.py&quot;</span>, line <span style="color: #000000;">127</span>, <span style="color: #000000; font-weight: bold;">in</span> get_response<br /> <span style="color: #7a0874; font-weight: bold;">return</span> callback<span style="color: #7a0874; font-weight: bold;">&#40;</span>request, <span style="color: #000000; font-weight: bold;">**</span>param_dict<span style="color: #7a0874; font-weight: bold;">&#41;</span><br /> File <span style="color: #ff0000;">&quot;/usr/local/lib/python2.6/dist-packages/django/views/defaults.py&quot;</span>, line <span style="color: #000000;">13</span>, <span style="color: #000000; font-weight: bold;">in</span> page_not_found<br /> t = loader.get_template<span style="color: #7a0874; font-weight: bold;">&#40;</span>template_name<span style="color: #7a0874; font-weight: bold;">&#41;</span> <span style="color: #666666; font-style: italic;"># You need to create a 404.html template.</span><br /> File <span style="color: #ff0000;">&quot;/usr/local/lib/python2.6/dist-packages/django/template/loader.py&quot;</span>, line <span style="color: #000000;">157</span>, <span style="color: #000000; font-weight: bold;">in</span> get_template<br /> template, origin = find_template<span style="color: #7a0874; font-weight: bold;">&#40;</span>template_name<span style="color: #7a0874; font-weight: bold;">&#41;</span><br /> File <span style="color: #ff0000;">&quot;/usr/local/lib/python2.6/dist-packages/django/template/loader.py&quot;</span>, line <span style="color: #000000;">138</span>, <span style="color: #000000; font-weight: bold;">in</span> find_template<br /> raise TemplateDoesNotExist<span style="color: #7a0874; font-weight: bold;">&#40;</span>name<span style="color: #7a0874; font-weight: bold;">&#41;</span><br /> TemplateDoesNotExist: <span style="color: #000000;">404</span>.html<br /> <br /> <span style="color: #000000; font-weight: bold;">&amp;</span>gt;<span style="color: #000000; font-weight: bold;">&amp;</span>gt; <span style="color: #000000; font-weight: bold;">in</span> <span style="color: #ff0000;">&quot;eu vou para a página de listagem de remédios&quot;</span> <span style="color: #666666; font-style: italic;"># delivery/features/listar_remedios.feature:8</span><br /> <br /> <span style="color: #660033;">----------------------------------------------------------------------</span><br /> Ran <span style="color: #000000;">2</span> tests <span style="color: #000000; font-weight: bold;">in</span> 0.370s<br /> <br /> FAILED <span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #007800;">errors</span>=<span style="color: #000000;">1</span><span style="color: #7a0874; font-weight: bold;">&#41;</span><br /> Destroying <span style="color: #7a0874; font-weight: bold;">test</span> database <span style="color: #ff0000;">'default'</span>...</div></div><p>E a resposta do Freshen ao nosso teste foi: Não encontrei o template 404.html! Nada a ver com nosso teste, certo? Acontece que, na verdade, a URL /delivery/remedios não foi encontrada e o Django tenta renderizar o template 404.html nestes casos, mas não importa isso no momento. Nosso desejo aqui é fazer a definição de passo passar, então vamo simplesmente colocar um arquivo chamado 404.html dentro do nosso diretório de templates, chamar o Freshen novamente e ver o que acontece:</p><div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ <span style="color: #c20cb9; font-weight: bold;">mkdir</span> templates<br /> francisco<span style="color: #000000; font-weight: bold;">@</span>radukibook ~<span style="color: #000000; font-weight: bold;">/</span>Projetos<span style="color: #000000; font-weight: bold;">/</span>blog<span style="color: #000000; font-weight: bold;">/</span>farmacia <span style="color: #7a0874; font-weight: bold;">&#40;</span>master<span style="color: #7a0874; font-weight: bold;">&#41;</span> $ <span style="color: #c20cb9; font-weight: bold;">touch</span> templates<span style="color: #000000; font-weight: bold;">/</span><span style="color: #000000;">404</span>.html<br /> francisco<span style="color: #000000; font-weight: bold;">@</span>radukibook ~<span style="color: #000000; font-weight: bold;">/</span>Projetos<span style="color: #000000; font-weight: bold;">/</span>blog<span style="color: #000000; font-weight: bold;">/</span>farmacia <span style="color: #7a0874; font-weight: bold;">&#40;</span>master<span style="color: #7a0874; font-weight: bold;">&#41;</span> $ nosetests <span style="color: #660033;">--with-freshen</span> <span style="color: #660033;">--with-django</span> <span style="color: #660033;">--language</span>=pt <span style="color: #660033;">-v</span><br /> Listar remédios: Listando todos os remédios ... FAIL<br /> Tests that <span style="color: #000000;">1</span> + <span style="color: #000000;">1</span> always equals <span style="color: #000000;">2</span>. ... ok<br /> <br /> ======================================================================<br /> FAIL: Listar remédios: Listando todos os remédios<br /> <span style="color: #660033;">----------------------------------------------------------------------</span><br /> Traceback <span style="color: #7a0874; font-weight: bold;">&#40;</span>most recent call <span style="color: #c20cb9; font-weight: bold;">last</span><span style="color: #7a0874; font-weight: bold;">&#41;</span>:<br /> File <span style="color: #ff0000;">&quot;/home/francisco/Projetos/blog/farmacia/delivery/features/steps.py&quot;</span>, line <span style="color: #000000;">23</span>, <span style="color: #000000; font-weight: bold;">in</span> visitar_pagina_listagem<br /> delivery.validate<span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #7a0874; font-weight: bold;">&#41;</span><br /> File <span style="color: #ff0000;">&quot;/usr/local/lib/python2.6/dist-packages/ludibrio/mock.py&quot;</span>, line <span style="color: #000000;">103</span>, <span style="color: #000000; font-weight: bold;">in</span> validate<br /> self._call_waiting_msg<span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #7a0874; font-weight: bold;">&#41;</span><span style="color: #7a0874; font-weight: bold;">&#41;</span><br /> MockExpectationError: Call waiting:<br /> Expected:<br /> Remedio.objects.all<span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #7a0874; font-weight: bold;">&#41;</span> <span style="color: #000000; font-weight: bold;">&amp;</span>gt;<span style="color: #000000; font-weight: bold;">&amp;</span>gt; scc.remedios<br /> Got only:<br /> <br /> <span style="color: #660033;">----------------------------------------------------------------------</span><br /> Ran <span style="color: #000000;">2</span> tests <span style="color: #000000; font-weight: bold;">in</span> 0.373s<br /> <br /> FAILED <span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #007800;">failures</span>=<span style="color: #000000;">1</span><span style="color: #7a0874; font-weight: bold;">&#41;</span><br /> Destroying <span style="color: #7a0874; font-weight: bold;">test</span> database <span style="color: #ff0000;">'default'</span>...</div></div><p>A validação do mock falhou. Simplesmente por que o mock não teve o comportamento esperado, que é a chamada ao método <em>Remedio.objects.all()</em>. Vamos fazer agora com que a requisição na URL <em>/delivery/remedios</em> seja mapeada para uma view que deverá fazer a chamada ao nosso método.</p><p>Primeiro, vamos definir a nossa view: dentro do módulo view da aplicação delivery, vamos criar a função a ser mapeada para a URL <em>/delivery/remedios</em>. Esta função se chamará <em>lista_remedios</em>:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">def</span> listar_remedios<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>:<br /> Remedio.<span style="color: black;">objects</span>.<span style="color: #008000;">all</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br /> <span style="color: #ff7700;font-weight:bold;">return</span> HttpResponse<span style="color: black;">&#40;</span><span style="color: #483d8b;">''</span><span style="color: black;">&#41;</span></div></div><p>Note que aqui novamente fizemos apenas o necessário para que a definição de passo passe. Tudo o que precisamos é da chamada a <em>Remedio.objects.all()</em>. Mas apenas isso não é suficiente, precisamos ainda mapear a URL <em>/delivery/remedios</em> para a nossa view recém-definida. Para isso, criaremos o model <em>urls.py</em> dentro da aplicação <em>delivery</em> com o seguinte conteúdo:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">conf</span>.<span style="color: black;">urls</span>.<span style="color: black;">defaults</span> <span style="color: #ff7700;font-weight:bold;">import</span> *<br /> <br /> urlpatterns <span style="color: #66cc66;">=</span> patterns<span style="color: black;">&#40;</span><span style="color: #483d8b;">'delivery.views'</span><span style="color: #66cc66;">,</span><br /> url<span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'^remedios'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'listar_remedios'</span><span style="color: #66cc66;">,</span> name <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'listar_remedios'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br /> <span style="color: black;">&#41;</span></div></div><p>Depois de definir tal arquivo, vamos incluir dentro do módulo urls.py principal do projeto, que deverá ficar desta forma:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">conf</span>.<span style="color: black;">urls</span>.<span style="color: black;">defaults</span> <span style="color: #ff7700;font-weight:bold;">import</span> *<br /> <br /> <span style="color: #808080; font-style: italic;"># Uncomment the next two lines to enable the admin:</span><br /> <span style="color: #808080; font-style: italic;"># from django.contrib import admin</span><br /> <span style="color: #808080; font-style: italic;"># admin.autodiscover()</span><br /> <br /> urlpatterns <span style="color: #66cc66;">=</span> patterns<span style="color: black;">&#40;</span><span style="color: #483d8b;">''</span><span style="color: #66cc66;">,</span><br /> <span style="color: #808080; font-style: italic;"># Example:</span><br /> <span style="color: #808080; font-style: italic;"># (r'^farmacia/', include('farmacia.foo.urls')),</span><br /> <br /> <span style="color: #808080; font-style: italic;"># Uncomment the admin/doc line below and add 'django.contrib.admindocs'</span><br /> <span style="color: #808080; font-style: italic;"># to INSTALLED_APPS to enable admin documentation:</span><br /> <span style="color: #808080; font-style: italic;"># (r'^admin/doc/', include('django.contrib.admindocs.urls')),</span><br /> <br /> <span style="color: #808080; font-style: italic;"># Uncomment the next line to enable the admin:</span><br /> <span style="color: #808080; font-style: italic;"># (r'^admin/', include(admin.site.urls)),</span><br /> <span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'^delivery/'</span><span style="color: #66cc66;">,</span> include<span style="color: black;">&#40;</span><span style="color: #483d8b;">'delivery.urls'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br /> <span style="color: black;">&#41;</span></div></div><p>Com o mapeamento de URLs configurado e a view devidamente criada, vamos novamente executar o Freshen para obter o feedback:</p><div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ nosetests <span style="color: #660033;">--with-freshen</span> <span style="color: #660033;">--with-django</span> <span style="color: #660033;">--language</span>=pt <span style="color: #660033;">-v</span><br /> Listar remédios: Listando todos os remédios ... UNDEFINED: <span style="color: #ff0000;">&quot;eu deveria ver a listagem com o nome dos 10 remédios&quot;</span> <span style="color: #666666; font-style: italic;"># delivery/features/listar_remedios.feature:9</span><br /> Tests that <span style="color: #000000;">1</span> + <span style="color: #000000;">1</span> always equals <span style="color: #000000;">2</span>. ... ok<br /> <br /> <span style="color: #660033;">----------------------------------------------------------------------</span><br /> Ran <span style="color: #000000;">2</span> tests <span style="color: #000000; font-weight: bold;">in</span> 0.390s<br /> <br /> OK <span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #007800;">UNDEFINED</span>=<span style="color: #000000;">1</span><span style="color: #7a0874; font-weight: bold;">&#41;</span><br /> Destroying <span style="color: #7a0874; font-weight: bold;">test</span> database <span style="color: #ff0000;">'default'</span>...</div></div><p>A parte boa é que nossa definição para o segundo passou funcionou! Agora o Freshen encontrou outro passo indefinido: &#8220;eu deveria ver a listagem com o nome dos 10 remédios&#8221;. Vamos agora implementar este passo, testando o conteúdo da resposta obtida na requisição à URL /delivery/remedios. Para definir este passo, utilizamos o decorator @Then:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #66cc66;">@</span>Then<span style="color: black;">&#40;</span><span style="color: #483d8b;">'eu deveria ver a listagem com o nome dos (<span style="color: #000099; font-weight: bold;">\d</span>+) remédios'</span><span style="color: black;">&#41;</span><br /> <span style="color: #ff7700;font-weight:bold;">def</span> verificar_conteudo_listagem<span style="color: black;">&#40;</span>quantidade_remedios<span style="color: black;">&#41;</span>:<br /> <span style="color: #ff7700;font-weight:bold;">for</span> remedio <span style="color: #ff7700;font-weight:bold;">in</span> scc.<span style="color: black;">remedios</span>:<br /> conteudo_esperado <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'&amp;lt;li&amp;gt;%s&amp;lt;/li&amp;gt;'</span> %<span style="color: black;">&#40;</span>remedio.<span style="color: black;">nome</span><span style="color: black;">&#41;</span><br /> assert_true<span style="color: black;">&#40;</span>conteudo_esperado <span style="color: #ff7700;font-weight:bold;">in</span> scc.<span style="color: black;">response</span>.<span style="color: black;">content</span><span style="color: black;">&#41;</span></div></div><p>Montamos o elemento HTML <em>li</em> com o conteúdo esperado e depois testamos se todos os nomes de remédios estão presentes na resposta da requisição, cada um em seu respectivo <strong>li</strong>. Depois fazemos um assert, para verificar se o conteúdo que definimos acima está presente no conteúdo da resposta da requisição. Ao chamar novamente o Freshen, obtemos a seguinte saída:</p><div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ nosetests <span style="color: #660033;">--with-freshen</span> <span style="color: #660033;">--with-django</span> <span style="color: #660033;">--language</span>=pt <span style="color: #660033;">-v</span><br /> Listar remédios: Listando todos os remédios ... FAIL<br /> Tests that <span style="color: #000000;">1</span> + <span style="color: #000000;">1</span> always equals <span style="color: #000000;">2</span>. ... ok<br /> <br /> ======================================================================<br /> FAIL: Listar remédios: Listando todos os remédios<br /> <span style="color: #660033;">----------------------------------------------------------------------</span><br /> Traceback <span style="color: #7a0874; font-weight: bold;">&#40;</span>most recent call <span style="color: #c20cb9; font-weight: bold;">last</span><span style="color: #7a0874; font-weight: bold;">&#41;</span>:<br /> File <span style="color: #ff0000;">&quot;/home/francisco/Projetos/blog/farmacia/delivery/features/steps.py&quot;</span>, line <span style="color: #000000;">31</span>, <span style="color: #000000; font-weight: bold;">in</span> verificar_conteudo_listagem<br /> assert_true<span style="color: #7a0874; font-weight: bold;">&#40;</span>conteudo_esperado <span style="color: #000000; font-weight: bold;">in</span> scc.response.content<span style="color: #7a0874; font-weight: bold;">&#41;</span><br /> AssertionError<br /> <br /> <span style="color: #660033;">----------------------------------------------------------------------</span><br /> Ran <span style="color: #000000;">2</span> tests <span style="color: #000000; font-weight: bold;">in</span> 0.362s<br /> <br /> FAILED <span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #007800;">failures</span>=<span style="color: #000000;">1</span><span style="color: #7a0874; font-weight: bold;">&#41;</span><br /> Destroying <span style="color: #7a0874; font-weight: bold;">test</span> database <span style="color: #ff0000;">'default'</span>...</div></div><p>E aí está: um AssertionError, o que significa que nossa definição falhou e o conteúdo esperado não veio na resposta da requisição. Para fazer com que esta definição funcione, vamos refatorar nossa view, para que ela renderize um template e dentro deste template teremos nossos elementos li com os nomes dos remédios. Eis o código da view, refatorado:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">def</span> listar_remedios<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>:<br /> remedios <span style="color: #66cc66;">=</span> Remedio.<span style="color: black;">objects</span>.<span style="color: #008000;">all</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br /> <span style="color: #ff7700;font-weight:bold;">return</span> render_to_response<span style="color: black;">&#40;</span><span style="color: #483d8b;">'listar_remedios.html'</span><span style="color: #66cc66;">,</span> <span style="color: black;">&#123;</span><br /> <span style="color: #483d8b;">'remedios'</span> : remedios<br /> <span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span> context_instance<span style="color: #66cc66;">=</span>RequestContext<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span><br /> <span style="color: black;">&#41;</span></div></div><p>Então devemos criar um template chamado <em>listar_remedios.html</em>. Como é um template da aplicação, coloque ele dentro do diretório templates da aplicação <em>delivery</em>. O conteúdo do template pode ser visto no Gist: <a title="Gist do template" rel="nofollow" href="http://gist.github.com/450936" target="_blank">http://gist.github.com/450936</a></p><p>Acredito que aqui tudo está pronto. Vamos perguntar ao Freshen?</p><div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ nosetests <span style="color: #660033;">--with-freshen</span> <span style="color: #660033;">--with-django</span> <span style="color: #660033;">--language</span>=pt <span style="color: #660033;">-v</span><br /> Listar remédios: Listando todos os remédios ... ok<br /> Tests that <span style="color: #000000;">1</span> + <span style="color: #000000;">1</span> always equals <span style="color: #000000;">2</span>. ... ok<br /> <br /> <span style="color: #660033;">----------------------------------------------------------------------</span><br /> Ran <span style="color: #000000;">2</span> tests <span style="color: #000000; font-weight: bold;">in</span> 0.366s<br /> <br /> OK<br /> Destroying <span style="color: #7a0874; font-weight: bold;">test</span> database <span style="color: #ff0000;">'default'</span>...</div></div><p>Nossa definição de passo passou, e não existe nenhum passo indefinido! Nosso trabalho está pronto, agora basta repetir o ciclo para implementar todas as funcionalidades do sistema, mas isso fica por conta de vocês.</p><p>O código deste projeto está disponível no Github: <a title="Exemplo de BDD em Django" rel="nofollow" href="http://github.com/fsouza/farmacia" target="_blank">http://github.com/fsouza/farmacia</a>.</p> ]]></content:encoded> <wfw:commentRss>http://www.franciscosouza.com.br/2010/06/24/bdd-em-django-desenvolvimento-web-mais-divertido-com-qualidade-usando-freshen/feed/</wfw:commentRss> <slash:comments>3</slash:comments> </item> <item><title>O que muda com o Django 1.2?</title><link>http://www.franciscosouza.com.br/2010/05/18/o-que-muda-com-o-django-1-2/</link> <comments>http://www.franciscosouza.com.br/2010/05/18/o-que-muda-com-o-django-1-2/#comments</comments> <pubDate>Wed, 19 May 2010 02:59:05 +0000</pubDate> <dc:creator>Francisco Souza</dc:creator> <category><![CDATA[desenvolvimento de softwares]]></category> <category><![CDATA[django]]></category> <category><![CDATA[frameworks]]></category> <category><![CDATA[open source]]></category> <category><![CDATA[python]]></category><guid isPermaLink="false">http://www.franciscosouza.com.br/?p=835</guid> <description><![CDATA[Esta semana saiu o Django 1.2 e dentre as novidades desta versão estão recursos como o suporte a múltiplos bancos de dados, a validação dos Django Models, baseada na validação dos Django Forms, um framework para mensagens e uma nova versão da template-tag if. Existem ainda alguns outros recursos, mas vamos dar uma olhada nesses quatro anteriores com mais atenção...]]></description> <content:encoded><![CDATA[<p>Esta semana saiu o Django 1.2 e dentre as novidades desta versão estão recursos como o <a title="Notas de lançamento para suporte a múltiplos bancos de dados no Django 1.2" rel="nofollow" href="http://docs.djangoproject.com/en/1.2/releases/1.2/#support-for-multiple-databases" target="_blank">suporte a múltiplos bancos de dados</a>, a <a title="Notas de lançamento para o model validation do Django 1.2" rel="nofollow" href="http://docs.djangoproject.com/en/1.2/releases/1.2/#model-validation" target="_blank">validação dos Django Models</a>, baseada na validação dos Django Forms, um <a title="Django 1.2 message framework" rel="nofollow" href="http://docs.djangoproject.com/en/1.2/releases/1.2/#messages-framework" target="_blank">framework para mensagens</a> e uma <a title="Notas de lançamento para a smart if template-tag do Django 1.2" rel="nofollow" href="http://docs.djangoproject.com/en/1.2/releases/1.2/#new-in-1-2-smart-if" target="_blank">nova versão da template-tag <em>if</em></a>. Existem ainda alguns outros recursos, mas vamos dar uma olhada nesses quatro anteriores com mais atenção&#8230;<span id="more-835"></span></p><p><strong>1. Suporte ao uso de vários bancos de dados:</strong> Como o nome indica, agora é possível utilizar vários bancos de dados no Django. Isso significa que a configuração do banco de dados no arquivo <em>settings.py </em>não acontece mais da mesma forma. Cada configuração de banco de dados é batizada com um nome, e tem seus próprios parâmetros e na hora de executar alguma tarefa no banco de dados, você especifica qual banco de dados deseja utilizar ao executar uma query através do método <em>using </em>da classe <em>QuerySet</em>, já na hora de gravar registros no banco de dados, você utiliza o <strong>parâmetro</strong> <em>using </em>para informar qual banco de dados utilizar. Não entendeu? Que tal um pouco de código?</p><p>Com Django 1.1:</p><p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">DATABASE_ENGINE <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'sqlite3'</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> DATABASE_NAME <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'dados.db'</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> DATABASE_USER <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">''</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> DATABASE_PASSWORD <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">''</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> DATABASE_HOST <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">''</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> DATABASE_PORT <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">''</span></div></div></p><p>Agora, utilizando o Django 1.2:</p><p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">DATABASES <span style="color: #66cc66;">=</span> <span style="color: black;">&#123;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; <span style="color: #483d8b;">'default'</span>: <span style="color: black;">&#123;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'ENGINE'</span>: <span style="color: #483d8b;">'django.db.backends.sqlite3'</span><span style="color: #66cc66;">,&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'NAME'</span>: <span style="color: #483d8b;">'dados.db'</span><span style="color: #66cc66;">,&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'USER'</span>: <span style="color: #483d8b;">''</span><span style="color: #66cc66;">,&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'PASSWORD'</span>: <span style="color: #483d8b;">''</span><span style="color: #66cc66;">,&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'HOST'</span>: <span style="color: #483d8b;">''</span><span style="color: #66cc66;">,&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'PORT'</span>: <span style="color: #483d8b;">''</span><span style="color: #66cc66;">,&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; <span style="color: black;">&#125;</span><span style="color: #66cc66;">,&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; <span style="color: #483d8b;">'alternative'</span> : <span style="color: black;">&#123;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'ENGINE'</span> : <span style="color: #483d8b;">'django.db.backends.mysql'</span><span style="color: #66cc66;">,&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'NAME'</span> : <span style="color: #483d8b;">'dados'</span><span style="color: #66cc66;">,&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'USER'</span> : <span style="color: #483d8b;">'root'</span><span style="color: #66cc66;">,&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'PASSWORD'</span> : <span style="color: #483d8b;">''</span><span style="color: #66cc66;">,&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'HOST'</span> : <span style="color: #483d8b;">'localhost'</span><span style="color: #66cc66;">,&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'PORT'</span> : <span style="color: #483d8b;">'3306'</span><span style="color: #66cc66;">,&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; <span style="color: black;">&#125;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> <span style="color: black;">&#125;</span></div></div></p><p>Note que configuramos dois bancos de dados, inclusive de tipos diferentes: um SQLite3, que identificamos como <em>default</em> e um MySQL, que identificamos como <em>alternative</em>.</p><p>Existem recursos mais &#8220;avançados&#8221;, como o roteamento automático do banco de dados (utilizar um banco de dados apenas para leitura e outro apenas para escrita, por exemplo), que podem ser conferidos na <a title="Documentação do suporte a múltiplos bancos de dados no Django" rel="nofollow" href="http://docs.djangoproject.com/en/1.2/topics/db/multi-db/" target="_blank">documentação oficial do Django</a>.</p><p><strong>2. Validação de modelos e introdução de validators: </strong>Agora os modelos são capazes de se <a title="Auto-validação dos modelos no Django 1.2" rel="nofollow" href="http://docs.djangoproject.com/en/1.2/ref/models/instances/#validating-objects" target="_blank">auto-validar-se a si mesmos</a> e suporte à construção de validadores personalizados (recomendo não usar esse termo jamais =P <em><a title="Custom validators no Django 1.2" rel="nofollow" href="http://docs.djangoproject.com/en/1.2/ref/validators/#ref-validators" target="_blank">custom validators</a> </em>soa bem melhor), que são configurados diretamente nos Models ou nos Forms.</p><blockquote><p>Os novos validators do Django funcionam de forma semelhante aos validators do <a title="Posts sobre WTForms" href="http://www.franciscosouza.com.br/tag/wtforms/" target="_self">WTForms</a>, que abordei no blogpost anterior. Assim, recomendo a leitura do blogpost de <a title="Formulários inteligentes no Pylons com WTForms" href="http://www.franciscosouza.com.br/2010/04/27/formularios-inteligentes-no-pylons-com-wtforms/" target="_self">integração do WTForms com o Pylons</a> para ver um pouco mais dos validators.</p></blockquote><p>Entendendo um pouco mais da validação de modelos, podemos criar um modelo qualquer e definir o <em>validate_even</em> (que valida se um número é par). Assim, vamos configurar nosso model para aceitar somente números pares em um de seus fields. Um <em>validator</em> nada mais é do que uma função que recebe um parâmetro e valida ele (!):</p><p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">core</span>.<span style="color: #dc143c;">exceptions</span> <span style="color: #ff7700;font-weight:bold;">import</span> ValidationError<span style="color: #66cc66;">&lt;</span>/p<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&lt;</span>p<span style="color: #66cc66;">&gt;</span><span style="color: #ff7700;font-weight:bold;">def</span> validate_even<span style="color: black;">&#40;</span>value<span style="color: black;">&#41;</span>:<span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> value % <span style="color: #ff4500;">2</span> <span style="color: #66cc66;">!=</span> <span style="color: #ff4500;">0</span>:<span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">raise</span> ValidationError<span style="color: black;">&#40;</span>u<span style="color: #483d8b;">'%s não é um número par.'</span> % value<span style="color: black;">&#41;</span></div></div></p><p>E no nosso <em>model</em> nós simplesmente passamos o objeto <em>validate_even</em> dentro de uma lista para o parâmetro <em>validators</em>:</p><p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">db</span> <span style="color: #ff7700;font-weight:bold;">import</span> models<span style="color: #66cc66;">&lt;</span>/p<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&lt;</span>p<span style="color: #66cc66;">&gt;</span><span style="color: #ff7700;font-weight:bold;">class</span> MyModel<span style="color: black;">&#40;</span>models.<span style="color: black;">Model</span><span style="color: black;">&#41;</span>:<span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; even_field <span style="color: #66cc66;">=</span> models.<span style="color: black;">IntegerField</span><span style="color: black;">&#40;</span>validators<span style="color: #66cc66;">=</span><span style="color: black;">&#91;</span>validate_even<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span></div></div></p><p><strong>Atenção:</strong> a validação dos objetos deve ser feita de forma explícita. O método save não executa a validação implicitamente.</p><p><strong>3. Framework de mensagens: </strong>O Django agora inclui um framework poderoso e personalizável para envio de mensagens, utilizadas em três níveis: <em>info, warning</em> e <em>error.</em> A configuração do framework de mensagens é um pouco mais trabalhosa, então recomendo fortemente a leitura da <a title="Documentação oficial para o framework de mensagens do Django 1.2" href="http://docs.djangoproject.com/en/1.2/ref/contrib/messages/" target="_blank">documentação oficial</a>.</p><p><strong>4. Nova template-tag &#8220;if&#8221;: </strong>O Django 1.2 trouxe uma melhoria na template tag if, que agora tornou-se mais poderosa. Esta melhoria pode ser um pouco polêmica, uma vez que facilita a implementação de lógica no template. A ideia aqui é substituir templates tags como <em>ifequal </em>e <em>ifnotequal</em>, além de introduzir o suporte ao uso de <em>template filters</em> com a tag <em>if</em>. Vamos ver um pouco de código&#8230;</p><p>No Django 1.1:</p><p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: black;">&#123;</span>% ifnotequal a b %<span style="color: black;">&#125;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp;...<span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> <span style="color: black;">&#123;</span>% endifnotequal %<span style="color: black;">&#125;</span></div></div></p><p>No Django 1.2:</p><p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: black;">&#123;</span>% <span style="color: #ff7700;font-weight:bold;">if</span> a <span style="color: #66cc66;">!=</span> b %<span style="color: black;">&#125;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp;...<span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> <span style="color: black;">&#123;</span>% endif %<span style="color: black;">&#125;</span></div></div></p><p>Ambos os códigos comparam se duas variáveis (a e b) são diferentes. A nova<em> template tag</em> trouxe o suporte ao uso de operadores, como demonstrado no exemplo acima, e também ao uso de <em>template filters</em>, como demonstrado abaixo:</p><p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #66cc66;">&lt;</span>div<span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; <span style="color: black;">&#123;</span>% <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #dc143c;">user</span>.<span style="color: #dc143c;">email</span>|lower <span style="color: #66cc66;">==</span> message.<span style="color: black;">recipient</span>|lower %<span style="color: black;">&#125;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">class</span><span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;highlight&quot;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; <span style="color: black;">&#123;</span>% endif %<span style="color: black;">&#125;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&gt;</span><span style="color: black;">&#123;</span><span style="color: black;">&#123;</span> message <span style="color: black;">&#125;</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">&lt;</span>/div<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&lt;</span>p<span style="color: #66cc66;">&gt;</span></div></div></p><p>O código acima compara se dois objetos são equivalentes, mas antes da comparação, converte ambos para minúsculo utilizando o <em>template filter</em> <em>lower</em>.</p><p>Existem diversos outros recursos, como template caching, e-mail backends, melhorias no sistema de autenticação e autorização, dentre outras. Para mais detalhes, confira as <a title="Django 1.2 release notes" rel="nofollow" href="http://docs.djangoproject.com/en/1.2/releases/1.2/" target="_blank">notas de lançamento do Django 1.2</a> e a <a title="Documentação oficial do Django 1.2" rel="nofollow" href="http://docs.djangoproject.com/en/1.2/" target="_blank">documentação oficial</a>.</p> ]]></content:encoded> <wfw:commentRss>http://www.franciscosouza.com.br/2010/05/18/o-que-muda-com-o-django-1-2/feed/</wfw:commentRss> <slash:comments>3</slash:comments> </item> <item><title>Behaviour Driven Development (BDD) em Django</title><link>http://www.franciscosouza.com.br/2010/04/02/behaviour-driven-development-bdd-em-django/</link> <comments>http://www.franciscosouza.com.br/2010/04/02/behaviour-driven-development-bdd-em-django/#comments</comments> <pubDate>Sat, 03 Apr 2010 02:30:50 +0000</pubDate> <dc:creator>Francisco Souza</dc:creator> <category><![CDATA[desenvolvimento de softwares]]></category> <category><![CDATA[agile]]></category> <category><![CDATA[BDD]]></category> <category><![CDATA[django]]></category> <category><![CDATA[python]]></category><guid isPermaLink="false">http://www.franciscosouza.net/?p=680</guid> <description><![CDATA[BDD (Behavior Driven Development) é uma técnica de utilizada em metodologias ágeis de desenvolvimento de software que encoraja interação entre todas as partes envolvidas na construção de um software (desenvolvedores e cliente, basicamente) - adaptado da Wikipedia.No Django, é comum vermos pessoas falando sobre o uso de testes unitários e doctests, para um desenvolvimento guiado por testes, mas e como seria utilizar BDD no Django? O que já existe de BDD em Python?]]></description> <content:encoded><![CDATA[<p><em>BDD (Behavior Driven Development) é uma técnica de utilizada em metodologias ágeis de desenvolvimento de software que encoraja interação entre todas as partes envolvidas na construção de um software (desenvolvedores e cliente, basicamente) </em>- adaptado da <a title="BDD in Wikipedia" rel="nofollow" href="http://en.wikipedia.org/wiki/Behavior_Driven_Development" target="_blank">Wikipedia</a>.</p><p>No Django, é comum vermos pessoas falando sobre o uso de testes unitários e doctests, para um <a title="Posts sobre TDD" href="http://www.franciscosouza.com.br/tag/tdd/">desenvolvimento guiado por testes</a>, mas e como seria utilizar BDD no Django? O que já existe de BDD em Python?<span id="more-680"></span></p><p>Na última Python Brasil, Hugo Lopes apresentou uma palestra intitulada <a title="Palestra BDD em Python, por Hugo Lopes" rel="nofollow" href="http://www.pythonbrasil.org.br/2009/sobre-o-evento/inscricoes/00f25696178cf2f075617273aece2edd" target="_blank">&#8220;Behavior Driven Development (BDD) em Python&#8221;</a>, onde apresentou alguns conceitos de BDD e algumas ferramentas e projetos de BDD em Python. Quem quiser saber mais sobre a palestra, pode <a title="Vídeo da palestra BDD em Python, por Hugo Lopes" rel="nofollow" href="http://blip.tv/file/2889996" target="_blank">assistir a palestra na online no blip.tv</a>.</p><p>Ainda não existe algo como o RSpec e o Cucumber sendo utilizado em larga escala junto ao Django, mas vou tentar mostrar aqui um pouco do uso das ferramentas apresentadas pelo Hugo, em conjunto com Django. Os passos para fazer algo em BDD é semelhante aos passos do TDD:</p><ol><li>Escreva o comportamento desejado;</li><li>Escreva o código para atender ao comportamento desejado;</li><li>Teste se o código escrito realmente atende ao comportamento desejado;</li><li>Refatore;</li><li>Entre em loop com os passos anteriores :)</li></ol><p>Podemos ver um pouco mais disso em um exemplo mais prático. Imaginando que estejamos desenvolvendo um sistema acadêmico, e para o módulo de controle de matrícula haja a seguinte <span style="text-decoration: line-through;">especificação </span>história:</p><blockquote><p>Eu como secretário gostaria de cadastrar um aluno no sistema, informando seu nome e sua data de nascimento.</p></blockquote><p>Bom, aí está nossa história. Agora, vamos escrever o comportamento dessa história. Utilizando PyCukes, o comportamento fica definido desta forma:</p><div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">História: Cadastro de Aluno<br /> Como um secretário<br /> Eu quero cadastrar um aluno informando nome e data de nascimento<br /> Para que ele possa ser matriculado em disciplinas<br /> <br /> Cenário 1: Cadastro de aluno sem nome<br /> Dado que eu estou cadastrando um aluno<br /> Quando eu não digito o nome<br /> Então o cadastro não pode ser completado</div></div><p>A especificação em português e bem bonitinha (poderia ser criada pelo seu cliente, com sua orientação). Este é o formato aceito pelo <em>PyCukes.</em> Agora basta traduzir os passos de cada cenário. Criamos apenas o primeiro cenário, que diz que o aluno não pode ser cadastrado sem nome. Então vamos fazer isso funcionar :)</p><p>Como é um projeto Django, devemos criar um model nosso alunos, certo? Errado, é BDD. Primeiro vem o comportamento, depois o código :) Depois de definirmos o comportamento em linguagem natural, precisamos ensinar ao PyCukes o que ele deve fazer com cada um dos passos do nosso cenário:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #808080; font-style: italic;">#coding:utf-8</span><br /> <span style="color: #ff7700;font-weight:bold;">from</span> pycukes <span style="color: #ff7700;font-weight:bold;">import</span> *<br /> <span style="color: #ff7700;font-weight:bold;">from</span> should_dsl <span style="color: #ff7700;font-weight:bold;">import</span> *<br /> <span style="color: #ff7700;font-weight:bold;">import</span> set_django<br /> <br /> <span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">db</span> <span style="color: #ff7700;font-weight:bold;">import</span> IntegrityError<br /> <span style="color: #ff7700;font-weight:bold;">from</span> matricula.<span style="color: black;">models</span> <span style="color: #ff7700;font-weight:bold;">import</span> Aluno<br /> <br /> <span style="color: #66cc66;">@</span>DadoQue<span style="color: black;">&#40;</span><span style="color: #483d8b;">'eu estou cadastrando um aluno'</span><span style="color: black;">&#41;</span><br /> <span style="color: #ff7700;font-weight:bold;">def</span> iniciar_cadastro_aluno<span style="color: black;">&#40;</span>contexto<span style="color: black;">&#41;</span>:<br /> <span style="color: #483d8b;">&quot;&quot;&quot;docstring for iniciar_cadastro_aluno&quot;&quot;&quot;</span><br /> contexto._aluno <span style="color: #66cc66;">=</span> Aluno<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br /> <br /> <span style="color: #66cc66;">@</span>Quando<span style="color: black;">&#40;</span><span style="color: #483d8b;">'eu não digito o nome'</span><span style="color: black;">&#41;</span><br /> <span style="color: #ff7700;font-weight:bold;">def</span> aluno_sem_nome<span style="color: black;">&#40;</span>contexto<span style="color: black;">&#41;</span>:<br /> <span style="color: #483d8b;">&quot;&quot;&quot;docstring for aluno_sem_nome&quot;&quot;&quot;</span><br /> contexto._aluno.<span style="color: black;">nome</span> <span style="color: #66cc66;">=</span> <span style="color: #008000;">None</span><br /> <br /> <span style="color: #66cc66;">@</span>Entao<span style="color: black;">&#40;</span><span style="color: #483d8b;">'o cadastro não pode ser completado'</span><span style="color: black;">&#41;</span><br /> <span style="color: #ff7700;font-weight:bold;">def</span> cadastro_nao_pode_ser_completado<span style="color: black;">&#40;</span>contexto<span style="color: black;">&#41;</span>:<br /> <span style="color: #483d8b;">&quot;&quot;&quot;docstring for cadastro_nao_pode_ser_completado&quot;&quot;&quot;</span><br /> IntegrityError |should_be.<span style="color: black;">thrown_by</span>| contexto._aluno.<span style="color: black;">save</span></div></div><p>Note na linha 4 o código <em>import set_django</em>. Trata-se do módulo que configura para que o projeto do Django possa ser enxergado pelo PyCukes. O código deste módulo é o seguinte:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">core</span>.<span style="color: black;">management</span> <span style="color: #ff7700;font-weight:bold;">import</span> setup_environ<br /> <span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span><br /> <span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span><br /> <br /> abs_path <span style="color: #66cc66;">=</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">dirname</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">abspath</span><span style="color: black;">&#40;</span>__file__<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br /> abs_path <span style="color: #66cc66;">=</span> abs_path.<span style="color: black;">replace</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'stories/step_definitions'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">''</span><span style="color: black;">&#41;</span><br /> <span style="color: #dc143c;">sys</span>.<span style="color: black;">path</span>.<span style="color: black;">append</span><span style="color: black;">&#40;</span>abs_path<span style="color: black;">&#41;</span><br /> <br /> <span style="color: #ff7700;font-weight:bold;">import</span> settings<br /> <br /> setup_environ<span style="color: black;">&#40;</span>settings<span style="color: black;">&#41;</span></div></div><p>Ao rodarmos o Pycukes, obteremos um erro, informando que a classe Aluno não existe:</p><div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ pycukes <span style="color: #660033;">-l</span> pt-br<br /> Traceback <span style="color: #7a0874; font-weight: bold;">&#40;</span>most recent call <span style="color: #c20cb9; font-weight: bold;">last</span><span style="color: #7a0874; font-weight: bold;">&#41;</span>:<br /> File <span style="color: #ff0000;">&quot;/usr/local/bin/pycukes&quot;</span>, line <span style="color: #000000;">8</span>, <span style="color: #000000; font-weight: bold;">in</span> <span style="color: #000000; font-weight: bold;">&amp;</span>lt;module<span style="color: #000000; font-weight: bold;">&amp;</span>gt;<br /> load_entry_point<span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #ff0000;">'pycukes==0.1.2'</span>, <span style="color: #ff0000;">'console_scripts'</span>, <span style="color: #ff0000;">'pycukes'</span><span style="color: #7a0874; font-weight: bold;">&#41;</span><span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #7a0874; font-weight: bold;">&#41;</span><br /> File <span style="color: #ff0000;">&quot;/usr/local/lib/python2.6/dist-packages/pycukes-0.1.2-py2.6.egg/pycukes/console.py&quot;</span>, line <span style="color: #000000;">40</span>, <span style="color: #000000; font-weight: bold;">in</span> main<br /> steps_modules = find_steps_modules<span style="color: #7a0874; font-weight: bold;">&#40;</span>values.steps_dir or stories_dirname+<span style="color: #ff0000;">'/step_definitions'</span><span style="color: #7a0874; font-weight: bold;">&#41;</span><br /> File <span style="color: #ff0000;">&quot;/usr/local/lib/python2.6/dist-packages/pycukes-0.1.2-py2.6.egg/pycukes/finder.py&quot;</span>, line <span style="color: #000000;">8</span>, <span style="color: #000000; font-weight: bold;">in</span> find_steps_modules<br /> <span style="color: #000000; font-weight: bold;">if</span> filename.endswith<span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #ff0000;">'steps.py'</span><span style="color: #7a0874; font-weight: bold;">&#41;</span><span style="color: #7a0874; font-weight: bold;">&#93;</span><br /> File <span style="color: #ff0000;">&quot;stories/step_definitions/cadastro_aluno_steps.py&quot;</span>, line <span style="color: #000000;">7</span>, <span style="color: #000000; font-weight: bold;">in</span> <span style="color: #000000; font-weight: bold;">&amp;</span>lt;module<span style="color: #000000; font-weight: bold;">&amp;</span>gt;<br /> from matricula.models import Aluno<br /> ImportError: cannot import name Aluno<br /> <br /> o shell devolveu <span style="color: #000000;">1</span></div></div><p>Ainda não vale por que foi um erro do Python, e não do Pycukes, certo? Então vamos criar o model Aluno e rodar o Pycukes agora com o aluno certinho, para ver se funciona?</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">class</span> Aluno<span style="color: black;">&#40;</span>models.<span style="color: black;">Model</span><span style="color: black;">&#41;</span>:<br /> nome <span style="color: #66cc66;">=</span> models.<span style="color: black;">CharField</span><span style="color: black;">&#40;</span>max_length<span style="color: #66cc66;">=</span><span style="color: #ff4500;">100</span><span style="color: black;">&#41;</span><br /> data_nascimento <span style="color: #66cc66;">=</span> models.<span style="color: black;">DateField</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></div><p>Rodando o PyCukes agora, tudo funciona certinho:</p><div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">pycukes <span style="color: #660033;">-l</span> pt-br<br /> História: Cadastro de Aluno<br /> Como um secretário<br /> Eu quero cadastrar um aluno informando nome e data de nascimento<br /> Para que ele possa ser matriculado em disciplinas<br /> <br /> Cenário <span style="color: #000000;">1</span>: Cadastro de aluno sem nome<br /> Dado que eu estou cadastrando um aluno &nbsp; ... OK<br /> Quando eu não digito o nome &nbsp; ... OK<br /> Então o cadastro não pode ser completado &nbsp; ... OK<br /> <br /> Rodou <span style="color: #000000;">1</span> cenário com <span style="color: #000000;">0</span> falhas, <span style="color: #000000;">0</span> erros e <span style="color: #000000;">0</span> passos pendentes</div></div><p>Note que a garantia de funcionalidade do comportamento é seguirmos o padrão do Django, dizendo que não aceitaremos null no atributo <em>nome</em> da classe <em>Aluno</em>.</p><p>Bom, aí está, um pouco de BDD, baseado no trio <em>Given, When</em> e <em>Then.</em> Quem quiser pode explorar mais o pycukes no <a title="Repositório do PyCukes" rel="nofollow" href="http://github.com/hugobr/pycukes" target="_blank">repositório do projeto no Github</a>.</p><p>Futuramente devo trazer um exemplo mais completo. O código do projeto deste post pode ser encontrado no Github: <a title="Repositório Git do projeto deste post" rel="nofollow" href="http://github.com/fsouza/post-bdd-django" target="_blank">http://github.com/fsouza/post-bdd-django</a>.</p> ]]></content:encoded> <wfw:commentRss>http://www.franciscosouza.com.br/2010/04/02/behaviour-driven-development-bdd-em-django/feed/</wfw:commentRss> <slash:comments>8</slash:comments> </item> <item><title>Construindo uma API RESTful em Django e acessando com Java</title><link>http://www.franciscosouza.com.br/2010/02/06/construindo-uma-api-restful-em-django-e-acessando-com-java/</link> <comments>http://www.franciscosouza.com.br/2010/02/06/construindo-uma-api-restful-em-django-e-acessando-com-java/#comments</comments> <pubDate>Sat, 06 Feb 2010 18:17:59 +0000</pubDate> <dc:creator>Francisco Souza</dc:creator> <category><![CDATA[desenvolvimento de softwares]]></category> <category><![CDATA[django]]></category> <category><![CDATA[java]]></category> <category><![CDATA[piston]]></category> <category><![CDATA[python]]></category> <category><![CDATA[REST]]></category><guid isPermaLink="false">http://www.franciscosouza.net/?p=633</guid> <description><![CDATA[No post sobre aplicações RESTful no Django, fiz uma rápida introdução sobre o django-piston. O piston é um framework para construção de APIs REST utilizando Django. Vamos a um exemplo bem simples: o IBGE com uma API REST para consulta de informações sobre cidades e regiões metropolitanas, onde os dados serão servidos em formato JSON. Vamos implementar dois recursos básicos: A consulta de estados e a consulta de cidades de um estados. E para aplicar isso, que tal uma aplicação em Java Swing com dois combo boxes, um para cidade e outro para estado?]]></description> <content:encoded><![CDATA[<p>No post sobre <a title="Aplicações RESTful no Django" href="http://www.franciscosouza.com.br/2009/12/30/aplicacoes-restful-no-django/">aplicações RESTful no Django</a>, fiz uma rápida introdução sobre o <a title="Django Piston" href="http://bitbucket.org/jespern/django-piston/" target="_blank">django-piston</a>. O piston é um <a title="Posts sobre frameworks" href="http://www.franciscosouza.com.br/tag/frameworks">framework</a> para construção de APIs REST utilizando Django. Vamos a um exemplo bem simples: o IBGE com uma API REST para consulta de informações sobre cidades e regiões metropolitanas, onde os dados serão servidos em formato JSON. Vamos implementar dois recursos básicos: A consulta de estados e a consulta de cidades de um estados. E para aplicar isso, que tal uma aplicação em Java Swing com dois combo boxes, um para cidade e outro para estado?<span id="more-633"></span></p><p>Bom, para começar, criamos o nosso projeto no Django, com a aplicação <em>ibge_cidades</em> (talvez o nome não seja melhor, mas imagine que seja uma nova implementação do IBGE Cidades xD). Três comandos básicos e estamos lá:</p><pre>$ django-admin.py startproject ibge_portal
$ cd ibge_portal
$ django-admin.py startapp ibge_cidades</pre><p>Agora, dentro da aplicação ibge_cidades, vamos definir nossos modelos no arquivo <em>models.py</em>:</p><p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">db</span> <span style="color: #ff7700;font-weight:bold;">import</span> models<span style="color: #66cc66;">&lt;</span>/p<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&lt;</span>p<span style="color: #66cc66;">&gt;</span><span style="color: #808080; font-style: italic;"># Create your models here.&lt;br /&gt;</span><br /> <span style="color: #ff7700;font-weight:bold;">class</span> Estado<span style="color: black;">&#40;</span>models.<span style="color: black;">Model</span><span style="color: black;">&#41;</span>:<span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; sigla <span style="color: #66cc66;">=</span> models.<span style="color: black;">CharField</span><span style="color: black;">&#40;</span>max_length <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">2</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; nome <span style="color: #66cc66;">=</span> models.<span style="color: black;">CharField</span><span style="color: black;">&#40;</span>max_length <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">100</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">&lt;</span>/p<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&lt;</span>p<span style="color: #66cc66;">&gt;</span><span style="color: #ff7700;font-weight:bold;">class</span> Cidade<span style="color: black;">&#40;</span>models.<span style="color: black;">Model</span><span style="color: black;">&#41;</span>:<span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; nome <span style="color: #66cc66;">=</span> models.<span style="color: black;">CharField</span><span style="color: black;">&#40;</span>max_length <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">100</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; estado <span style="color: #66cc66;">=</span> models.<span style="color: black;">ForeignKey</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Estado'</span><span style="color: black;">&#41;</span></div></div></p><p>Modelos simples para um exemplo bem simples =) Agora, vamos popular um pouco nosso banco de dados, apenas para ter uma &#8220;gigante&#8221; massa de dados:</p><p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ ./manage.<span style="color: black;">py</span> shell<span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> Python 2.6.4 <span style="color: black;">&#40;</span>r264:<span style="color: #ff4500;">75706</span><span style="color: #66cc66;">,</span> Dec &nbsp;<span style="color: #ff4500;">7</span> <span style="color: #ff4500;">2009</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">18</span>:<span style="color: #ff4500;">43</span>:<span style="color: #ff4500;">55</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> <span style="color: black;">&#91;</span>GCC 4.4.1<span style="color: black;">&#93;</span> on linux2<span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> Type <span style="color: #483d8b;">&quot;help&quot;</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">&quot;copyright&quot;</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">&quot;credits&quot;</span> <span style="color: #ff7700;font-weight:bold;">or</span> <span style="color: #483d8b;">&quot;license&quot;</span> <span style="color: #ff7700;font-weight:bold;">for</span> more information.<span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> <span style="color: black;">&#40;</span>InteractiveConsole<span style="color: black;">&#41;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&gt;&gt;&gt;</span> <span style="color: #ff7700;font-weight:bold;">from</span> ibge_cidades.<span style="color: black;">models</span> <span style="color: #ff7700;font-weight:bold;">import</span> *<span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&gt;&gt;&gt;</span> es <span style="color: #66cc66;">=</span> Estado<span style="color: black;">&#40;</span>sigla <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'ES'</span><span style="color: #66cc66;">,</span> nome <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'Espírito Santo'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&gt;&gt;&gt;</span> es.<span style="color: black;">save</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&gt;&gt;&gt;</span> rj <span style="color: #66cc66;">=</span> Estado<span style="color: black;">&#40;</span>sigla <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'RJ'</span><span style="color: #66cc66;">,</span> nome <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'Rio de Janeiro'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&gt;&gt;&gt;</span> rj.<span style="color: black;">save</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&gt;&gt;&gt;</span> niteroi <span style="color: #66cc66;">=</span> Cidade<span style="color: black;">&#40;</span>nome <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'Niterói'</span><span style="color: #66cc66;">,</span> estado <span style="color: #66cc66;">=</span> rj<span style="color: black;">&#41;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&gt;&gt;&gt;</span> niteroi.<span style="color: black;">save</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&gt;&gt;&gt;</span> cachoeiro <span style="color: #66cc66;">=</span> Cidade<span style="color: black;">&#40;</span>nome <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'Cachoeiro de Itapemirim'</span><span style="color: #66cc66;">,</span> estado <span style="color: #66cc66;">=</span> es<span style="color: black;">&#41;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&gt;&gt;&gt;</span> cachoeiro.<span style="color: black;">save</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&gt;&gt;&gt;</span> vitoria <span style="color: #66cc66;">=</span> Cidade<span style="color: black;">&#40;</span>nome <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'Vitória'</span><span style="color: #66cc66;">,</span> estado <span style="color: #66cc66;">=</span> es<span style="color: black;">&#41;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&gt;&gt;&gt;</span> vitoria.<span style="color: black;">save</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&gt;&gt;&gt;</span> rio <span style="color: #66cc66;">=</span> Cidade<span style="color: black;">&#40;</span>nome <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'Rio de Janeiro'</span><span style="color: #66cc66;">,</span> estado <span style="color: #66cc66;">=</span> rj<span style="color: black;">&#41;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&gt;&gt;&gt;</span> rio.<span style="color: black;">save</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&gt;&gt;&gt;</span></div></div></p><p>Com o nosso banco de dados super populado e nossos modelos enfim definidos, podemos seguir em frente e construir nossa API :) A API será servida no diretório <em>api</em>, assim, basta criar um pacote Python chamado api na raiz do projeto:</p><pre>$ mkdir api
$ touch api/__init__.py</pre><p>Adicionamos ainda os arquivos <em>urls.py</em> e <em>handlers.py</em> ao nosso diretório api:</p><pre>$ touch api/urls.py
$ touch api/handlers.py</pre><p>O piston trabalha com resources, que são objetos modelados por uma classe que herda de <em>BaseHandler</em>. Cada resource pode estar relacionado diretamente com um model. Vamos definir dois handlers: EstadoHandler e CidadeHandler:</p><p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">class</span> EstadoHandler <span style="color: black;">&#40;</span>BaseHandler<span style="color: black;">&#41;</span>:<span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; allowed_methods <span style="color: #66cc66;">=</span> <span style="color: black;">&#40;</span><span style="color: #483d8b;">'GET'</span><span style="color: #66cc66;">,</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; model <span style="color: #66cc66;">=</span> Estado<span style="color: #66cc66;">&lt;</span>/p<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&lt;</span>p<span style="color: #66cc66;">&gt;</span> &nbsp; &nbsp;<span style="color: #ff7700;font-weight:bold;">def</span> read<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> request<span style="color: black;">&#41;</span>:<span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; pass<span style="color: #66cc66;">&lt;</span>/p<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&lt;</span>p<span style="color: #66cc66;">&gt;</span><span style="color: #ff7700;font-weight:bold;">class</span> CidadeHandler<span style="color: black;">&#40;</span>BaseHandler<span style="color: black;">&#41;</span>:<span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; allowed_methods <span style="color: #66cc66;">=</span> <span style="color: black;">&#40;</span><span style="color: #483d8b;">'GET'</span><span style="color: #66cc66;">,</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; model <span style="color: #66cc66;">=</span> Cidade<span style="color: #66cc66;">&lt;</span>/p<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&lt;</span>p<span style="color: #66cc66;">&gt;</span> &nbsp; &nbsp;<span style="color: #ff7700;font-weight:bold;">def</span> read<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> request<span style="color: #66cc66;">,</span> sigla_estado<span style="color: black;">&#41;</span>:<span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">pass</span></div></div></p><p>O método <em>read </em>de um <em>resource</em> responde a uma requisição do tipo GET, existem ainda os métodos <em>create</em>, <em>update</em> e <em>delete</em> para responder às requisições do tipo <em>POST, PUT</em> e <em>DELETE</em>, respectivamente. O método read do <em>EstadoHandler</em> retornará a lista com todos os estados, em formato JSON, já o método <em>read</em> do <em>resource CidadeHandler</em> receberá a sigla de um estado e listará todas as cidades daquele estado. A implementação disso é bem simples:</p><p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">class</span> EstadoHandler <span style="color: black;">&#40;</span>BaseHandler<span style="color: black;">&#41;</span>:<span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; allowed_methods <span style="color: #66cc66;">=</span> <span style="color: black;">&#40;</span><span style="color: #483d8b;">'GET'</span><span style="color: #66cc66;">,</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; model <span style="color: #66cc66;">=</span> Estado<span style="color: #66cc66;">&lt;</span>/p<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&lt;</span>p<span style="color: #66cc66;">&gt;</span> &nbsp; &nbsp;<span style="color: #ff7700;font-weight:bold;">def</span> read<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> request<span style="color: black;">&#41;</span>:<span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; estados <span style="color: #66cc66;">=</span> Estado.<span style="color: black;">objects</span>.<span style="color: #008000;">all</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> estados<span style="color: #66cc66;">&lt;</span>/p<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&lt;</span>p<span style="color: #66cc66;">&gt;</span><span style="color: #ff7700;font-weight:bold;">class</span> CidadeHandler<span style="color: black;">&#40;</span>BaseHandler<span style="color: black;">&#41;</span>:<span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; allowed_methods <span style="color: #66cc66;">=</span> <span style="color: black;">&#40;</span><span style="color: #483d8b;">'GET'</span><span style="color: #66cc66;">,</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; model <span style="color: #66cc66;">=</span> Cidade<span style="color: #66cc66;">&lt;</span>/p<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&lt;</span>p<span style="color: #66cc66;">&gt;</span> &nbsp; &nbsp;<span style="color: #ff7700;font-weight:bold;">def</span> read<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> request<span style="color: #66cc66;">,</span> sigla_estado<span style="color: black;">&#41;</span>:<span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; cidades <span style="color: #66cc66;">=</span> Cidade.<span style="color: black;">objects</span>.<span style="color: #008000;">filter</span><span style="color: black;">&#40;</span>estado__sigla <span style="color: #66cc66;">=</span> sigla_estado<span style="color: black;">&#41;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> cidades</div></div></p><p>Duas linhas pra cada método e a API está quase pronta. O que falta? Apenas o mapeamento de URLs, que fica da seguinte forma:</p><ul><li>GET /api/estados: Retorna a lista de todos estados;</li><li>GET /api/cidades/&lt;sigla&gt;: Retorna a lista de todas as cidades do estado identificado por &lt;sigla&gt;.</li></ul><p>Temos um arquivo <em>urls.py</em> na raiz do projeto e outro no diretório <em>api</em>. Primeiro, editamos o arquivo da raiz do projeto, para adicionar um include ao outro arquivo:</p><p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">conf</span>.<span style="color: black;">urls</span>.<span style="color: black;">defaults</span> <span style="color: #ff7700;font-weight:bold;">import</span> *<span style="color: #66cc66;">&lt;</span>/p<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&lt;</span>p<span style="color: #66cc66;">&gt;</span>urlpatterns <span style="color: #66cc66;">=</span> patterns<span style="color: black;">&#40;</span><span style="color: #483d8b;">''</span><span style="color: #66cc66;">,&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; <span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'^api/'</span><span style="color: #66cc66;">,</span> include<span style="color: black;">&#40;</span><span style="color: #483d8b;">'ibge_portal.api.urls'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> <span style="color: black;">&#41;</span></div></div></p><p>Em seguida adicionamos ao <em>api/urls.py</em> o mapeamento para os <em>resources</em>:</p><p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">conf</span>.<span style="color: black;">urls</span>.<span style="color: black;">defaults</span> <span style="color: #ff7700;font-weight:bold;">import</span> *<span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #ff7700;font-weight:bold;">from</span> piston.<span style="color: #dc143c;">resource</span> <span style="color: #ff7700;font-weight:bold;">import</span> Resource<span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #ff7700;font-weight:bold;">from</span> ibge_portal.<span style="color: black;">api</span>.<span style="color: black;">handlers</span> <span style="color: #ff7700;font-weight:bold;">import</span> EstadoHandler<span style="color: #66cc66;">,</span> CidadeHandler<span style="color: #66cc66;">&lt;</span>/p<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&lt;</span>p<span style="color: #66cc66;">&gt;</span>estado_resource <span style="color: #66cc66;">=</span> Resource<span style="color: black;">&#40;</span>EstadoHandler<span style="color: black;">&#41;</span><span style="color: #66cc66;">&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> cidade_resource <span style="color: #66cc66;">=</span> Resource<span style="color: black;">&#40;</span>CidadeHandler<span style="color: black;">&#41;</span><span style="color: #66cc66;">&lt;</span>/p<span style="color: #66cc66;">&gt;</span><br /> <span style="color: #66cc66;">&lt;</span>p<span style="color: #66cc66;">&gt;</span>urlpatterns <span style="color: #66cc66;">=</span> patterns<span style="color: black;">&#40;</span><span style="color: #483d8b;">''</span><span style="color: #66cc66;">,&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; <span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'^estados/'</span><span style="color: #66cc66;">,</span> estado_resource<span style="color: black;">&#41;</span><span style="color: #66cc66;">,&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> &nbsp; &nbsp; <span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'^cidades/(?P&lt;sigla_estado&gt;[^/]+)'</span><span style="color: #66cc66;">,</span> cidade_resource<span style="color: black;">&#41;</span><span style="color: #66cc66;">,&lt;</span>br /<span style="color: #66cc66;">&gt;</span><br /> <span style="color: black;">&#41;</span></div></div></p><p>Agora sim a API está pronta, e já podemos construir uma aplicação utilizando Java Swing para acessar a API :) O código da aplicação Java é bem simples (e pode estar despadronizado, desculpe programadores Java =/). Basicamente, existem dois métodos, um para preencher cada combo:</p><p><div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000066; font-weight: bold;">void</span> preencherComboCidades<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Aioexception+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">IOException</span></a> <span style="color: #009900;">&#123;</span><span style="color: #339933;">&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; Estado selecionado <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>Estado<span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">this</span>.<span style="color: #006633;">comboEstados</span>.<span style="color: #006633;">getSelectedItem</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; <a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Aurl+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">URL</span></a> url <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> <a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Aurl+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">URL</span></a><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;http://localhost:8000/api/cidades/&quot;</span> <span style="color: #339933;">+</span> selecionado.<span style="color: #006633;">getSigla</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; <a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Aurlconnection+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">URLConnection</span></a> urlConnection <span style="color: #339933;">=</span> url.<span style="color: #006633;">openConnection</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; <a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Abufferedreader+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">BufferedReader</span></a> reader <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> <a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Abufferedreader+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">BufferedReader</span></a><span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">new</span> <a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Ainputstreamreader+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">InputStreamReader</span></a><span style="color: #009900;">&#40;</span>urlConnection.<span style="color: #006633;">getInputStream</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; <a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Astring+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">String</span></a> linhas <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;&quot;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; <a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Astring+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">String</span></a> linha <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">while</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>linha <span style="color: #339933;">=</span> reader.<span style="color: #006633;">readLine</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">!=</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><span style="color: #339933;">&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; linhas <span style="color: #339933;">+=</span> linha<span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><span style="color: #339933;">&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; reader.<span style="color: #006633;">close</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; JSONArray array <span style="color: #339933;">=</span> JSONArray.<span style="color: #006633;">fromObject</span><span style="color: #009900;">&#40;</span>linhas<span style="color: #009900;">&#41;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; <a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Aobject+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">Object</span></a><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> objCidades <span style="color: #339933;">=</span> array.<span style="color: #006633;">toArray</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; Map<span style="color: #339933;">&lt;</span><a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Astring+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">String</span></a>, Class<span style="color: #339933;">&gt;</span> classMap <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> HashMap<span style="color: #339933;">&lt;</span><a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Astring+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">String</span></a>, Class<span style="color: #339933;">&gt;</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; classMap.<span style="color: #006633;">put</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;estado&quot;</span>, Estado.<span style="color: #000000; font-weight: bold;">class</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; <a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Adefaultcomboboxmodel+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">DefaultComboBoxModel</span></a> model <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Adefaultcomboboxmodel+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">DefaultComboBoxModel</span></a><span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">this</span>.<span style="color: #006633;">comboCidades</span>.<span style="color: #006633;">getModel</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; model.<span style="color: #006633;">removeAllElements</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">for</span> <span style="color: #009900;">&#40;</span><a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Aobject+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">Object</span></a> objCidade <span style="color: #339933;">:</span> objCidades<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><span style="color: #339933;">&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; JSONObject object <span style="color: #339933;">=</span> JSONObject.<span style="color: #006633;">fromObject</span><span style="color: #009900;">&#40;</span>objCidade<span style="color: #009900;">&#41;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; Cidade cidade <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>Cidade<span style="color: #009900;">&#41;</span> JSONObject.<span style="color: #006633;">toBean</span><span style="color: #009900;">&#40;</span>object, Cidade.<span style="color: #000000; font-weight: bold;">class</span>, classMap<span style="color: #009900;">&#41;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; model.<span style="color: #006633;">addElement</span><span style="color: #009900;">&#40;</span>cidade<span style="color: #009900;">&#41;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><span style="color: #339933;">&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> <span style="color: #009900;">&#125;</span><span style="color: #339933;">&lt;/</span>p<span style="color: #339933;">&gt;</span><br /> <span style="color: #339933;">&lt;</span>p<span style="color: #339933;">&gt;</span><span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000066; font-weight: bold;">void</span> preencherComboEstados<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Aioexception+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">IOException</span></a> <span style="color: #009900;">&#123;</span><span style="color: #339933;">&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; <a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Aurl+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">URL</span></a> url <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> <a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Aurl+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">URL</span></a><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;http://localhost:8000/api/estados&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; <a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Aurlconnection+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">URLConnection</span></a> urlConnection <span style="color: #339933;">=</span> url.<span style="color: #006633;">openConnection</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; <a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Abufferedreader+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">BufferedReader</span></a> reader <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> <a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Abufferedreader+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">BufferedReader</span></a><span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">new</span> <a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Ainputstreamreader+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">InputStreamReader</span></a><span style="color: #009900;">&#40;</span>urlConnection.<span style="color: #006633;">getInputStream</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; <a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Astring+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">String</span></a> linhas <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;&quot;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; <a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Astring+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">String</span></a> linha <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">while</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>linha <span style="color: #339933;">=</span> reader.<span style="color: #006633;">readLine</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">!=</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><span style="color: #339933;">&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; linhas <span style="color: #339933;">+=</span> linha<span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><span style="color: #339933;">&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; reader.<span style="color: #006633;">close</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; JSONArray array <span style="color: #339933;">=</span> JSONArray.<span style="color: #006633;">fromObject</span><span style="color: #009900;">&#40;</span>linhas<span style="color: #009900;">&#41;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; <a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Aobject+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">Object</span></a><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> objEstados <span style="color: #339933;">=</span> array.<span style="color: #006633;">toArray</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; <a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Adefaultcomboboxmodel+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">DefaultComboBoxModel</span></a> model <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> <a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Adefaultcomboboxmodel+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">DefaultComboBoxModel</span></a><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; model.<span style="color: #006633;">addElement</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;Selecione o estado&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">for</span> <span style="color: #009900;">&#40;</span><a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Aobject+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">Object</span></a> objEstado <span style="color: #339933;">:</span> objEstados<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><span style="color: #339933;">&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; JSONObject object <span style="color: #339933;">=</span> JSONObject.<span style="color: #006633;">fromObject</span><span style="color: #009900;">&#40;</span>objEstado<span style="color: #009900;">&#41;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; Estado estado <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>Estado<span style="color: #009900;">&#41;</span> JSONObject.<span style="color: #006633;">toBean</span><span style="color: #009900;">&#40;</span>object, Estado.<span style="color: #000000; font-weight: bold;">class</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; model.<span style="color: #006633;">addElement</span><span style="color: #009900;">&#40;</span>estado<span style="color: #009900;">&#41;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><span style="color: #339933;">&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">this</span>.<span style="color: #006633;">comboEstados</span> <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> <a href="http://www.google.com/search?hl=en&amp;q=allinurl%3Ajcombobox+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span style="color: #003399;">JComboBox</span></a><span style="color: #009900;">&#40;</span>model<span style="color: #009900;">&#41;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">this</span>.<span style="color: #006633;">comboEstados</span>.<span style="color: #006633;">addItemListener</span><span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">this</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;&lt;</span>br <span style="color: #339933;">/&gt;</span><br /> <span style="color: #009900;">&#125;</span></div></div></p><p>O código completo dos dois projetos está no Bitbucket:</p><ul><li>Lado servidor, Django: <a title="Codigo do lado servidor" href="http://bitbucket.org/franciscosouza/exemplo-api-django/" target="_blank">http://bitbucket.org/franciscosouza/exemplo-api-django/</a>;</li><li>Lado cliente, Java Swing: <a title="Código do lado cliente" href="http://bitbucket.org/franciscosouza/exemplo-api-java-swing/" target="_blank">http://bitbucket.org/franciscosouza/exemplo-api-java-swing/</a>.</li></ul> ]]></content:encoded> <wfw:commentRss>http://www.franciscosouza.com.br/2010/02/06/construindo-uma-api-restful-em-django-e-acessando-com-java/feed/</wfw:commentRss> <slash:comments>8</slash:comments> </item> <item><title>Uso de frameworks para desenvolvimento web e mitos que já deveriam ter desaparecido</title><link>http://www.franciscosouza.com.br/2010/01/22/uso-de-frameworks-para-desenvolvimento-web-e-mitos-que-ja-deveriam-ter-desaparecido/</link> <comments>http://www.franciscosouza.com.br/2010/01/22/uso-de-frameworks-para-desenvolvimento-web-e-mitos-que-ja-deveriam-ter-desaparecido/#comments</comments> <pubDate>Fri, 22 Jan 2010 23:11:20 +0000</pubDate> <dc:creator>Francisco Souza</dc:creator> <category><![CDATA[desenvolvimento de softwares]]></category> <category><![CDATA[django]]></category> <category><![CDATA[frameworks]]></category> <category><![CDATA[open source]]></category> <category><![CDATA[preconceito]]></category><guid isPermaLink="false">http://www.franciscosouza.net/?p=617</guid> <description><![CDATA[O desenvolvimento de aplicações web utilizando frameworks se expandiu de tal forma que tornou-se imprescindível o uso de frameworks para construção de sistemas voltados para internet/intranet/web. Pequenos e grandes; nacionais e internacionais; muitos são os cases atribuídos aos modernos e ágeis frameworks para desenvolvimento web.]]></description> <content:encoded><![CDATA[<p>O desenvolvimento de aplicações web utilizando <a title="Posts sobre frameworks" href="http://www.franciscosouza.com.br/tag/frameworks/">frameworks</a> se expandiu de tal forma que tornou-se imprescindível o uso de frameworks para construção de sistemas voltados para internet/intranet/web. Pequenos e grandes; nacionais e internacionais; muitos são os cases atribuídos aos modernos e ágeis frameworks para desenvolvimento web.<span id="more-617"></span></p><p>O famoso padrão MVC (model view controller) encaixa-se como uma luva para aplicações web, e é melhor aproveitado quando explorado por um framework. Frameworks implementam o padrão MVC de forma realmente padronizada (o que às vezes varia, é a nomenclatura, como no <a title="Posts sobre Django" href="http://www.franciscosouza.com.br/tag/django">Django</a>), explorando ao máximo o padrão MVC.</p><p>Existem inúmeros benefícios alcançados ao utilizar frameworks, como o ganho de produtividade, a redução da possibilidade de erros (produtividade, de novo), maior nível de abstração (produtividade?), compatibilidade e integração entre aplicações (produtividade!), desenvolvimento de forma mais segura e prazerosa :) Além destes benefícios, há a vantagem de contar com o apoio da comunidade, já que os frameworks são usados em larga escala e geralmente são comunitários.</p><p>Contra estes benefícios, existem poucas desvantagens e muitos mitos. Em grande parte das discussões que envolvem o uso de frameworks, percebe-se no time “do contra” muito preconceito, argumentos recheados de mitos e, por vezes, confusões e conclusões baseadas em frustrações passadas. Alguns dos mitos e confusões mais comuns são:</p><ul><li><strong>Frameworks amputam o desenvolvedor</strong>. Algumas pessoas defendem que ao usarmos frameworks passamos mais tempo configurando do que programando. Em alguns casos, tais pessoas têm razão (experimente fazer algo bem simples em frameworks que não trazem convention over configuration), mas, na maioria dos casos, elas estão incorretas. Assim, defender o não-uso de frameworks argumentando que o desenvolvedor crescerá mais pessoal e profissionalmente fazendo tudo “na mão” é algo bastante errado, até por que os frameworks atuam principalmente nas partes mais chatas. Não é fazendo validação de formulário e tratamento de requisições com type cast que alguém se torna um excelente programador;</li><li><strong>Não é boa ideia usar os frameworks modernos, pois os antigos eram muito complexos</strong>. “Eu tenho um trauma com frameworks, usei isso um tempo atrás e não deu certo”. Preconceito discriminatório não é bom em informática nem em nenhum outro lugar. Hoje em dia, os frameworks são muito mais modernos e ágeis do que as ferramentas de anos atrás. Não faz sentido algum julgar uma ferramenta que você não conhece baseando em uma que você conhece (e não gosta), não da pra justificar a recusa do VRaptor 3 baseando-se numa má experiência com o Struts 1.1 em 2003, ou de um dos vários frameworks Python modernos por que em 2002 você testou o Zope e achou ele complicado;</li><li><strong>Frameworks provocam perda de desempenho</strong>. Sim, provocam, mas isso geralmente é imperceptível. Mesmo que haja uma gigante e complexa estrutura (o que é incomum nos frameworks mais modernos), a interpretação de uma linguagem de templates e mais algumas firulas que fazem com que se perca desempenho, dificilmente o usuário final perceberá a diferença. Além disso, os frameworks também fornecem recursos como cache em memória para que o desempenho com eles seja melhor do que sem eles;</li><li><strong>Se um framework tiver uma falha de segurança, minha aplicação fica vulneráve</strong>l. Sim, é verdade, mas frameworks raramente têm falhas de segurança (pesquise no Google sobre falhas de segurança em frameworks). Frameworks bons são testados, utilizados e desenvolvidos massivamente; e costumam ter grande parte do código coberta por testes, o que garante um alto índice de segurança e pequena possibilidade de falha.</li><li><strong>Usar framework? Eu não gosto de WordPress nem Joomla, são pesados!</strong> Por mais incrível que possa parecer, existem profissionais que atuam com desenvolvimento web e não sabem ao certo o que é framework. Esta falta de conhecimento é comum em alguns profissionais totalmente técnicos (geralmente 100% auto-didatas), desprovidos de alguns conceitos fundamentais. É um caso bem difícil de solucionar, mas o incentivo da comunidade e a exigência do mercado de um nível maior de conhecimento de conceitos pode, a longo prazo, solucionar este problema.</li></ul><p>Hoje em dia, dificilmente alguém consegue levantar argumentos válidos contra o uso de frameworks web. Cases grandes como Twitter, SourceForge e BitTorrent.com mostram que os “peixes grandes” acreditam e, consequentemente, investem em frameworks web.</p><p>Frameworks web são utilizados em larga escala. Não é moda, é uma necessidade comprovada pela comunidade e pelo meio corporativo, e alguns mitos têm que ser derrubados.</p> ]]></content:encoded> <wfw:commentRss>http://www.franciscosouza.com.br/2010/01/22/uso-de-frameworks-para-desenvolvimento-web-e-mitos-que-ja-deveriam-ter-desaparecido/feed/</wfw:commentRss> <slash:comments>4</slash:comments> </item> <item><title>Aplicações RESTful no Django</title><link>http://www.franciscosouza.com.br/2009/12/30/aplicacoes-restful-no-django/</link> <comments>http://www.franciscosouza.com.br/2009/12/30/aplicacoes-restful-no-django/#comments</comments> <pubDate>Wed, 30 Dec 2009 14:32:14 +0000</pubDate> <dc:creator>Francisco Souza</dc:creator> <category><![CDATA[python]]></category> <category><![CDATA[django]]></category> <category><![CDATA[piston]]></category> <category><![CDATA[REST]]></category><guid isPermaLink="false">http://www.franciscosouza.net/?p=572</guid> <description><![CDATA[O Django é um framework moderno, com muitos recursos "out of box" e módulos inclusos para fazer 54131998944535 coisas. Porém, diferente de frameworks como Pylons e VRaptor, o Django não possui no seu conjunto de módulos uma API RESTful prontinha pra usar, sem complicações. O que a comunidade geralmente faz nestes casos? Desenvolve a API que falta. Por interesse, curiosidade e aprendizado, fui atrás de algumas dessas APIs, para efetuar alguns testes.]]></description> <content:encoded><![CDATA[<p>O <a title="Posts sobre Django" href="http://www.franciscosouza.com.br/tag/django/">Django</a> é um framework moderno, com muitos recursos &#8220;out of box&#8221; e <a title="Referência de módulos do Django" rel="nofollow" href="http://docs.djangoproject.com/en/1.1/modindex/" target="_blank">módulos inclusos para fazer 54131998944535 coisas</a>. Porém, diferente de frameworks como <a title="Posts sobre Pylons" rel="nofollow" href="http://www.franciscosouza.com.br/tag/pylons/" target="_self">Pylons</a> e <a rel="nofollow" href="http://vraptor.caelum.com.br" target="_blank">VRaptor</a>, o Django não possui no seu conjunto de módulos uma API RESTful prontinha pra usar, sem complicações. O que a comunidade geralmente faz nestes casos? Desenvolve a API que falta. Por interesse, curiosidade e aprendizado, fui atrás de algumas dessas APIs, para efetuar alguns testes.<span id="more-572"></span>Vasculhando pela internet, encontrei diversos artigos e projetos sobre o assunto. O básico do básico seria a definição de uma classe, que deveria ser estendida para a criação de um resource. Veja o código da classe, retirado do <a rel="nofollow" href="http://www.djangosnippets.org/snippets/1071/" target="_blank">Django snippets</a>:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">class</span> RestView<span style="color: black;">&#40;</span><span style="color: #008000;">object</span><span style="color: black;">&#41;</span>:<br /> <span style="color: #483d8b;">&quot;&quot;&quot;<br /> Subclass this and add GET / POST / etc methods.<br /> &quot;&quot;&quot;</span><br /> allowed_methods <span style="color: #66cc66;">=</span> <span style="color: black;">&#40;</span><span style="color: #483d8b;">'GET'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'PUT'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'POST'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'DELETE'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'HEAD'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'OPTIONS'</span><span style="color: black;">&#41;</span><br /> <br /> <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__call__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> request<span style="color: #66cc66;">,</span> *args<span style="color: #66cc66;">,</span> **kwargs<span style="color: black;">&#41;</span>:<br /> method <span style="color: #66cc66;">=</span> nonalpha_re.<span style="color: black;">sub</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">''</span><span style="color: #66cc66;">,</span> request.<span style="color: black;">method</span>.<span style="color: black;">upper</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br /> <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> method <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">self</span>.<span style="color: black;">allowed_methods</span> <span style="color: #ff7700;font-weight:bold;">or</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #008000;">hasattr</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> method<span style="color: black;">&#41;</span>:<br /> <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.<span style="color: black;">method_not_allowed</span><span style="color: black;">&#40;</span>method<span style="color: black;">&#41;</span><br /> <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">getattr</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> method<span style="color: black;">&#41;</span><span style="color: black;">&#40;</span>request<span style="color: #66cc66;">,</span> *args<span style="color: #66cc66;">,</span> **kwargs<span style="color: black;">&#41;</span><br /> <br /> <span style="color: #ff7700;font-weight:bold;">def</span> method_not_allowed<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> method<span style="color: black;">&#41;</span>:<br /> response <span style="color: #66cc66;">=</span> HttpResponse<span style="color: black;">&#40;</span><span style="color: #483d8b;">'Method not allowed: %s'</span> % method<span style="color: black;">&#41;</span><br /> response.<span style="color: black;">status_code</span> <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">405</span><br /> <span style="color: #ff7700;font-weight:bold;">return</span> response</div></div><p>Com um pouco de introspecção (bem pouco), o método <em>__call__</em> analisa o objeto <em>request</em> e faz a chamada a um método (do objeto <em>RestView</em>) de acordo com o método da requisição (HTTP, aqueles verbos legais, tipo <em>PUT, POST, GET, DELETE</em>). Basicamente, um <em>resource</em> seria uma classe descendente de RestView. Na nossa classe resource, implementaríamos um método chamado <em>GET</em> para tratar as requisições <em>GET</em>, um método <em>POST</em> para tratar as requisições <em>POST</em>, e assim sucessivamente. Veja um exemplo de <em>resource</em></p><p>:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">class</span> UserView<span style="color: black;">&#40;</span>RestView<span style="color: black;">&#41;</span>:<br /> <span style="color: #ff7700;font-weight:bold;">def</span> GET<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> request<span style="color: #66cc66;">,</span> user_id<span style="color: black;">&#41;</span>:<br /> <span style="color: #483d8b;">'''Este método obtém um usuário e renderiza um template passando tal usuário como parâmetro'''</span><br /> <span style="color: #dc143c;">user</span> <span style="color: #66cc66;">=</span> get_object_or_404<span style="color: black;">&#40;</span>User<span style="color: #66cc66;">,</span> pk <span style="color: #66cc66;">=</span> user_id<span style="color: black;">&#41;</span><br /> <span style="color: #ff7700;font-weight:bold;">return</span> render_to_response<span style="color: black;">&#40;</span><span style="color: #483d8b;">'ver_usuario.html'</span><span style="color: #66cc66;">,</span> <span style="color: #008000;">locals</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> context_instance <span style="color: #66cc66;">=</span> RequestContext<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br /> <br /> <span style="color: #ff7700;font-weight:bold;">def</span> PUT<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> request<span style="color: #66cc66;">,</span> user_id<span style="color: black;">&#41;</span>:<br /> <span style="color: #483d8b;">'''Este método atualiza um usuário no banco de dados'''</span><br /> <span style="color: #dc143c;">user</span> <span style="color: #66cc66;">=</span> get_object_or_404<span style="color: black;">&#40;</span>User<span style="color: #66cc66;">,</span> pk <span style="color: #66cc66;">=</span> user_id<span style="color: black;">&#41;</span><br /> <span style="color: #dc143c;">user</span>.<span style="color: black;">name</span> <span style="color: #66cc66;">=</span> request.<span style="color: black;">POST</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'user_name'</span><span style="color: black;">&#93;</span><br /> <span style="color: #dc143c;">user</span>.<span style="color: black;">save</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br /> <span style="color: #ff7700;font-weight:bold;">return</span> HttpResponseRedirect<span style="color: black;">&#40;</span>reverse<span style="color: black;">&#40;</span><span style="color: #483d8b;">'users.views.list_users'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></div></div><p>Assim, poderíamos fazer o seguinte mapeamento no <em>urls.py</em>, para a classe acima:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">urlpatterns <span style="color: #66cc66;">=</span> patterns<span style="color: black;">&#40;</span><span style="color: #483d8b;">''</span><span style="color: #66cc66;">,</span><br /> ...<br /> <span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'^user/(<span style="color: #000099; font-weight: bold;">\d</span>+)/$'</span><span style="color: #66cc66;">,</span> UserView<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br /> ...<br /> <span style="color: black;">&#41;</span></div></div><p>Note que um objeto é passado por parâmetro, e não a classe em si.  Esta solução funciona, pelo menos para trabalhar com diferentes tipos de requisição a uma mesma URL. Mas e no caso de queremos explorar mais a REST, com uso de tecnologias como JSON e <a title="Posts sobre XML" href="http://www.franciscosouza.com.br/tag/xml/">XML</a>? Certamente, nossa classe <em>RestView</em> não é capaz de nos atender, seria necessário estendê-la, ou simplesmente procurar uma API completa para trabalhar com REST no Django. Existem duas ferramentas muito boas aqui: <a href="http://code.google.com/p/django-rest/" target="_blank">django-rest</a> e <a href="http://bitbucket.org/jespern/django-piston/" target="_blank">django-piston</a>, sendo a segunda uma incrível e completa ferramenta para trabalhar com diversos recursos, como integração com os <em>models </em>e <em>forms </em>do Django; autenticação plugável (incluindo uma interface transparente para trabalhar com <a href="http://oauth.net/" target="_blank">OAuth</a>. Pelas características, vemos que o Piston é muito mais do que uma API RESTful, é uma ferramenta indispensável ao desenvolvedor Django :)  Apesar de toda a empolgação com o Piston, vamos deixar ele parar depois. Antes, vamos ao django-rest. Trata-se de uma interface RESTful, quem fornece integração com os <em>models</em> e também algumas <em>generic views</em> inclusas no pacote, facilitando a criação de CRUDs, por exemplo. Existem ainda outros projetos interessantes, como o <a rel="nofollow" href="http://code.google.com/p/django-restful-model-views/" target="_blank">django-restful-models-views</a>, que é uma aplicação plugável, mas vou abordar aqui apenas as ferramentas citadas no parágrafo anterior.  Basicamente, o django-rest trabalha de duas formas: &#8220;semi-automático&#8221;, onde fornecemos o modelo e ele &#8220;se vira&#8221; (semelhante às <em>generic views</em>); ou de forma manual, onde configuramos os <em>resources</em> manualmente, muito comum quando não estamos trabalhando restritamente com <em>models</em>. Um exemplo de código ligado ao model <em>User</em>, que possui os atributos <em>name </em>e <em>age</em>, seria o seguinte (arquivo <em>urls.py</em></p><p>):</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">conf</span>.<span style="color: black;">urls</span>.<span style="color: black;">defaults</span> <span style="color: #ff7700;font-weight:bold;">import</span> *<br /> <span style="color: #ff7700;font-weight:bold;">from</span> django_restapi.<span style="color: black;">model_resource</span> <span style="color: #ff7700;font-weight:bold;">import</span> Collection<br /> <span style="color: #ff7700;font-weight:bold;">from</span> django_restapi.<span style="color: black;">responder</span> <span style="color: #ff7700;font-weight:bold;">import</span> TemplateResponder<br /> <span style="color: #ff7700;font-weight:bold;">from</span> users.<span style="color: black;">models</span> <span style="color: #ff7700;font-weight:bold;">import</span> User<br /> <br /> user_resource <span style="color: #66cc66;">=</span> Collection<span style="color: black;">&#40;</span><br /> queryset <span style="color: #66cc66;">=</span> User.<span style="color: black;">objects</span>.<span style="color: #008000;">all</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br /> <span style="display:block;background-color:#ffff66">permitted_methods <span style="color: #66cc66;">=</span> <span style="color: black;">&#40;</span><span style="color: #483d8b;">'GET'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'POST'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'PUT'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'DELETE'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br /></span>responder <span style="color: #66cc66;">=</span> TemplateResponder<span style="color: black;">&#40;</span><br /> template_dir <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'users'</span><span style="color: #66cc66;">,</span><br /> template_object_name <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'my_user'</span><span style="color: #66cc66;">,</span><br /> <span style="color: black;">&#41;</span><br /> <span style="color: black;">&#41;</span><br /> <br /> urlpatterns <span style="color: #66cc66;">=</span> patterns<span style="color: black;">&#40;</span><span style="color: #483d8b;">''</span><span style="color: #66cc66;">,</span><br /> url<span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'^users/(.*?)/?$'</span><span style="color: #66cc66;">,</span> user_resource<span style="color: #66cc66;">,</span> <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'user_detail'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br /> <span style="color: black;">&#41;</span></div></div><p>O objeto <em>user_resource</em> armazena as configurações do nosso <em>resource</em>. Note o atributo <em>responder</em>, que armazena o tipo de resposta. Aqui, poderíamos utilizar além de <em>TemplateResponder</em>, <em>JSONResponder</em>, <em>XMLResponder</em>, dentre outros tipos de retorno. Na linha 8, definimos os métodos que serão permitidos. Assim, este <em>resource</em> responderá pelas seguintes requisições:</p><ul><li>GET /users: Uma lista com todos os usuários;</li><li>GET /users/&lt;id&gt;: Visualização dos detalhes de um usuário;</li><li>POST /users: Cria um usuário;</li><li>PUT /users/&lt;id&gt;: Atualiza os dados de um usuário;</li><li>DELETE /users/&lt;id&gt;: Apaga um usuário.</li></ul><p>A documentação do <em>django-rest</em> é bem pobre. Para entender alguns comportamentos básicos, tive que olhar o código fonte da ferramenta. Por isso, vamos agora para a parte mais legal e interessante: <em>django-piston</em>.</p><p>Diferente do <em>django-rest</em>, o <em>django-piston</em> possui <a title="Documentação do django-piston" href="http://bitbucket.org/jespern/django-piston/wiki/Home" target="_blank">excelente documentação</a>, e também uma <a title="Grupo de discussão django-piston" href="http://groups.google.com/group/django-piston" target="_blank">comunidade ativa</a>, sendo a melhor alternativa quando se trata de API RESTful. Vamos rapidamente ver como tratar o exemplo com o model <em>User</em> descrito acima. É importante notar que o <em>django-piston</em> é bastante aconselhável para a criação de APIs. Assim, ele fornece MUITA coisa para trabalhar com APIs, é totalmente voltado para isto. Vamos distorcê-lo um pouco e fazer com que ele transfira dados para os templates.</p><p>Precisamos criar um <em>resource</em>, e para a criação de um <em>resource</em> é necessário que antes seja criado um <em>handler</em>. Os <em>handlers</em> são utilizados pelos <em>resource</em> para tratar as requisições. Um <em>handler </em>é um objeto modelado pela classe <em>BaseHandler</em> (por uma descendente de <em>BaseHandler</em>, para ser mais exato). Veja um exemplo de <em>handler</em>:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">class</span> UserHandler<span style="color: black;">&#40;</span>BaseHandler<span style="color: black;">&#41;</span>:<br /> allowed_methods <span style="color: #66cc66;">=</span> <span style="color: black;">&#40;</span><span style="color: #483d8b;">'GET'</span><span style="color: #66cc66;">,</span><span style="color: black;">&#41;</span><br /> model <span style="color: #66cc66;">=</span> User<br /> <br /> <span style="color: #ff7700;font-weight:bold;">def</span> read<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> request<span style="color: #66cc66;">,</span> user_id <span style="color: #66cc66;">=</span> <span style="color: #008000;">None</span><span style="color: black;">&#41;</span>:<br /> <span style="color: #ff7700;font-weight:bold;">if</span> user_id <span style="color: #ff7700;font-weight:bold;">is</span> <span style="color: #008000;">None</span>:<br /> users <span style="color: #66cc66;">=</span> User.<span style="color: black;">objects</span>.<span style="color: #008000;">all</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br /> template <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'user_list.html'</span><br /> <span style="color: #ff7700;font-weight:bold;">else</span>:<br /> <span style="color: #dc143c;">user</span> <span style="color: #66cc66;">=</span> User.<span style="color: black;">objects</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span>pk <span style="color: #66cc66;">=</span> user_id<span style="color: black;">&#41;</span><br /> template <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'user_detail.html'</span><br /> template <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'users/%s'</span> % template<br /> <span style="color: #ff7700;font-weight:bold;">return</span> render_to_response<span style="color: black;">&#40;</span>template<span style="color: #66cc66;">,</span> <span style="color: #008000;">locals</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> context_instance <span style="color: #66cc66;">=</span> RequestContext<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></div></div><p>Este handler é salvo em um arquivo chamado <em>handlers.py</em>, dentro do diretório de nossa aplicação. Agora, basta adicionar o mapeamento da URL e um <em>resource</em> baseado no nosso <em>UserHandler</em> passa a tratar as requisições, de forma RESTful. Veja como ficaria o <em>urls.py</em>:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">conf</span>.<span style="color: black;">urls</span>.<span style="color: black;">defaults</span> <span style="color: #ff7700;font-weight:bold;">import</span> *<br /> <span style="color: #ff7700;font-weight:bold;">from</span> piston.<span style="color: #dc143c;">resource</span> <span style="color: #ff7700;font-weight:bold;">import</span> Resource<br /> <span style="color: #ff7700;font-weight:bold;">from</span> users.<span style="color: black;">handlers</span> <span style="color: #ff7700;font-weight:bold;">import</span> UserHandler<br /> <br /> user_resource <span style="color: #66cc66;">=</span> Resource<span style="color: black;">&#40;</span>handler <span style="color: #66cc66;">=</span> UserHandler<span style="color: black;">&#41;</span><br /> <br /> urlpatterns <span style="color: #66cc66;">=</span> patterns<span style="color: black;">&#40;</span><span style="color: #483d8b;">''</span><span style="color: #66cc66;">,</span><br /> url<span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'users/(?P&amp;lt;user_id&amp;gt;<span style="color: #000099; font-weight: bold;">\d</span>*)/$'</span><span style="color: #66cc66;">,</span> user_resource<span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br /> <span style="color: black;">&#41;</span></div></div><p>O <em>django-piston</em> é uma ferramenta incrível, com muitos recursos, que provavelmente voltará a aparecer aqui no blog. Recomendo que mergulhe nela e veja como é simples criar APIs RESTful usando Django. Em breve eu falo mais sobre <em>django-piston</em> focando no desenvolvimento de APIs :)</p> ]]></content:encoded> <wfw:commentRss>http://www.franciscosouza.com.br/2009/12/30/aplicacoes-restful-no-django/feed/</wfw:commentRss> <slash:comments>7</slash:comments> </item> <item><title>Construindo um CRUD com frameworks Python, Parte IV: Django Remake</title><link>http://www.franciscosouza.com.br/2009/12/23/construindo-um-crud-com-frameworks-python-parte-iv-django-remake/</link> <comments>http://www.franciscosouza.com.br/2009/12/23/construindo-um-crud-com-frameworks-python-parte-iv-django-remake/#comments</comments> <pubDate>Wed, 23 Dec 2009 11:40:11 +0000</pubDate> <dc:creator>Francisco Souza</dc:creator> <category><![CDATA[desenvolvimento de softwares]]></category> <category><![CDATA[python]]></category> <category><![CDATA[django]]></category> <category><![CDATA[frameworks]]></category><guid isPermaLink="false">http://www.franciscosouza.net/?p=547</guid> <description><![CDATA[Na segunda parte desta sequência de tutoriais sobre frameworks Python, eu apresentei a criação do CRUD em Django, fazendo "tudo na mão". Fui intimado recomendado por alguns membros da comunidade Django Brasil a usar as generic views, interessante recurso do framework que permite trabalhar de forma muito mais rápida para geração de tarefas comuns, como CRUDs. Vamos então ao uso das generic views...]]></description> <content:encoded><![CDATA[<p>Na <a title="Construindo um CRUD com frameworks Python, Parte II: Django" href="http://www.franciscosouza.com.br/2009/12/16/construindo-um-crud-com-frameworks-python-parte-ii-django/" target="_blank">segunda parte desta sequência de tutoriais sobre frameworks Python</a>, eu apresentei a criação do CRUD em <a title="Posts sobre Django" href="http://www.franciscosouza.com.br/tag/django/">Django</a>, fazendo &#8220;tudo na mão&#8221;. Fui <span style="text-decoration: line-through;">intimado </span>recomendado por alguns membros da comunidade <a title="Comunidade Django Brasil" rel="nofollow" href="http://www.djangobrasil.org" target="_blank">Django Brasil</a> a usar as <em>generic views</em>, interessante recurso do framework que permite trabalhar de forma muito mais rápida para geração de tarefas comuns, como CRUDs. Vamos então ao uso das <em>generic views</em>&#8230;<span id="more-547"></span>Ao invés de aproveitar o antigo projeto, para provar que a coisa realmente é rápida, vamos começar do zero, com um outro exemplo (quase igual). Trata-se de um sistema acadêmico para a Faculdade ABCDEF. Assim, vamos criar o projeto abcdef_academico com o comando:</p><pre>$ django-admin.py startproject abcdef_academico</pre><p>Após executarmos este comando, acessamos o diretório abcdef_academico, para criar a nossa aplicação, que chamaremos de <em>alunos</em>:</p><pre>$ django-admin.py startapp alunos</pre><p>Como já aprendemos, agora que criamos nossa aplicação e nosso projeto, devemos criar os models. No nosso caso, a classe aluno vai ter apenas os campos <em>nome</em> e <em>idade</em>, igualzinho era com a classe Usuario no exemplo anterior. Assim, o código <a title="Posts sobre Python" href="http://www.franciscosouza.com.br/tag/python/">Python</a> do <em>models.py </em>fica da seguinte forma:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">db</span> <span style="color: #ff7700;font-weight:bold;">import</span> models<br /> <br /> <span style="color: #808080; font-style: italic;"># Create your models here.</span><br /> <span style="color: #ff7700;font-weight:bold;">class</span> Aluno<span style="color: black;">&#40;</span>models.<span style="color: black;">Model</span><span style="color: black;">&#41;</span>:<br /> nome <span style="color: #66cc66;">=</span> models.<span style="color: black;">CharField</span><span style="color: black;">&#40;</span>max_length <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">100</span><span style="color: black;">&#41;</span><br /> idade <span style="color: #66cc66;">=</span> models.<span style="color: black;">IntegerField</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></div><p>Como vamos trabalhar com <em>generic views</em>, não vamos precisar criar o ModelForm. Passaremos para a view apenas o nosso model e ela vai se virar para gerenciar tudo, legal né?! =) Vamos apenas acertar o arquivo settings.py, com as mesmas configurações do <em>settings.py </em>do post anterior, sem as generic views. Para utilizar as generic views, nada muda no <em>settings.py</em>.</p><p>Agora podemos começar a trabalhar aonde a coisa acontece: no arquivo <em>urls.py</em>. Lá que definimos que vamos usar uma view genérica, mapeando uma URL para uma view genérica, que recebe como parâmetro algumas informações. Vamos primeiro criar o formulário de cadastro de um novo aluno. Para isto, adicione as duas linhas de import ao início do arquivo:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">views</span>.<span style="color: black;">generic</span> <span style="color: #ff7700;font-weight:bold;">import</span> create_update<br /> <span style="color: #ff7700;font-weight:bold;">from</span> alunos.<span style="color: black;">models</span> <span style="color: #ff7700;font-weight:bold;">import</span> Aluno</div></div><p>Em seguida, configure um dicionário, chamado <em>params_novo_aluno</em>, que contém todos os parâmetros que a generic view <em>django.views.generic.create_update.create_object</em> (que vamos utilizar) necessita. Este dicionário, fica desta forma:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">params_novo_aluno <span style="color: #66cc66;">=</span> <span style="color: black;">&#123;</span><br /> <span style="color: #483d8b;">'model'</span> : Aluno<span style="color: #66cc66;">,</span><br /> <span style="color: #483d8b;">'post_save_redirect'</span> : <span style="color: #483d8b;">'/alunos/'</span><span style="color: #66cc66;">,</span><br /> <span style="color: black;">&#125;</span></div></div><p>O primeiro parâmetro, chamado <em>model, </em>refere-se ao modelo para o qual será gerado o formulário, a validação e a inserção. Já o segundo parâmetro, <em>post_save_redirect</em>, indica para onde o usuário será redirecionado após o objeto ser salvo no banco de dados. A URL <em>/alunos/</em> será responsável por listar todos os alunos cadastrados.</p><p>Ainda no arquivo urls.py, falta apenas definir a URL pela qual será acessado o tal formulário. Vamos adotar o padrão <em>/alunos/novo</em>, assim, adicionamos à tupla <em>urlpatterns</em> o seguinte item:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'alunos/novo/$'</span><span style="color: #66cc66;">,</span> create_update.<span style="color: black;">create_object</span><span style="color: #66cc66;">,</span> params_novo_aluno<span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'novo_aluno'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span></div></div><p>Desta forma, já temos o mapeamento. Falta apenas o template! Note que em momento algum especificamos qual template utilizaremos. Isto ocorre por que este <em>generic view</em> padroniza o nome do template (mas é possível alterar, passando o parâmetro <em>template_name</em>). Desta forma, o padrão é que o arquivo de template se chame <em>_form.html</em> e fique dentro de um diretório com o nome da aplicação, no diretório de templates. No nosso caso, teremos então<em> templates/alunos/aluno_form.html</em>. O código do template fica bem simples também:</p><div class="codecolorer-container html4strict default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="html4strict codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">{% extends &quot;base.html&quot; %}<br /> <br /> {% block &quot;titulo&quot; %}Cadastro de novo aluno - {{ block.super }}{% endblock &quot;titulo&quot; %}<br /> <br /> {% block &quot;conteudo&quot; %}<br /> <br /> <span style="color: #009900;">&lt;<a href="http://december.com/html/4/element/form.html"><span style="color: #000000; font-weight: bold;">form</span></a> <span style="color: #000066;">method</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;post&quot;</span>&gt;</span> {{ form.as_table }}<span style="color: #ddbb00;">&amp;nbsp;</span><br /> <span style="color: #009900;">&lt;<a href="http://december.com/html/4/element/table.html"><span style="color: #000000; font-weight: bold;">table</span></a> <span style="color: #000066;">border</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;0&quot;</span> <span style="color: #000066;">width</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;60%&quot;</span>&gt;</span><br /> <span style="color: #009900;">&lt;<a href="http://december.com/html/4/element/tbody.html"><span style="color: #000000; font-weight: bold;">tbody</span></a>&gt;</span><br /> <span style="color: #009900;">&lt;<a href="http://december.com/html/4/element/tr.html"><span style="color: #000000; font-weight: bold;">tr</span></a>&gt;</span><br /> <span style="color: #009900;">&lt;<a href="http://december.com/html/4/element/th.html"><span style="color: #000000; font-weight: bold;">th</span></a>&gt;&lt;<span style="color: #66cc66;">/</span><a href="http://december.com/html/4/element/th.html"><span style="color: #000000; font-weight: bold;">th</span></a>&gt;</span><br /> <span style="color: #009900;">&lt;<a href="http://december.com/html/4/element/td.html"><span style="color: #000000; font-weight: bold;">td</span></a>&gt;&lt;<a href="http://december.com/html/4/element/input.html"><span style="color: #000000; font-weight: bold;">input</span></a> <span style="color: #000066;">type</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;submit&quot;</span> <span style="color: #000066;">value</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;Cadastrar!&quot;</span> <span style="color: #66cc66;">/</span>&gt;&lt;<span style="color: #66cc66;">/</span><a href="http://december.com/html/4/element/td.html"><span style="color: #000000; font-weight: bold;">td</span></a>&gt;</span><br /> <span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><a href="http://december.com/html/4/element/tr.html"><span style="color: #000000; font-weight: bold;">tr</span></a>&gt;</span><br /> <span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><a href="http://december.com/html/4/element/tbody.html"><span style="color: #000000; font-weight: bold;">tbody</span></a>&gt;</span><br /> <span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><a href="http://december.com/html/4/element/table.html"><span style="color: #000000; font-weight: bold;">table</span></a>&gt;</span><br /> <span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><a href="http://december.com/html/4/element/form.html"><span style="color: #000000; font-weight: bold;">form</span></a>&gt;</span>{% endblock &quot;conteudo&quot; %}</div></div><p>Agora que já temos nosso formulário de cadastro funcionando, podemos passar para a página de leitura, o R do CRUD. Vamos criar a página com listagem de todos os alunos e a página de visualização dos detalhes de um aluno. Para isto, utilizamos outro outro conjunto de <em>generic views</em>, incluso no módulo<em>django.views.generic.list_detail</em>. Adicione, então, o seguinte import ao início do arquivo <em>urls.py</em>:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">views</span>.<span style="color: black;">generic</span> <span style="color: #ff7700;font-weight:bold;">import</span> list_detail</div></div><p>E criamos um novo pattern, relativo à URL /alunos, que mostrará uma lista de alunos. Então, fica a seguinte entrada no <em>urls.py</em>:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'alunos/$'</span><span style="color: #66cc66;">,</span> list_detail.<span style="color: black;">object_list</span><span style="color: #66cc66;">,</span> <span style="color: black;">&#123;</span> <span style="color: #483d8b;">'queryset'</span> : Aluno.<span style="color: black;">objects</span>.<span style="color: #008000;">all</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> <span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'lista_alunos'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span></div></div><p>No caso desta generic view, o nome padrão para o template é <em>&lt;model&gt;_list.html</em>. Novamente, construímos nosso template, que fica no caminho <em>templates/alunos/aluno_list.html</em>:</p><div class="codecolorer-container html4strict default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="html4strict codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">{% extends &quot;base.html&quot; %}<br /> <br /> {% block &quot;titulo&quot; %}Listagem de alunos - {{ block.super }}{% endblock &quot;titulo&quot; %}<br /> <br /> {% block &quot;conteudo&quot; %}<br /> <span style="color: #009900;">&lt;<a href="http://december.com/html/4/element/ul.html"><span style="color: #000000; font-weight: bold;">ul</span></a>&gt;</span> {% for aluno in object_list %}<span style="color: #ddbb00;">&amp;nbsp;</span><br /> &nbsp; &nbsp; <span style="color: #009900;">&lt;<a href="http://december.com/html/4/element/li.html"><span style="color: #000000; font-weight: bold;">li</span></a>&gt;&lt;<a href="http://december.com/html/4/element/a.html"><span style="color: #000000; font-weight: bold;">a</span></a> <span style="color: #000066;">href</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;{% url ver_aluno aluno.id %}&quot;</span>&gt;</span>{{ aluno.nome }}<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><a href="http://december.com/html/4/element/a.html"><span style="color: #000000; font-weight: bold;">a</span></a>&gt;&lt;<span style="color: #66cc66;">/</span><a href="http://december.com/html/4/element/li.html"><span style="color: #000000; font-weight: bold;">li</span></a>&gt;</span><br /> {% endfor %}<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><a href="http://december.com/html/4/element/ul.html"><span style="color: #000000; font-weight: bold;">ul</span></a>&gt;</span><br /> <span style="color: #009900;">&lt;<a href="http://december.com/html/4/element/a.html"><span style="color: #000000; font-weight: bold;">a</span></a> <span style="color: #000066;">href</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;{% url novo_aluno %}&quot;</span>&gt;</span>Cadastrar novo aluno<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><a href="http://december.com/html/4/element/a.html"><span style="color: #000000; font-weight: bold;">a</span></a>&gt;</span><br /> <br /> {% endblock &quot;conteudo&quot; %}</div></div><p>Agora que temos a listagem de alunos pronta, vamos visualizar os detalhes de cada aluno. Para isto, utilizaremos a generic view <em>django.views.generic.list_detail.object_detail</em>. Esta view também recebe como parâmetro um queryset do Django, além de um id, que vem como parâmetro na URL, chamado <em>object_id</em>. Esta view também está capacitada a trabalhar com slugs. Assim, teremos nossa view sendo acessada por este endereço: <em>/alunos/&lt;object_id&gt;</em>. Para fazer isso, bastam apenas dois passos: adicionar um pattern ao <em>urls.py</em> e criar o template <em>aluno_detail.html</em> dentro do subdiretório <em>alunos</em> no diretório de templates. O mapeamento do pattern fica da seguinte forma:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'alunos/(?P&amp;lt;object_id&amp;gt;<span style="color: #000099; font-weight: bold;">\d</span>+)/$'</span><span style="color: #66cc66;">,</span> list_detail.<span style="color: black;">object_detail</span><span style="color: #66cc66;">,</span> <span style="color: black;">&#123;</span> <span style="color: #483d8b;">'queryset'</span> : Aluno.<span style="color: black;">objects</span>.<span style="color: #008000;">all</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> <span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'ver_aluno'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span></div></div><p>É importante que o parâmetro da URL se chame <em>object_id</em>, pois é o nome que a view recebe. O template fica também bem simples de entender:</p><div class="codecolorer-container html4strict default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="html4strict codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">{% extends &quot;base.html&quot; %}<br /> <br /> {% block &quot;titulo&quot; %}Detalhes do aluno {{ object.nome }} - {{ block.super }}{% endblock &quot;titulo&quot; %}<br /> <br /> {% block &quot;conteudo&quot; %}<br /> <span style="color: #009900;">&lt;<a href="http://december.com/html/4/element/table.html"><span style="color: #000000; font-weight: bold;">table</span></a> <span style="color: #000066;">border</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;0&quot;</span> <span style="color: #000066;">width</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;60%&quot;</span>&gt;</span><br /> <span style="color: #009900;">&lt;<a href="http://december.com/html/4/element/tbody.html"><span style="color: #000000; font-weight: bold;">tbody</span></a>&gt;</span><br /> <span style="color: #009900;">&lt;<a href="http://december.com/html/4/element/tr.html"><span style="color: #000000; font-weight: bold;">tr</span></a>&gt;</span><br /> <span style="color: #009900;">&lt;<a href="http://december.com/html/4/element/th.html"><span style="color: #000000; font-weight: bold;">th</span></a>&gt;</span>Nome:<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><a href="http://december.com/html/4/element/th.html"><span style="color: #000000; font-weight: bold;">th</span></a>&gt;</span><br /> <span style="color: #009900;">&lt;<a href="http://december.com/html/4/element/td.html"><span style="color: #000000; font-weight: bold;">td</span></a>&gt;</span>{{ object.nome }}<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><a href="http://december.com/html/4/element/td.html"><span style="color: #000000; font-weight: bold;">td</span></a>&gt;</span><br /> <span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><a href="http://december.com/html/4/element/tr.html"><span style="color: #000000; font-weight: bold;">tr</span></a>&gt;</span><br /> <span style="color: #009900;">&lt;<a href="http://december.com/html/4/element/tr.html"><span style="color: #000000; font-weight: bold;">tr</span></a>&gt;</span><br /> <span style="color: #009900;">&lt;<a href="http://december.com/html/4/element/th.html"><span style="color: #000000; font-weight: bold;">th</span></a>&gt;</span>Idade:<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><a href="http://december.com/html/4/element/th.html"><span style="color: #000000; font-weight: bold;">th</span></a>&gt;</span><br /> <span style="color: #009900;">&lt;<a href="http://december.com/html/4/element/td.html"><span style="color: #000000; font-weight: bold;">td</span></a>&gt;</span>{{ object.idade }}<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><a href="http://december.com/html/4/element/td.html"><span style="color: #000000; font-weight: bold;">td</span></a>&gt;</span><br /> <span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><a href="http://december.com/html/4/element/tr.html"><span style="color: #000000; font-weight: bold;">tr</span></a>&gt;</span><br /> <span style="color: #009900;">&lt;<a href="http://december.com/html/4/element/tr.html"><span style="color: #000000; font-weight: bold;">tr</span></a>&gt;</span><br /> <span style="color: #009900;">&lt;<a href="http://december.com/html/4/element/th.html"><span style="color: #000000; font-weight: bold;">th</span></a>&gt;</span>Opções:<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><a href="http://december.com/html/4/element/th.html"><span style="color: #000000; font-weight: bold;">th</span></a>&gt;</span><br /> <span style="color: #009900;">&lt;<a href="http://december.com/html/4/element/td.html"><span style="color: #000000; font-weight: bold;">td</span></a>&gt;&lt;<a href="http://december.com/html/4/element/a.html"><span style="color: #000000; font-weight: bold;">a</span></a> <span style="color: #000066;">href</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;{% url editar_aluno object.id %}&quot;</span>&gt;</span>Editar<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><a href="http://december.com/html/4/element/a.html"><span style="color: #000000; font-weight: bold;">a</span></a>&gt;</span> | <span style="color: #009900;">&lt;<a href="http://december.com/html/4/element/a.html"><span style="color: #000000; font-weight: bold;">a</span></a> <span style="color: #000066;">href</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;{% url apagar_aluno object.id %}&quot;</span>&gt;</span>Apagar<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><a href="http://december.com/html/4/element/a.html"><span style="color: #000000; font-weight: bold;">a</span></a>&gt;&lt;<span style="color: #66cc66;">/</span><a href="http://december.com/html/4/element/td.html"><span style="color: #000000; font-weight: bold;">td</span></a>&gt;</span><br /> <span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><a href="http://december.com/html/4/element/tr.html"><span style="color: #000000; font-weight: bold;">tr</span></a>&gt;</span><br /> <span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><a href="http://december.com/html/4/element/tbody.html"><span style="color: #000000; font-weight: bold;">tbody</span></a>&gt;</span><br /> <span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><a href="http://december.com/html/4/element/table.html"><span style="color: #000000; font-weight: bold;">table</span></a>&gt;</span><br /> {% endblock &quot;conteudo&quot; %}</div></div><p>Neste template já criamos a referência para as páginas de edição e remoção do objeto. Para editar, precisamos apenas adicionar a chamada à view <em>django.views.generic.create_update.update_object </em>sem a necessidade de um novo template, pois esta view utiliza por padrão o mesmo template da view <em>create_object</em>. Assim, basta adicionar um novo pattern ao <em>urls.py</em>:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'alunos/(?P<span style="color: #000099; font-weight: bold;">\d</span>+)/editar/$'</span><span style="color: #66cc66;">,</span> create_update.<span style="color: black;">update_object</span><span style="color: #66cc66;">,</span> params_novo_aluno<span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'editar_aluno'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span></div></div><p>E, para finalizar, vamos para a parte de apagar o objeto. O Django trabalha com o padrão de partir para uma página de confirmação, então aqui precisaremos de um template chamado <em>aluno_confirm_delete.html</em>. Antes, mapeamos a URL <em>/alunos/&lt;object_id&gt;/apagar</em> para nossa generic view <em>django.views.generic.delete_object</em> e, em seguida, criamos o template necessário.</p><p>Para seguir o padrão, vou deixar uma parte sem postar código aqui no blog, mas o <a title="Projeto completo do CRUD utilizando generic views" href="http://bitbucket.org/franciscosouza/tutorial-django-remake-crud/" target="_blank">projeto completo está disponível no BitBucket</a>. E essa foi uma rápida revisão da criação de CRUDs usando Django! =)</p> ]]></content:encoded> <wfw:commentRss>http://www.franciscosouza.com.br/2009/12/23/construindo-um-crud-com-frameworks-python-parte-iv-django-remake/feed/</wfw:commentRss> <slash:comments>5</slash:comments> </item> <item><title>Construindo um CRUD com frameworks Python, Parte II: Django</title><link>http://www.franciscosouza.com.br/2009/12/16/construindo-um-crud-com-frameworks-python-parte-ii-django/</link> <comments>http://www.franciscosouza.com.br/2009/12/16/construindo-um-crud-com-frameworks-python-parte-ii-django/#comments</comments> <pubDate>Wed, 16 Dec 2009 11:00:00 +0000</pubDate> <dc:creator>Francisco Souza</dc:creator> <category><![CDATA[desenvolvimento de softwares]]></category> <category><![CDATA[python]]></category> <category><![CDATA[django]]></category> <category><![CDATA[frameworks]]></category><guid isPermaLink="false">http://www.franciscosouza.net/?p=514</guid> <description><![CDATA[O segundo framework a ser abordado na sequência de posts sobre CRUD's usando frameworks Python é o Django, o mais badalado entre os frameworks Python, com comunidade mais ativa. Assim como foi com o Pylons, vamos ver como Django trabalha com URL's, MVC (ou MTV... - veja mais a frente), abstração do banco de dados, etc. Vamos ao mesmo caso: no portal da empresa ABC Informática, desenvolveremos o cadastro de usuários (que, por incrível que pareça, não tem login, senha, essas coisas, apenas nome e idade hehe)...]]></description> <content:encoded><![CDATA[<p>O segundo framework a ser abordado na sequência de posts sobre CRUD&#8217;s usando frameworks Python é o Django, o mais badalado entre os frameworks Python, com comunidade mais ativa. Assim como foi com o Pylons, vamos ver como Django trabalha com URL&#8217;s, MVC (ou MTV&#8230; &#8211; veja mais a frente), abstração do banco de dados, etc. Vamos ao mesmo caso: no portal da empresa ABC Informática, desenvolveremos o cadastro de usuários (que, por incrível que pareça, não tem login, senha, essas coisas, apenas nome e idade hehe)&#8230;<span id="more-514"></span></p><p>A instalação do Django também é bem simples e se da pelo easy_install, assim como no Pylons. Então, basta acessar o terminal e digitar o comando:</p><pre># easy_install -U django</pre><p>O Django fornece a ferramenta de linha de comando django-admin para criação de projetos e aplicações. Projetos Django se dividem em aplicações e é importante <a title="&quot;Desmistificando o conceito de Django Apps&quot;, por Henrique Bastos" href="http://henriquebastos.net/2009/11/13/desmistificando-o-conceito-de-django-apps/" target="_blank">entender o conceito de Django Apps</a>, para evitar o uso inadequado do framework. Vamos chamar nosso projeto de dj_portal_abc, então, para criar o projeto, basta executar o comando:</p><pre>$ django-admin.py startproject dj_portal_abc</pre><p>Será criado o diretório dj_portal_abc, com estrutura semelhante à seguinte:</p><p style="text-align: center;"><img class="aligncenter size-full wp-image-516" title="Estrutura de arquivos de um projeto novo do Django" src="http://www.franciscosouza.com.br/wp-content/uploads/2009/12/estrutura_django.png" alt="Estrutura de arquivos de um projeto novo Django" width="145" height="115" /></p><p>Sim, a estrutura é muito mais simples que a estrutura do Pylons, mas a criança ainda vai crescer :) Agora que criamos o projeto, vamos configurá-lo. Faremos apenas alguns ajustes básicos: o banco de dados utilizado; o idioma padrão e também a timezone padrão. Abra o arquivo settings.py e encontre o seguinte trecho de configuração de banco de dados:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">DATABASE_ENGINE <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">''</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.</span><br /> DATABASE_NAME <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">''</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Or path to database file if using sqlite3.</span><br /> DATABASE_USER <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">''</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Not used with sqlite3.</span><br /> DATABASE_PASSWORD <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">''</span> &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Not used with sqlite3.</span><br /> DATABASE_HOST <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">''</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Set to empty string for localhost. Not used with sqlite3.</span><br /> DATABASE_PORT <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">''</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Set to empty string for default. Not used with sqlite3.</span></div></div><p>Altere a variável <em>DATABASE_ENGINE</em> para sqlite3 e a variável <em>DATABASE_NAME</em> para portal_abc.db, ficando assim (note as linhas em destaque):</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">DATABASE_ENGINE <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'sqlite3'</span><br /> DATABASE_NAME <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'portal_abc.db'</span><br /> DATABASE_USER <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">''</span><br /> DATABASE_PASSWORD <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">''</span><br /> DATABASE_HOST <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">''</span><br /> DATABASE_PORT <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">''</span></div></div><p>Agora vamos alterar a <em>TIME_ZONE</em> para São Paulo e o idioma padrão para português brasileiro. Logo abaixo da configuração do banco de dados, você encontra as seguintes linhas no settings.py:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #808080; font-style: italic;"># Local time zone for this installation. Choices can be found here:</span><br /> <span style="color: #808080; font-style: italic;"># http://en.wikipedia.org/wiki/List_of_tz_zones_by_name</span><br /> <span style="color: #808080; font-style: italic;"># although not all choices may be available on all operating systems.</span><br /> <span style="color: #808080; font-style: italic;"># If running in a Windows environment this must be set to the same as your</span><br /> <span style="color: #808080; font-style: italic;"># system time zone.</span><br /> TIME_ZONE <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'America/Chicago'</span><br /> <br /> <span style="color: #808080; font-style: italic;"># Language code for this installation. All choices can be found here:</span><br /> <span style="color: #808080; font-style: italic;"># http://www.i18nguy.com/unicode/language-identifiers.html</span><br /> LANGUAGE_CODE <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'en-us'</span></div></div><p>Configure a variável <em>TIME_ZONE</em> como America/Sao_Paulo e a variável <em>LANGUAGE_CODE</em> como pt-br, ficando assim:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">TIME_ZONE <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'America/Sao_Paulo'</span><br /> <br /> LANGUAGE_CODE <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'pt-br'</span></div></div><p>Agora que terminamos de configurar o projeto, vamos criar nossa aplicação. Vamos novamente utilizar o django-admin.py para gerar uma aplicação. Como no caso de iniciar um projeto, usamos <em>startproject</em>, para iniciar uma aplicação, usamos <em>startapp</em>. Intuitivo, não?</p><pre>$ django-admin-py startapp usuarios</pre><p>Após executar este comando, temos um novo diretório e a estrutura mudou um pouco. O Django criou o diretório com o nome da aplicação, e colocou lá alguns arquivos: models.py, onde definimos nossos modelos; tests.py, onde definimos nossos testes unitários (e também doctests); e views.py, nossa controladora que tem nome estranho =P Vamos começar definindo nosso model. Diferente do SQL Alchemy, usado no Pylons, o Django implementa o modelo de ORM chamado ActiveRecord. <strong><span style="color: #ff0000;"><span style="font-weight: normal;"><span style="color: #000000;">Reinout van Rees</span></span> </span></strong>considera o pattern ActiveRecord excelente para pequenas queries, mas problemático para estruturas mais complexas. A disputa DataMapper x ActiveRecord, e a preferência por DataMapper, foi fator fundamental na decisão do SourceForge adotar Turbo Gears (outro framework Python, que também utiliza SQL Alchemy, muito influenciado pelo Pylons) ao invés Django, Ruby on Rails ou Cake PHP.</p><p>&#8220;Sem querer querendo&#8221;, eu vou apresentar um pouco uma comparação entre ActiveRecord e DataMapper em Python, estabelecida nas ferramentas Django ORM vs. SQL Alchemy. No meu exemplo mega simples, ActiveRecord será a melhor resposta sem sombra de dúvidas, mas não deixe de ler sobre o assunto para formar uma opinião.</p><p><strong>Definindo o Model</strong></p><p>No Django, os models são definidos dentro da aplicação, no módulo <em>models.py</em>. Cada model é uma classe que herda de <em>django.db.models.Model</em>. Abaixo segue nosso model Usuario:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">class</span> Usuario<span style="color: black;">&#40;</span>models.<span style="color: black;">Model</span><span style="color: black;">&#41;</span>:<br /> nome <span style="color: #66cc66;">=</span> models.<span style="color: black;">CharField</span><span style="color: black;">&#40;</span>max_length<span style="color: #66cc66;">=</span><span style="color: #ff4500;">100</span><span style="color: black;">&#41;</span><br /> idade <span style="color: #66cc66;">=</span> models.<span style="color: black;">IntegerField</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></div><p>Note que a classe ficou bem simples. Agora, vamos dizer ao Django que vamos utilizar a aplicação <em>usuarios</em> em nosso projeto <em>dj_portal_abc </em>e depois criar o banco de dados. Para isso, basta abrir o arquivo settings.py novamente e buscar a tupla <em>INSTALLED_APPS</em>. Adicione ao final da tupla o nome da nossa aplicação,  ficando desta forma a tupla:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">INSTALLED_APPS <span style="color: #66cc66;">=</span> <span style="color: black;">&#40;</span><br /> <span style="color: #483d8b;">'django.contrib.auth'</span><span style="color: #66cc66;">,</span><br /> <span style="color: #483d8b;">'django.contrib.contenttypes'</span><span style="color: #66cc66;">,</span><br /> <span style="color: #483d8b;">'django.contrib.sessions'</span><span style="color: #66cc66;">,</span><br /> <span style="color: #483d8b;">'django.contrib.sites'</span><span style="color: #66cc66;">,</span><br /> <span style="color: #483d8b;">'usuarios'</span><span style="color: #66cc66;">,</span><br /> <span style="color: black;">&#41;</span></div></div><p>Agora basta utilizar o <em>manage.py</em>, que permite gerenciar o banco de dados e também a execução da aplicação. Para criar as tabelas no banco de dados, rodamos o comando:</p><pre>$ python manage.py syncdb</pre><p>Como a aplicação <em>django.contrib.auth</em> está instalada, ele vai perguntar se desejamos criar um usuário. Já que este recurso não vai ser utilizado, podemos simplesmente responder &#8220;no&#8221;.</p><p><strong>&#8220;Controllers&#8221; e URL&#8217;s no Django</strong></p><p>Como visto no começo, controllers são chamados de views no Django. Assim, definimos nossas funções de controle no módulo <em>views.py</em> da nossa aplicação. O Django não fornece um padrão de acesso às URL&#8217;s, sendo necessário configurar todas as URLs para uso. A ideia de usar REST com Django fica como assunto de um próximo post. Neste CRUD, vamos seguir o padrão torto de trabalhar de maneira &#8220;não-RESTful&#8221;.</p><p>Vamos trabalhar na mesma ordem, criando primeiramente a view responsável por listar os usuários. Vamos chamá-la de <em>listar_usuarios</em>. Dentro do nosso arquivo <em>views.py</em>, da aplicação <em>usuarios</em>, adicionamos o seguinte código:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">def</span> listar_usuarios<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>:<br /> usuarios <span style="color: #66cc66;">=</span> Usuario.<span style="color: black;">objects</span>.<span style="color: #008000;">all</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br /> <span style="color: #ff7700;font-weight:bold;">return</span> render_to_response<span style="color: black;">&#40;</span><span style="color: #483d8b;">'usuarios/listar.html'</span><span style="color: #66cc66;">,</span> <span style="color: #008000;">locals</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> context_instance<span style="color: #66cc66;">=</span>RequestContext<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></div></div><p>Após criar a view, precisamos mapeá-la na configuração de urls. Abra o módulo <em>urls.py</em> e adicione ao objeto <em>urlpatterns</em> o item respectivo à view criada. Ficará desta forma:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">urlpatterns <span style="color: #66cc66;">=</span> patterns<span style="color: black;">&#40;</span><span style="color: #483d8b;">''</span><span style="color: #66cc66;">,</span><br /> <span style="color: #808080; font-style: italic;"># Example:</span><br /> <span style="color: #808080; font-style: italic;"># (r'^dj_portal_abc/', include('dj_portal_abc.foo.urls')),</span><br /> <br /> <span style="color: #808080; font-style: italic;"># Uncomment the admin/doc line below and add 'django.contrib.admindocs'</span><br /> <span style="color: #808080; font-style: italic;"># to INSTALLED_APPS to enable admin documentation:</span><br /> <span style="color: #808080; font-style: italic;"># (r'^admin/doc/', include('django.contrib.admindocs.urls')),</span><br /> <br /> <span style="color: #808080; font-style: italic;"># Uncomment the next line to enable the admin:</span><br /> <span style="color: #808080; font-style: italic;"># (r'^admin/', include(admin.site.urls)),</span><br /> <span style="display:block;background-color:#ffff66"><span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'^usuarios/$'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'usuarios.views.listar_usuarios'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br /></span><span style="color: black;">&#41;</span></div></div><p>Vemos na linha 11 (em destaque) a configuração: dizemos que o endereço <em>/usuarios/</em> será tratado pela view <em>listar_usuarios</em>. Por mais chato que isso possa parecer, tem que ser feito assim, infelizmente. Se executarmos o servidor agora, através do comando</p><pre>$ python manage.py runserver</pre><p>obteremos um erro: <em>TemplateDoesNotExist at /usuarios/</em>. Dentro da nossa view, utilizamos o atalho <em>render_to_response</em>, para renderizar nosso template &#8216;usuarios/listar.html&#8217;, passando para ele todas as variáveis locais (no caso, apenas a variável <em>usuarios</em>). Mas em que momento definimos este template? Em momento algum!</p><p>Vamos, antes de mais nada, configurar no settings.py qual será nosso diretório com os templates. Assim, criamos o diretório <em>templates</em> na raiz do nosso projeto (junto aos arquivos <em>manage.py</em> e <em>settings.py</em>), e abrimos o settings.py para indicar que aquele será nosso diretório de templates, na tupla <em>TEMPLATE_DIRS</em>. Para obter o caminho absoluto, fazemos uso do módulo os, da biblioteca padrão do Python. Assim, adiciona as duas linhas ao começo do arquivo settings.py:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span><br /> RAIZ_DO_PROJETO <span style="color: #66cc66;">=</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">dirname</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">abspath</span><span style="color: black;">&#40;</span>__file__<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></div></div><p>Em seguida, altere a tupla <em>TEMPLATE_DIRS</em>, para que fique desta forma:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">TEMPLATE_DIRS <span style="color: #66cc66;">=</span> <span style="color: black;">&#40;</span><br /> <span style="color: #808080; font-style: italic;"># Put strings here, like &quot;/home/html/django_templates&quot; or &quot;C:/www/django/templates&quot;.</span><br /> <span style="color: #808080; font-style: italic;"># Always use forward slashes, even on Windows.</span><br /> <span style="color: #808080; font-style: italic;"># Don't forget to use absolute paths, not relative paths.</span><br /> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span>RAIZ_DO_PROJETO<span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'templates'</span><span style="color: black;">&#41;</span><br /> <span style="color: black;">&#41;</span></div></div><p>Após isso, criamos o arquivo <em>base.html</em>, que é nosso template que servirá de base. Este arquivo deve ficar dentro do diretório <em>templates</em>, e tem simplesmente o seguinte código:</p><div class="codecolorer-container html4strict default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="html4strict codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">{% block &quot;conteudo&quot; %}<br /> {% endblock &quot;conteudo&quot; %}</div></div><p>Após esta definição, crie o template da listagem de usuários, dentro do diretório <em>templates/usuarios</em>, com o nome <em>listar.html</em>.  Este template fica com o seguinte código:</p><div class="codecolorer-container html4strict default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="html4strict codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">{% extends &quot;base.html&quot; %}<br /> <br /> {% block &quot;titulo&quot; %}Listagem de usuários - {{ block.super }}{% endblock &quot;titulo&quot; %}<br /> <br /> {% block &quot;conteudo&quot; %}<br /> <span style="color: #009900;">&lt;<a href="http://december.com/html/4/element/ul.html"><span style="color: #000000; font-weight: bold;">ul</span></a>&gt;</span> {% for usuario in usuarios %}<span style="color: #ddbb00;">&amp;nbsp;</span><br /> &nbsp; &nbsp; <span style="color: #009900;">&lt;<a href="http://december.com/html/4/element/li.html"><span style="color: #000000; font-weight: bold;">li</span></a>&gt;&lt;<a href="http://december.com/html/4/element/a.html"><span style="color: #000000; font-weight: bold;">a</span></a> <span style="color: #000066;">href</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;{% url usuarios.views.ver_usuario usuario.id %}&quot;</span>&gt;</span>{{ usuario.nome }}<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><a href="http://december.com/html/4/element/a.html"><span style="color: #000000; font-weight: bold;">a</span></a>&gt;&lt;<span style="color: #66cc66;">/</span><a href="http://december.com/html/4/element/li.html"><span style="color: #000000; font-weight: bold;">li</span></a>&gt;</span><br /> {% endfor %}<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><a href="http://december.com/html/4/element/ul.html"><span style="color: #000000; font-weight: bold;">ul</span></a>&gt;</span><br /> <span style="color: #009900;">&lt;<a href="http://december.com/html/4/element/a.html"><span style="color: #000000; font-weight: bold;">a</span></a> <span style="color: #000066;">href</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;{% url usuarios.views.novo %}&quot;</span>&gt;</span>Cadastrar novo usuário<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><a href="http://december.com/html/4/element/a.html"><span style="color: #000000; font-weight: bold;">a</span></a>&gt;</span><br /> <br /> {% endblock &quot;conteudo&quot; %}</div></div><p>Novamente, não vou postar o código de todos os templates, para evitar que o post cresça muito. Daqui pra frente, colocarei todos os templates no pastebin e todo o código estará disponível no Bitbucket.</p><p>Para não ficar abrindo e mapeando toda hora as URLs, vamos acessar o arquivo e mapear todas elas:</p><ul><li>/usuarios: Uma lista com todos os usuários;</li><li>/usuarios/novo: Cria um novo usuário (exibe o formulário e também processa o formulário);</li><li>/usuarios/{id}: Exibe os dados de um usuário (visualização);</li><li>/usuarios/{id}/apagar: Apaga um usuário do banco de dados;</li><li>/usuarios/{id}/editar: Edita os dados de um usuário.</li></ul><p>Assim, nosso arquivo <em>urls.py</em> fica assim:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">conf</span>.<span style="color: black;">urls</span>.<span style="color: black;">defaults</span> <span style="color: #ff7700;font-weight:bold;">import</span> *<br /> <br /> urlpatterns <span style="color: #66cc66;">=</span> patterns<span style="color: black;">&#40;</span><span style="color: #483d8b;">''</span><span style="color: #66cc66;">,</span><br /> <span style="color: #808080; font-style: italic;"># Example:</span><br /> <span style="color: #808080; font-style: italic;"># (r'^dj_portal_abc/', include('dj_portal_abc.foo.urls')),</span><br /> <br /> <span style="color: #808080; font-style: italic;"># Uncomment the admin/doc line below and add 'django.contrib.admindocs'</span><br /> <span style="color: #808080; font-style: italic;"># to INSTALLED_APPS to enable admin documentation:</span><br /> <span style="color: #808080; font-style: italic;"># (r'^admin/doc/', include('django.contrib.admindocs.urls')),</span><br /> <br /> <span style="color: #808080; font-style: italic;"># Uncomment the next line to enable the admin:</span><br /> <span style="color: #808080; font-style: italic;"># (r'^admin/', include(admin.site.urls)),</span><br /> <span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'^usuarios/$'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'usuarios.views.listar_usuarios'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br /> <span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'^usuarios/novo/$'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'usuarios.views.novo'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br /> <span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'^usuarios/(?P<span style="color: #000099; font-weight: bold;">\d</span>+)/$'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'usuarios.views.ver_usuario'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br /> <span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'^usuarios/(?P<span style="color: #000099; font-weight: bold;">\d</span>+)/apagar/$'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'usuarios.views.apagar'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br /> <span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'^usuarios/(?P<span style="color: #000099; font-weight: bold;">\d</span>+)/editar/$'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'usuarios.views.editar'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br /> <span style="color: black;">&#41;</span></div></div><p>Vamos agora criar todas essas views mapeadas. Vamos fazer na ordem: construir o formulário de cadastro, antes de tudo. Antes de criar a nossa view <em>novo</em>, definiremos o formulário, utilizando o recurso de formulário do Django (Django Forms). Para isto, crie um arquivo chamado <em>forms.py</em> dentro do diretório da aplicação <em>usuarios</em>. Este arquivo conterá nosso form, que é uma classe que herda da classe <em>django.forms.Form</em>. Além da classe <em>django.forms.Form</em>, o Django Forms fornece a classe <em>django.forms.ModelForm</em>, que se integra com os models e faz quase tudo de forma transparente. Veja como ficou a classe <em>UsuarioForm</em>:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">class</span> UsuarioForm<span style="color: black;">&#40;</span>ModelForm<span style="color: black;">&#41;</span>:<br /> <span style="color: #ff7700;font-weight:bold;">class</span> Meta:<br /> model <span style="color: #66cc66;">=</span> Usuario</div></div><p>Note que o Django faz tudo &#8220;por baixo dos panos&#8221;, relacionando a classe de modelo com o formulário. Também seria possível definir manualmente todos os campos. Para mais detalhes sobre Django Forms, consulte a <a title="Documentação oficial do Django Forms" href="http://docs.djangoproject.com/en/dev/topics/forms/" target="_blank">documentação oficial</a>.</p><p>Com a classe de formulário definida, podemos partir agora para nossa <em>view</em>, que fica com o seguinte código:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">def</span> novo<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>:<br /> <span style="color: #ff7700;font-weight:bold;">if</span> request.<span style="color: black;">method</span> <span style="color: #66cc66;">==</span> <span style="color: #483d8b;">'POST'</span>:<br /> form <span style="color: #66cc66;">=</span> UsuarioForm<span style="color: black;">&#40;</span>request.<span style="color: black;">POST</span><span style="color: black;">&#41;</span><br /> <span style="display:block;background-color:#ffff66"><span style="color: #ff7700;font-weight:bold;">if</span> form.<span style="color: black;">is_valid</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br /></span><span style="display:block;background-color:#ffff66">form.<span style="color: black;">save</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br /></span><span style="color: #ff7700;font-weight:bold;">return</span> HttpResponseRedirect<span style="color: black;">&#40;</span>reverse<span style="color: black;">&#40;</span><span style="color: #483d8b;">'usuarios.views.listar_usuarios'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br /> <span style="color: #ff7700;font-weight:bold;">else</span>:<br /> form <span style="color: #66cc66;">=</span> UsuarioForm<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br /> <span style="color: #ff7700;font-weight:bold;">return</span> render_to_response<span style="color: black;">&#40;</span><span style="color: #483d8b;">'usuarios/novo.html'</span><span style="color: #66cc66;">,</span> <span style="color: #008000;">locals</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> context_instance<span style="color: #66cc66;">=</span>RequestContext<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></div></div><p>Note que o Django faz algumas &#8220;mágicas&#8221; nas linhas destacadas: validação automática e salva os dados no banco de dados. Tudo por causa da uma forte e acoplada integração com os models. Eu sei que isso de perguntar se o método do <em>request</em> é <em>POST</em> lembra um pouco PHP, mas é a forma como geralmente se trabalha nas views :)</p><p>Agora é só definir o template, que recebe o formulário pela variável <em>forms</em>. O código do template está aqui: <a rel="nofollow" href="http://pastebin.com/f1479663c" target="_blank">http://pastebin.com/f1479663c</a>. Nossa view e nosso template estão prontos, vamos seguir a ordem e criar agora a view de visualização dos dados, chamada <em>ver_usuario</em>. Seu código fica bem simples:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">def</span> ver_usuario<span style="color: black;">&#40;</span>request<span style="color: #66cc66;">,</span> <span style="color: #008000;">id</span><span style="color: black;">&#41;</span>:<br /> usuario <span style="color: #66cc66;">=</span> Usuario.<span style="color: black;">objects</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span>pk<span style="color: #66cc66;">=</span><span style="color: #008000;">id</span><span style="color: black;">&#41;</span><br /> <span style="color: #ff7700;font-weight:bold;">return</span> render_to_response<span style="color: black;">&#40;</span><span style="color: #483d8b;">'usuarios/ver.html'</span><span style="color: #66cc66;">,</span> <span style="color: #008000;">locals</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> context_instance<span style="color: #66cc66;">=</span>RequestContext<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></div></div><p>Depois de definir a view, basta construir o template. O código do template <em>usuarios/ver.html</em> é o seguinte: <a href="http://pastebin.com/f47a43b9a" target="_blank">http://pastebin.com/f47a43b9a</a>. Novamente, vou deixar a view <em>editar</em> por conta, e vou terminar na view <em>apagar</em>, que é a mais simples de toda:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">def</span> apagar<span style="color: black;">&#40;</span>request<span style="color: #66cc66;">,</span> <span style="color: #008000;">id</span><span style="color: black;">&#41;</span>:<br /> usuario <span style="color: #66cc66;">=</span> Usuario.<span style="color: black;">objects</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span>pk<span style="color: #66cc66;">=</span><span style="color: #008000;">id</span><span style="color: black;">&#41;</span><br /> usuario.<span style="color: black;">delete</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br /> <span style="color: #ff7700;font-weight:bold;">return</span> HttpResponseRedirect<span style="color: black;">&#40;</span>reverse<span style="color: black;">&#40;</span><span style="color: #483d8b;">'usuarios.views.listar_usuarios'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></div></div><p>E pronto! =) Aí está, podemos rodar e testar nosso projeto. O código completo do projeto pode ser <a title="Projeto no Bitbucket" href="http://bitbucket.org/franciscosouza/tutorial-django-crud/" target="_blank">obtido no Bitbucket</a>.</p> ]]></content:encoded> <wfw:commentRss>http://www.franciscosouza.com.br/2009/12/16/construindo-um-crud-com-frameworks-python-parte-ii-django/feed/</wfw:commentRss> <slash:comments>23</slash:comments> </item> <item><title>Reduzindo a quantidade de acessos ao banco de dados no Django</title><link>http://www.franciscosouza.com.br/2009/10/28/reduzindo-a-quantidade-de-acessos-ao-banco-de-dados-no-django/</link> <comments>http://www.franciscosouza.com.br/2009/10/28/reduzindo-a-quantidade-de-acessos-ao-banco-de-dados-no-django/#comments</comments> <pubDate>Thu, 29 Oct 2009 02:13:00 +0000</pubDate> <dc:creator>Francisco Souza</dc:creator> <category><![CDATA[desenvolvimento de softwares]]></category> <category><![CDATA[banco de dados]]></category> <category><![CDATA[django]]></category> <category><![CDATA[python]]></category><guid isPermaLink="false">http://wp.franciscosouza.net/2009/10/28/reduzindo-a-quantidade-de-acessos-ao-banco-de-dados-no-django/</guid> <description><![CDATA[Um dos problemas que vemos com o uso do Django é a ausência do join nas consultas &#8220;padrões&#8221;. Por exemplo, se temos um model chamado Pessoa que tem um carro, fazemos dois selects (!) ao executar o seguinte código p = Pessoa.objects.get&#40;id=1&#41; carro = p.carro Na primeira linha é feito um select na tabela ligada [...]]]></description> <content:encoded><![CDATA[<p>Um dos problemas que vemos com o uso do Django é a ausência do join nas consultas &#8220;padrões&#8221;. Por exemplo, se temos um model chamado Pessoa que tem um carro, fazemos dois selects (!) ao executar o seguinte código<span id="more-133"></span></p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">p <span style="color: #66cc66;">=</span> Pessoa.<span style="color: black;">objects</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #008000;">id</span><span style="color: #66cc66;">=</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span><br /> carro <span style="color: #66cc66;">=</span> p.<span style="color: black;">carro</span></div></div><p>Na primeira linha é feito um select na tabela ligada à classe Pessoa pesquisando id = 1, e na segunda linha é feito um novo select na tabela ligada à classe Carros pesquisando o id da pessoa (fk) = 1.</p><p>Imagine que tenhamos o seguinte modelo de classes:</p><div class="separator" style="clear: both; text-align: center;"><a style="margin-left: 1em; margin-right: 1em;" href="http://4.bp.blogspot.com/_AewNgvR35uk/Suj82KfNEyI/AAAAAAAAAgQ/rJs4BfN_U_Y/s1600-h/class+diagram.png" target="_blank"><img src="http://4.bp.blogspot.com/_AewNgvR35uk/Suj82KfNEyI/AAAAAAAAAgQ/rJs4BfN_U_Y/s400/class+diagram.png" border="0" alt="" /></a></div><p>Basicamente o que eu quero é saber qual o continente de um cliente (id=1). Em Python/Django, basta fazer o seguinte:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">cliente <span style="color: #66cc66;">=</span> Cliente.<span style="color: black;">objects</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #008000;">id</span><span style="color: #66cc66;">=</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span><br /> continente <span style="color: #66cc66;">=</span> cliente.<span style="color: black;">bairro</span>.<span style="color: black;">cidade</span>.<span style="color: black;">uf</span>.<span style="color: black;">pais</span>.<span style="color: black;">continente</span></div></div><p>Mas e aí, quantas vezes eu vou no banco de dados fazendo isso? Vamos unir o interpretador interativo + o debug do Django! (:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #66cc66;">&gt;&gt;&gt;</span> cliente <span style="color: #66cc66;">=</span> Cliente.<span style="color: black;">objects</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #008000;">id</span><span style="color: #66cc66;">=</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span><br /> <span style="color: #66cc66;">&gt;&gt;&gt;</span> continente <span style="color: #66cc66;">=</span> cliente.<span style="color: black;">bairro</span>.<span style="color: black;">cidade</span>.<span style="color: black;">uf</span>.<span style="color: black;">pais</span>.<span style="color: black;">continente</span><br /> <span style="color: #66cc66;">&gt;&gt;&gt;</span> <span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">db</span> <span style="color: #ff7700;font-weight:bold;">import</span> connection<br /> <span style="color: #66cc66;">&gt;&gt;&gt;</span> q <span style="color: #66cc66;">=</span> connection.<span style="color: black;">queries</span><br /> <span style="color: #66cc66;">&gt;&gt;&gt;</span> <span style="color: #ff7700;font-weight:bold;">for</span> qq <span style="color: #ff7700;font-weight:bold;">in</span> q:<br /> ... &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">print</span> qq<br /> ...<br /> <span style="color: black;">&#123;</span><span style="color: #483d8b;">'time'</span>: <span style="color: #483d8b;">'0.003'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'sql'</span>: u<span style="color: #483d8b;">'SELECT &quot;clientes_cliente&quot;.&quot;id&quot;, &quot;clientes_cliente&quot;.&quot;nome&quot;, &quot;clientes_cliente&quot;.&quot;idade&quot;, &quot;clientes_cliente&quot;.&quot;bairro_id&quot; FROM &quot;clientes_cliente&quot; WHERE &quot;clientes_cliente&quot;.&quot;id&quot; = 1 '</span><span style="color: black;">&#125;</span><br /> <span style="color: black;">&#123;</span><span style="color: #483d8b;">'time'</span>: <span style="color: #483d8b;">'0.000'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'sql'</span>: u<span style="color: #483d8b;">'SELECT &quot;clientes_bairro&quot;.&quot;id&quot;, &quot;clientes_bairro&quot;.&quot;nome&quot;, &quot;clientes_bairro&quot;.&quot;cidade_id&quot; FROM &quot;clientes_bairro&quot; WHERE &quot;clientes_bairro&quot;.&quot;id&quot; = 1 '</span><span style="color: black;">&#125;</span><br /> <span style="color: black;">&#123;</span><span style="color: #483d8b;">'time'</span>: <span style="color: #483d8b;">'0.000'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'sql'</span>: u<span style="color: #483d8b;">'SELECT &quot;clientes_cidade&quot;.&quot;id&quot;, &quot;clientes_cidade&quot;.&quot;nome&quot;, &quot;clientes_cidade&quot;.&quot;uf_id&quot; FROM &quot;clientes_cidade&quot; WHERE &quot;clientes_cidade&quot;.&quot;id&quot; = 1 '</span><span style="color: black;">&#125;</span><br /> <span style="color: black;">&#123;</span><span style="color: #483d8b;">'time'</span>: <span style="color: #483d8b;">'0.000'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'sql'</span>: u<span style="color: #483d8b;">'SELECT &quot;clientes_uf&quot;.&quot;id&quot;, &quot;clientes_uf&quot;.&quot;sigla&quot;, &quot;clientes_uf&quot;.&quot;pais_id&quot; FROM &quot;clientes_uf&quot; WHERE &quot;clientes_uf&quot;.&quot;id&quot; = 1 '</span><span style="color: black;">&#125;</span><br /> <span style="color: black;">&#123;</span><span style="color: #483d8b;">'time'</span>: <span style="color: #483d8b;">'0.000'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'sql'</span>: u<span style="color: #483d8b;">'SELECT &quot;clientes_pais&quot;.&quot;id&quot;, &quot;clientes_pais&quot;.&quot;nome&quot;, &quot;clientes_pais&quot;.&quot;continente_id&quot; FROM &quot;clientes_pais&quot; WHERE &quot;clientes_pais&quot;.&quot;id&quot; = 1 '</span><span style="color: black;">&#125;</span><br /> <span style="color: black;">&#123;</span><span style="color: #483d8b;">'time'</span>: <span style="color: #483d8b;">'0.000'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'sql'</span>: u<span style="color: #483d8b;">'SELECT &quot;clientes_continente&quot;.&quot;id&quot;, &quot;clientes_continente&quot;.&quot;nome&quot; FROM &quot;clientes_continente&quot; WHERE &quot;clientes_continente&quot;.&quot;id&quot; = 1 '</span><span style="color: black;">&#125;</span><br /> <span style="color: #66cc66;">&gt;&gt;&gt;</span></div></div><p>Note: foram duas linhas de comando e <strong>SEIS</strong> acessos ao banco de dados. Foi feito um select para cada entidade. Mas e agora, tem como resolver isso? (: Graças a Deus, sim! Basta usar a ideia do <em><a href="http://docs.djangoproject.com/en/dev/ref/models/querysets/#id4" target="_blank">select_related</a></em>. Vamos fazer um novo teste usando select_related? Aí está:</p><div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #66cc66;">&gt;&gt;&gt;</span> cliente <span style="color: #66cc66;">=</span> Cliente.<span style="color: black;">objects</span>.<span style="color: black;">select_related</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #008000;">id</span><span style="color: #66cc66;">=</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span><br /> <span style="color: #66cc66;">&gt;&gt;&gt;</span> continente <span style="color: #66cc66;">=</span> cliente.<span style="color: black;">bairro</span>.<span style="color: black;">cidade</span>.<span style="color: black;">uf</span>.<span style="color: black;">pais</span>.<span style="color: black;">continente</span><br /> <span style="color: #66cc66;">&gt;&gt;&gt;</span> <span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">db</span> <span style="color: #ff7700;font-weight:bold;">import</span> connection<br /> <span style="color: #66cc66;">&gt;&gt;&gt;</span> q <span style="color: #66cc66;">=</span> connection.<span style="color: black;">queries</span><br /> <span style="color: #66cc66;">&gt;&gt;&gt;</span> <span style="color: #ff7700;font-weight:bold;">for</span> qq <span style="color: #ff7700;font-weight:bold;">in</span> q:<br /> ... &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">print</span> qq<br /> ...<br /> <span style="color: black;">&#123;</span><span style="color: #483d8b;">'time'</span>: <span style="color: #483d8b;">'0.010'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'sql'</span>: u<span style="color: #483d8b;">'SELECT &quot;clientes_cliente&quot;.&quot;id&quot;, &quot;clientes_cliente&quot;.&quot;nome&quot;, &quot;clientes_cliente&quot;.&quot;idade&quot;, &quot;clientes_cliente&quot;.&quot;bairro_id&quot;, &quot;clientes_bairro&quot;.&quot;id&quot;, &quot;clientes_bairro&quot;.&quot;nome&quot;, &quot;clientes_bairro&quot;.&quot;cidade_id&quot;, &quot;clientes_cidade&quot;.&quot;id&quot;, &quot;clientes_cidade&quot;.&quot;nome&quot;, &quot;clientes_cidade&quot;.&quot;uf_id&quot;, &quot;clientes_uf&quot;.&quot;id&quot;, &quot;clientes_uf&quot;.&quot;sigla&quot;, &quot;clientes_uf&quot;.&quot;pais_id&quot;, &quot;clientes_pais&quot;.&quot;id&quot;, &quot;clientes_pais&quot;.&quot;nome&quot;, &quot;clientes_pais&quot;.&quot;continente_id&quot;, &quot;clientes_continente&quot;.&quot;id&quot;, &quot;clientes_continente&quot;.&quot;nome&quot; FROM &quot;clientes_cliente&quot; INNER JOIN &quot;clientes_bairro&quot; ON (&quot;clientes_cliente&quot;.&quot;bairro_id&quot; = &quot;clientes_bairro&quot;.&quot;id&quot;) INNER JOIN &quot;clientes_cidade&quot; ON (&quot;clientes_bairro&quot;.&quot;cidade_id&quot; = &quot;clientes_cidade&quot;.&quot;id&quot;) INNER JOIN &quot;clientes_uf&quot; ON (&quot;clientes_cidade&quot;.&quot;uf_id&quot; = &quot;clientes_uf&quot;.&quot;id&quot;) INNER JOIN &quot;clientes_pais&quot; ON (&quot;clientes_uf&quot;.&quot;pais_id&quot; = &quot;clientes_pais&quot;.&quot;id&quot;) INNER JOIN &quot;clientes_continente&quot; ON (&quot;clientes_pais&quot;.&quot;continente_id&quot; = &quot;clientes_continente&quot;.&quot;id&quot;) WHERE &quot;clientes_cliente&quot;.&quot;id&quot; = 1 '</span><span style="color: black;">&#125;</span><br /> <span style="color: #66cc66;">&gt;&gt;&gt;</span></div></div><p>Bem melhor: duas linhas de código, um select! Apenas um acesso ao banco, seu DBA não vai caçar você insanamente por acessar 800 milhões de vezes o banco de dados, e sua aplicação estará BEEEM mais rápida =D</p><p>Bom, aí fica a dica para os iniciantes (já que todos os blog tutorials ignoram isso de select_related). Good coding (:</p><p>O projeto deste exemplo está no Bitbucket: <a href="http://bitbucket.org/franciscosouza/exemplo_select/" target="_blank">http://bitbucket.org/franciscosouza/exemplo_select/</a>.</p> ]]></content:encoded> <wfw:commentRss>http://www.franciscosouza.com.br/2009/10/28/reduzindo-a-quantidade-de-acessos-ao-banco-de-dados-no-django/feed/</wfw:commentRss> <slash:comments>2</slash:comments> </item> <item><title>Django com Admin e Flatpages no AppEngine</title><link>http://www.franciscosouza.com.br/2009/10/19/django-com-admin-e-flatpages-no-appengine/</link> <comments>http://www.franciscosouza.com.br/2009/10/19/django-com-admin-e-flatpages-no-appengine/#comments</comments> <pubDate>Mon, 19 Oct 2009 14:00:00 +0000</pubDate> <dc:creator>Francisco Souza</dc:creator> <category><![CDATA[desenvolvimento de softwares]]></category> <category><![CDATA[appengine]]></category> <category><![CDATA[cloud computing]]></category> <category><![CDATA[django]]></category> <category><![CDATA[python]]></category><guid isPermaLink="false">http://wp.franciscosouza.net/2009/10/19/django-com-admin-e-flatpages-no-appengine/</guid> <description><![CDATA[No meu site (não no blog, apenas no site) estou utilizando Python + Django, na estrutura do Google AppEngine. Levar o Django do jeito &#8220;natural&#8221; para o AppEngine faz-nos perder alguns recursos interessantes, como o Admin e as Flatpages. Na página /sobre, por exemplo, eu estava fazendo algo bem escroto: escrevendo o conteúdo nu e [...]]]></description> <content:encoded><![CDATA[<p>No meu site (não no blog, apenas no site) estou utilizando Python + Django, na estrutura do Google AppEngine. Levar o Django do jeito &#8220;natural&#8221; para o AppEngine faz-nos perder alguns recursos interessantes, como o Admin e as Flatpages.</p><p>Na página /sobre, por exemplo, eu estava fazendo algo bem escroto: escrevendo o conteúdo nu e cru no template. Isso não é bom, afinal de contas, qualquer alteração implicaria em um novo upload da aplicação. Eu precisava de algo dinâmico, sabia que existiam as Flatpages e tinha duas opções: buscar uma forma de usar as Flatpages (e o Admin) no AppEngine, ou desenvolver algo como um &#8220;gerenciador de conteúdos&#8221;, para que eu pudesse cadastrar minhas páginas e fazer todo o gerenciamento via web. Essas duas opções viraram passos: primeiro eu fui buscar, e encontrei&#8230;<span id="more-131"></span></p><p>O projeto <a href="http://code.google.com/p/app-engine-patch/" target="_blank">app-engine-patch</a> traz diversos recursos do Django para uso no AppEngine não presentes no Django padrão do Google App Engine (ou na versão SVN), como a interface do admin, o uso das funções de e-mail próprias do Django, uso da autenticação do Django, flatpages, o gerenciamento utilizando o <em>manage.py</em> (com manage.py upload, para enviar a aplicação, por exemplo) e <a href="http://code.google.com/p/app-engine-patch/wiki/GettingStarted" target="_blank">muito mais</a>!</p><p>Vou passar rapidamente pelo meu roteiro de migração, no começo tive dificuldades, mas no final foi muito de boa. Baixei o projeto exemplo, copiei o diretório <em>common</em> para dentro do meu projeto, fiz um &#8220;merge&#8221; do meu <em>settings.py </em>com o <em>settings.py</em> do projeto exemplo, substituí meu <em>manage.py</em> pelo <em>manage.py </em>de exemplo e estava lá, com suporte ao admin e às flatpages.</p><p>E agora a /sobre/ é uma flatpage. Não muda nada pra quem vê, mas pra quem gerencia, é um progresso e tanto (: Adicionalmente, resolvi disponibilizar o código do site no Bitbucket: <a href="http://bitbucket.org/franciscosouza/website/" target="_blank">http://bitbucket.org/franciscosouza/website/</a>.</p><p><strong>Observação: </strong>Um problema que tive foi o uso do meu e-mail no Google Apps, o app-engine-patch não permite fazer a migração do banco de dados ou qualquer acesso remoto usando um usuário de um domínio do Google Apps. Para migrar o banco de dados, você deve usar um Gmail ou outra conta Google (:</p><p><strong>Observação 2: </strong>Tive um outro problema com um índice para uso do admin. O AppEngine localmente não gerou o índice automaticamente, então tive que adicionar um índice ao <em>index.yaml</em>:</p><div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">- kind: django_admin_log<br /> &nbsp; &nbsp; properties:<br /> &nbsp; &nbsp; - name: user<br /> &nbsp; &nbsp; - name: action_time<br /> &nbsp; &nbsp; direction: desc</div></div> ]]></content:encoded> <wfw:commentRss>http://www.franciscosouza.com.br/2009/10/19/django-com-admin-e-flatpages-no-appengine/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> </channel> </rss>
