Django Utilidades :: Marinho Brandaohttp://marinhobrandao.com/pt-brFri, 21 Nov 2008 02:54:07 -0000Aprendendo Django no Planeta Terrahttp://marinhobrandao.com/blog/p/aprendendo-django-no-planeta-terra/<div class="document"> <p>Acabo de colocar no ar o site <strong>Aprendendo Django no Planeta Terra</strong> <a class="footnote-reference" href="#id8" id="id1" name="id1">[1]</a>.</p> <img alt="http://marinhobrandao.com/blog/p/aprendendo-django-ilustracao/?img=1" src="http://marinhobrandao.com/blog/p/aprendendo-django-ilustracao/?img=1" /> <p>Quem tem me acompanhado com uma certa regularidade sabe que eu valorizo muito a imagem, mas tenho evoluído nesse sentido.</p> <p>Após mais de 2 anos usando o Django em projetos profissionais e brincadeiras pessoas, e participando da comunidade brasileira, especialmente, uma coisa se tornou muito clara pra mim: é preciso dar um passo mais ousado.</p> <p>Eu cheguei a escrever um livro quase completo há 1 ano atrás e não gostei do resultado. Alguns amigos apontaram o que eu já sentia: o conteúdo estava burocrático e pesado, e precisava de um certo pragmatismo (obrigado Andrews).</p> <p>Então, este é um livro eletrônico voltado para o iniciante. São capítulos curtos, liberados quase que diariamente, bem ilustrados e com uma linguagem que segue a história de um alienígena, me inspirando na série do <strong>Guia do Mochileiro das Galáxias</strong>.</p> <p>É a história de um alienígena e seus dois amigos terráqueos, que estão aprendendo Django e se divertindo muito com isso. À medida que a história se desenrola, o framework é desmistificado e a pessoa vai aprendendo como resolver problemas e fazer boas sacadas no seu dia-a-dia.</p> <p>Sou muito grato ao <strong>Luciano Ramalho</strong> <a class="footnote-reference" href="#id10" id="id2" name="id2">[3]</a>, <strong>Guilherme Semente</strong> <a class="footnote-reference" href="#id11" id="id3" name="id3">[4]</a> e <strong>Felippe Nardi</strong> pelas dicas, ao <strong>João Matheus</strong> por suas ilustrações e ao <strong>Miltinho Brandão</strong> <a class="footnote-reference" href="#id9" id="id4" name="id4">[2]</a> pelas idéias, ouvido e amizade de sempre. E claro, não somente eles, todo o restante da comunidade brasileira <a class="footnote-reference" href="#id12" id="id5" name="id5">[5]</a>, da World News Network <a class="footnote-reference" href="#id13" id="id6" name="id6">[6]</a> e dos desenvolvedores do Django Project <a class="footnote-reference" href="#id14" id="id7" name="id7">[7]</a>.</p> <p><strong>Links relacionados</strong></p> <table class="docutils footnote" frame="void" id="id8" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id1" name="id8">[1]</a></td><td><a class="reference" href="http://www.aprendendodjango.com/">http://www.aprendendodjango.com/</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id9" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id4" name="id9">[2]</a></td><td><a class="reference" href="http://miltonweb.wordpress.com/">http://miltonweb.wordpress.com/</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id10" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id2" name="id10">[3]</a></td><td><a class="reference" href="http://ramalho.org/">http://ramalho.org/</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id11" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id3" name="id11">[4]</a></td><td><a class="reference" href="http://semente.taurinus.org/">http://semente.taurinus.org/</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id12" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id5" name="id12">[5]</a></td><td><a class="reference" href="http://www.djangobrasil.org/">http://www.djangobrasil.org/</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id13" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id6" name="id13">[6]</a></td><td><a class="reference" href="http://wnnetworks.com/">http://wnnetworks.com/</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id14" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id7" name="id14">[7]</a></td><td><a class="reference" href="http://www.djangoproject.com/">http://www.djangoproject.com/</a></td></tr> </tbody> </table> </div> Django-plus: para que serve? (parte 2)http://marinhobrandao.com/blog/p/django-plus-para-que-serve-parte-2/<h2>Template Filters de comparação</h2> Continuando a explanação sobre os recursos disponíveis no <b>django-plus</b> <a title="[1]" href="http://code.google.com/p/django-plus/" id="z.1t">[1]</a>, eu gostaria somente de esclarecer um pouco mais sobre as template filters que disponibilizei no artigo anterior <a title="[2]" href="http://marinhobrandao.com/blog/p/django-plus-para-que-serve/" id="j9m1">[2]</a>.<br> <br> Vamos tomar a template filter <b>is_equal</b> como melhor exemplo.<br> <br> O sistema de templates do Django disponibiliza apenas um meio para se fazer comparativo de igualdade, assim:<br> <pre><code>{% ifequal user.username 'marinho' %} exibe ou faz alguma coisa {% endifequal %} </code></pre> <br> Notou a limitação? Não? Imagine que você precise colocar ali um simples <b>AND</b>, ou um <b>OR</b>. Não é possível ou no máximo é doloroso. Mas com a template tag {% if %} é possível, portanto, será nesse momento que você fará uso da template filter is_equal, assim:<br> <br> <pre><code>{% load djangoplus_tags %} {% if user.username|is_equal:'marinho' and user.is_staff %} exibe ou faz alguma coisa {% endif %} </code></pre> <br> Compreendeu? Agora siga a mesma linha de raciocínio para as template filters <b>is_not_equal</b> (diferente), <b>is_lt</b> (menor que), <b>is_lte</b> (menor ou igual), <b>is_gt</b> (maior que), <b>is_gte</b> (maior ou igual) e <b>in_list</b> (na lista).<br> <br> Agora, com as coisas esclarecidas, vamos adiante.<br> <h2>Model Info</h2> Hoje vamos falar especificamente do pacote de funcionalidades <b>model_info</b>. Para que ele serve?<br> <br> Bom, quando você quer criar um admin para uma classe de modelo, como você faz? Assim:<br> <br> <pre><code>class AdminPessoa(ModelAdmin): fields = ('nome','idade',) admin.site.register(Pessoa, AdminPessoa) </code></pre> <br> Simples né? Sim, muito. E para formulários de inclusão/alteração? Assim:<br> <br> <pre><code>class FormPessoa(ModelForm): class Meta: &nbsp;&nbsp;&nbsp;&nbsp; model = Pessoa </code></pre> <br> Sim, isso é suficiente para seu formulário funcionar bem para a classe de model. Simples assim.<br> <br> Agora, e quando você precisa listar os registros de uma queryset ou exibir as informações de um deles?<br> <br> Bom, aí é aquela coisa: você vai carregar o objeto ou a lista de objetos e vai tratar campo por campo no template. Quando for necessário mais de uma vez, você vai criar um template e incluí-lo nos demais, para evitar repetição de código. Para quem se acostumou com as mágicas acima, esse processo é doloroso ou no mínimo entediante.<br> <br> Então, para isso servem as classes e template tags do Model Info do django-plus.<br> <br> Vamos a um exemplo de exibição de dados de um objeto de uma classe <b>Pessoa</b>:<br> <h3><b>views.py</b><br> </h3> <pre><code>from django.shortcuts import get_object_or_404, render_to_response from models import Pessoa def detalhes_da_pessoa(request, pessoa_id): pessoa = get_object_or_404(Pessoa, id=pessoa_id) return render_to_response('detalhes_da_pessoa.html', locals()) </code></pre> <h3><b>info.py</b><br> </h3> <pre><code>from djangoplus.model_info import ModelInfo from models import Pessoa class InfoPessoa(ModelInfo): class Meta: &nbsp;&nbsp; &nbsp; model = Pessoa </code></pre> <h3><b>detalhes_da_pessoa.html</b> (template)<br> </h3> <pre><code>{% load djangoplus_tags %} &lt;table class="info"&gt; {% model_info_for_object 'minha_aplicacao.info.InfoPessoa' pessoa %} &lt;/table&gt; </code></pre> <br> Absurdamente simples, não? Neste caso, não importa se sua classe <b>Pessoa</b> possua 2 ou 50 campos, eles serão todos (à exceção de ManyToManyField) listados comportadamente na tabela informada e funcionará bem.<br> <br> Vamos fazer uma listagem? Ok, vamos lá:<br> <h3><b>views.py</b> (acrescente as linhas ao fim do arquivo)<br> </h3> <pre><code>def lista_de_pessoas(request): pessoas = Pessoa.objects.all() return render_to_response('lista_de_pessoas.html', locals()) </code></pre> <h3><b>info.py</b> (acrescente as linhas ao fim do arquivo)<br> </h3> <pre><code>from djangoplus.model_info import ModelList class ListPessoa(ModelList): class Meta: &nbsp;&nbsp; &nbsp; model = Pessoa </code></pre> <h3><b>lista_de_pessoas.html</b> (template)<br> </h3> <pre><code>{% load djangoplus_tags %} &lt;table class="list"&gt; {% model_info_for_list 'minha_aplicacao.info.ListPessoa' pessoas %} &lt;/table&gt; </code></pre> <br> Da mesma forma, será exibida uma lista dos objetos da queryset <b>pessoas</b>, e se a classe Pessoa possuir o método <b>get_absolute_url</b> declarado, serão exibidos também ícones para edição e exclusão, seguindo as convenções comuns <b>"{{ objeto.get_absolute_url }}edit/"</b> e <b>"{{ objeto.get_absolute_url }}delete/"</b>, respectivamente.<br> <br> Mas é só isso? Para funcionar o básico, sim, mas aquela subclasse "Meta" permite diversas outras formas de customização.<br> <br> Vamos falar primeiramente da classe ModelInfo:<br> <br> <ul> <li><b>fields</b> - lista dos campos a serem exibidos;<br> </li> <li><b>exclude</b> - lista dos campos a não serem exibidos (não é muito inteligente usar o fields e o exclude ao mesmo tempo);<br> </li> <li><b>show_if_none</b> - informe True ou False para mostrar campos com valor <b>None</b> ou não;<br> </li> <li><b>show_if_empty</b> - informe True ou False para mostrar campos com valor <b>vazio</b> ou não;</li> <li><b>auto_urlize</b> - informe True ou False para transformar URLs em links;</li> <li><b>auto_linebreaks</b> - informe True ou False para mostrar quebras de linha em campos do tipo TextField;<br> </li> <li><b>list_display_links</b> - lista dos campos que serão exibidos como link para a página do objeto (raramente usada);</li> <li><b>fieldsets</b> - lista de fieldsets. Segue o mesmo padrão do fieldsets do Admin;</li> <li><b>row_template</b> (default: '&lt;tr&gt;&lt;th&gt;%s&lt;/th&gt;&lt;td&gt;%s&lt;/td&gt;&lt;/tr&gt;') - permite que você informe o formato para exibir cada campo e seu referido valor;</li> <li><b>fieldset_title_template</b> (default: '&lt;h3&gt;%s&lt;/h3&gt;') - permite que você informe o formato para exibir o título da fieldset;</li> <li><b>show_fieldset_title</b> - informe True ou False para exibir os títulos de fieldsets ou não;</li> </ul> <br> Bacana não? Agora vamos para a classe ModelList:<br> <br> <ul> <li><b>fields</b> - lista dos campos a serem exibidos;<br> </li> <li><b>exclude</b> - lista dos campos a não serem exibidos (não é muito inteligente usar o fields e o exclude ao mesmo tempo;<br> </li> <li><b>show_if_none</b> - informe True ou False para mostrar campos com valor <b>None</b> ou não;<br> </li> <li><b>show_if_empty</b> - informe True ou False para mostrar campos com valor <b>vazio</b> ou não;</li> <li><b>auto_urlize</b> - informe True ou False para transformar URLs em links;</li> <li><b>auto_linebreaks</b> - informe True ou False para mostrar quebras de linha em campos do tipo TextField;<br> </li> <li><b>list_display_links</b> - lista dos campos que serão exibidos como link para a página do objeto (raramente usada);</li> <li><b>td_template</b> (default: '&lt;td&gt;%s&lt;/td&gt;') - formato para exibir cada célula da tabela; </li> <li><b>th_template</b> (default: '&lt;th&gt;%s&lt;/th&gt;') - formato para exibir cada célula do cabeçalho da tabela;</li> <li><b>tr_template</b> (default: '&lt;tr&gt;%s&lt;/tr&gt;') - formato para exibir cada linha da tabela; </li> <li><b>thead_template</b> (default: '&lt;thead&gt;&lt;tr&gt;%s&lt;/tr&gt;&lt;/thead&gt;') - formato para exibir o cabeçalho da tabela;</li> <li><b>tbody_template</b> (default: '&lt;tbody&gt;%s&lt;/tbody&gt;') - formato para exibir o corpo de dados da tabela;</li> <li><b>icon_edit_template</b> (default: '&lt;a href="%(edit_url)s" title="Edit this"&gt;&lt;img src="%(media_url)simg/admin/icon_changelink.gif" alt="Edit"/&gt;&lt;/a&gt;') - formato para exibir o ícone de edição (informe vazio para não ser exibido);</li> <li><b>icon_delete_template</b> (default: '&lt;a href="%(delete_url)s" title="Delete this"&gt;&lt;img src="%(media_url)simg/admin/icon_deletelink.gif" alt="Edit"/&gt;&lt;/a&gt;') - formato para exibir o ícone de exclusão (informe vazio para não ser exibido);<br> </li> <li><b>group_template</b> (default: '&lt;tr&gt;&lt;td colspan="%(cols)s" class="group"&gt;&lt;h3&gt;%(display)s&lt;/h3&gt;&lt;/td&gt;&lt;/tr&gt;') - formato para agrupamentos; </li> <li><b>groups</b> - lista dos campos pelos quais os registros serão agrupados;&nbsp;</li> </ul> <br> Ufa! Mais poderoso e flexível do que pensava, não? Mas ainda tem mais.<br> <br> Tanto na classe ModelInfo quanto na ModelList, é possível informar métodos seguindo as sintaxes e regras abaixo:<br> <br> <ul> <li><b>get_CAMPO_display</b>(self, f_name) - retorna um título customizado para o campo em questão;</li> <li><b>get_CAMPO_value</b>(self, f_name, instance) - retorna um valor customizado e/ou tratado para o campo em questão. Isso lhe permite declarar campos calculados ou que são parte de uma instância diferente da instância que está sendo carregada. O campo em questão deve ser informado no atributo "fields" da subclasse META ou ao menos ser parte da classe e não estar no atributo "exclude";</li> <li><b>render_buttons_cell</b>(self, instance, edit_url=None, delete_url=None) - caso sobrecarregue este campo, é possível mudar o padrão das URLs de <b>edição</b> e <b>exclusão</b> do objeto;<br> </li> </ul> <br> Por fim, a classe ModelInfo permite que também ser declarada diretamente na view e ser chamada para cada campo solitariamente, assim:<br> <h3><b>views.py</b><br> </h3> <pre><code>from django.shortcuts import get_object_or_404, render_to_response from models import Pessoa from info import InfoPessoa def detalhes_da_pessoa(request, pessoa_id): pessoa = get_object_or_404(Pessoa, id=pessoa_id) info_pessoa = InfoPessoa(pessoa) return render_to_response('detalhes_da_pessoa.html', locals()) </code></pre> <h3><b>detalhes_da_pessoa.html</b> (template)<br> </h3> <pre><code>{% load djangoplus_tags %} {{ info_pessoa.nome }} </code></pre> <br> Gostou? E algo semelhante pode ser feito com a classe ModelList, que é iterável:<br> <h3><b>views.py</b> (acrescente as linhas ao fim do arquivo)<br> </h3> <pre><code>from info import ListPessoa def lista_de_pessoas(request): pessoas = Pessoa.objects.all() lista_pessoas = ListPessoa(pessoas) return render_to_response('lista_de_pessoas.html', locals()) </code></pre> <h3><b>lista_de_pessoas.html</b> (template)<br> </h3> <pre><code>{% load djangoplus_tags %} &lt;table class="list"&gt; {% for pessoa in lista_pessoas %} &lt;tr&gt;&lt;td&gt;{{ pessoa }}&lt;/td&gt;&lt;/tr&gt; {% endfor %} &lt;/table&gt; </code></pre> <br> Bom, é isso aí, espero que ajude. Eu ganho um bom temp usando esses recursos, acho que podem ser úteis para você também ;)<br> <h2><b>Links relacionados</b><br> </h2> <ol> <li><a title="http://code.google.com/p/django-plus/" href="http://code.google.com/p/django-plus/" id="se97"></a><a title="http://code.google.com/p/django-plus/" href="http://code.google.com/p/django-plus/" id="se97">http://code.google.com/p/django-plus/</a></li> <li><a title="http://marinhobrandao.com/blog/p/django-plus-para-que-serve/" href="http://marinhobrandao.com/blog/p/django-plus-para-que-serve/" id="erk7">http://marinhobrandao.com/blog/p/django-plus-para-que-serve/</a><br> </li> </ol>Django-plus: para que serve?http://marinhobrandao.com/blog/p/django-plus-para-que-serve/ <p>O django-plus foi uma biblioteca criada com o propósito de oferecer alguns recursos - criados por mim ou não, os códigos contidos nele que não são meus, recebem as devidas indicações ao autor - úteis que não faziam parte do core do Django. </p> <p>Porque isso? Porque o Django possui uma política de coesão que evita que ele vire um cara gordão cheio de coisas, mas que existem coisas que realmente ajudam bastante em certas tarefas, mas que não justificam a criação de uma aplicação plugável. </p> <p>Para adicionar a django-plus a seu projeto, você precisa fazer o download em <a href="http://code.google.com/p/django-plus/source/checkout">[2]</a> ou ainda em <a href="http://django-plus.googlecode.com/files/django-plus-1.0-stable.tar.gz">[6]</a> e adicionar ao projeto como uma aplicação qualquer. </p> <p>Seguem abaixo os recursos da django-plus. </p> <h3> JSONField</h3> <p>Tipo de campo que armazena listas ou dicionários em formato JSON bastante interessante. Foi criado pleo usuário <strong>deadwisdom</strong> que o colocou no Django Snippets <a href="http://www.djangosnippets.org/snippets/377/">[1]</a>. </p> <p>Exemplo de uso </p> <pre><code>from djangoplus.fieldtypes import JSONField class Produto(models.Model): nome = models.CharField(max_length=50) lista = JSONField(blank=True) </code></pre><p>este campo aceitará listas, dicionários e outros valores serializáveis. </p> <h3> SectionedForm</h3> <p>Baseando num snippet que criei <a href="http://www.djangosnippets.org/snippets/798/">[3]</a>, trata-se de uma classe de form que permite dividir o form em fieldsets (da mesma forma que você pode fazer no Admin, por exemplo. </p> <p>Exemplo de uso </p> <pre><code>from djangoplus.forms import SectionedForm class FormProduto(SectionedForm, forms.ModelForm): class Meta: model = Produto fields = ('nome', 'referecia', 'categorias', 'lista',) fieldsets = ( (None, ('nome','referencia',)), (_('Outras Informacoes'), ('categorias','lista',)), ) </code></pre><p>quando o form for utilizando no template, em forma forma convencional, ele será divido em duas seções, uma sem título e outra chamada &quot;Outras Informacoes&quot;. A divisão será feita por um elemento H3, mas você pode customizar isso adicionando o atributo &quot;fieldset_template&quot;, exemplo </p> <pre><code>class FormProduto(SectionedForm, forms.ModelForm): class Meta: model = Produto fields = ('nome', 'referecia', 'categorias', 'lista',) fieldset_template = '&lt;h2 id=&quot;section_%(section)s&quot;&gt;%(title)s&lt;/h2&gt;' </code></pre><p>usando o <strong>id</strong> gerado por ele (veja o HTML gerado para saber qual), você pode exibir ou ocultar uma seção usando JavaScript. </p> <p>para mim tem sido muito útil, pois é realmente chato ter formulários longos sem nenhuma divisão ou ter que informar campo por campo por causa delas. </p> <h3> Proteção anti-robôs para e-mails, URLs e outras informações</h3> <p>Baseado no snippet que criei em <a href="http://www.djangosnippets.org/snippets/742/">[4]</a>. </p> <p>Sabe aquela coisa que alguns sites fazem para proteger um e-mail contra spam? exemplo &quot;mari...@gmail.com&quot;. </p> <p>Pois é, essa proteção é para isso. Na verdade eu precisa de uma proteção que valesse para todo tipo de informações, então ao invés de fazer o mesmo com o e-mail (adicionar as reticencias), ele exibe uma frase do tipo &quot;informação confidencial, clique aqui para se certificar de que não é um robô&quot;. Aí ele vai para uma página com capcha e tudo resolvido. </p> <p>Como usar </p> <ol> <li><p>Adicione o middleware <strong>'djangoplus.middleware.ProtectAntiRobotsMiddleware'</strong> </p> </li> <li><p>No template onde deseja ocultar uma informação, acrescente </p> <pre><code>{% load djangoplus_tags %} {% protectantirobots %} &lt;a href=&quot;mailto: {{ office.email }}&quot;&gt;{{ office.email }}&lt;/a&gt; {% endprotectantirobots %} </code></pre> </li> </ol> <h3> FakeSessionCookieMiddleware</h3> <p>Criado por <strong>Dan Fairs</strong> e disponível em <a href="http://www.stereoplex.com/two-voices/cookieless-django-sessions-and-authentication-without-cookies">[5]</a> <em></em> este middleware aceita que seja informada a sessão através de um parâmetro na URL. É bastante útil para a criação de integração com Flesh, Ajax, Google Gadgets, Facebook F8, etc. </p> <p>Para usá-lo, acrescente-o como middleware ao seu projeto 'djangoplus.middleware.FakeSessionCookieMiddleware' </p> <h3> render_to_json</h3> <p>Como o nome indica, funciona de forma semelhante à shortcut render_to_response, mas se destina a responder em formato JSON. </p> <p>Exemplo de uso </p> <pre><code> from django.utils import simplejson from djangoplus.shortcuts import render_to_json def json_view(request): ret = {'nome': 'Tarsila', 'idade': 2} return render_to_json(simplejson.dumps(ret)) </code></pre> <h3> render_to_mail</h3> <p>Da mesma forma que a supracitade, renderiza, só que enviando um e-mail. É ainda mais útil para se enviar e-mails do tipo HTML. </p> <p>Como usar </p> <ol> <li><p>Se deseja usar formato texto plano, declare a setting </p> <pre><code>DEFAULT_EMAIL_CONTENT_SUBTYPE = 'plain' </code></pre> </li> <li><p>Para enviar um e-mail a partir de uma template, use um código como este </p> <pre><code>from djangoplus.shortcuts import render_to_mail return render_to_mail( 'central/usuarios/mensagem.html', locals(), subject='Titulo da mensagem', recipient_list=['marinho@marinhobrandao.com'], ) </code></pre> </li> </ol> <h3> DynamicTemplate</h3> <p>Trata-se de um template dinâmico, que é manipulado através do Admin e pode ser acrescentado a um outro template através da template <strong>{% dynamic_template %}</strong>. </p> <p>É um recurso poderoso, que pode ser utilizado em grupos, quando todos os templates de um grupo em comum são adicionados em lote. </p> <p>Como usar </p> <ol> <li><p>Vá até seu Admin, procure a seção &quot;Djangoplus&quot; e clique na opção &quot;Dynamic templates&quot; e adicione seu template dinâmico; </p> </li> <li><p>Em seu template, adicione uma template tag assim </p> <pre><code>{% load djangoplus_tags %} {% dynamic_template slug-do-template %} {% dynamic_template group grupo-do-template %} </code></pre> </li> </ol> <h3> Template filters</h3> <p>E por falar em template tags, o django-plus possui muitos template filters, úteis ou não para o seu caso </p> <ul> <li><p>{{ <strong>numero|multiple_of:3 }}</strong> - retorna True se &quot;numero&quot; é multiplo de &quot;3&quot; </p> </li> <li><p>{{ <strong>valor|in_list:lista }}</strong> - retorna True se &quot;valor&quot; está contido em &quot;lista&quot; </p> </li> <li><p>{{ <strong>valor|is_equal:outro_valor }}</strong> - retorna True se &quot;valor&quot; é igual a &quot;outro_valor&quot; </p> </li> <li><p>{{ <strong>valor|is_not_equal:outro_valor }}</strong> - retorna True se &quot;valor&quot; não é igual a &quot;outro_valor&quot; </p> </li> <li><p>{{ <strong>valor|is_lt:outro_valor }}</strong> - retorna True se &quot;valor&quot; é menor que &quot;outro_valor&quot; </p> </li> <li><p>{{ <strong>valor|is_lte:outro_valor }}</strong> - retorna True se &quot;valor&quot; é menor ou igual a &quot;outro_valor&quot; </p> </li> <li><p>{{ <strong>valor|is_gt:outro_valor }}</strong> - retorna True se &quot;valor&quot; é maior que &quot;outro_valor&quot; </p> </li> <li><p>{{ <strong>valor|is_gte:outro_valor }}</strong> - retorna True se &quot;valor&quot; é maior ou igual a &quot;outro_valor&quot; </p> </li> <li><p>{{ <strong>numero|is_day_of:data }}</strong> - retorna True se &quot;numero&quot; é o dia de uma &quot;data&quot; </p> </li> <li><p>{{ <strong>numero|is_month_of:data }}</strong> - retorna True se &quot;numero&quot; é o mês de uma &quot;data&quot; </p> </li> <li><p>{{ <strong>numero|is_year_of:data }}</strong> - retorna True se &quot;numero&quot; é o ano de uma &quot;data&quot; </p> </li> <li><p>{{ <strong>numero|is_hour_of:data }}</strong> - retorna True se &quot;numero&quot; é a hora de uma &quot;data&quot; </p> </li> <li><p>{{ <strong>numero|is_minute_of:data }}</strong> - retorna True se &quot;numero&quot; é o minuto de uma &quot;data&quot; </p> </li> <li><p>{{ <strong>numero|is_second_of:data }}</strong> - retorna True se &quot;numero&quot; é o segundo de uma &quot;data&quot; </p> </li> <li><p>{{ <strong>data|dec_year:numero }}</strong> - decrementa &quot;numero&quot; de anos de uma &quot;data&quot; </p> </li> <li><p>{{ <strong>data|dec_month:numero }}</strong> - decrementa &quot;numero&quot; de meses de uma &quot;data&quot; </p> </li> <li><p>{{ <strong>data|inc_year:numero }}</strong> - incrementa &quot;numero&quot; de anos de uma &quot;data&quot; </p> </li> <li><p>{{ <strong>data|inc_month:numero }}</strong> - incrementa &quot;numero&quot; de meses de uma &quot;data&quot; </p> </li> <li><p>{{ <strong>lista|list_as_text }}</strong> - faz exatamente o mesmo que a template filter <em>join</em> faz hoje (na época da criação ela não existia) </p> </li> <li><p>{{ <strong>lista|list_as_text:atributo }}</strong> - faz o mesmo que acima, porém é baseado no atributo informado </p> </li> <li><p>{{ <strong>lista|list_as_links:atributo }}</strong> - faz o mesmo que a template filter <em>list_as_text</em>, entretanto, monta um link para cada uma delas </p> </li> <li><p>{{ <strong>variavel|startswith:outro_valor }}</strong> - retorna True se a string contida em &quot;variavel&quot; inicia com a contida em &quot;outro_valor&quot; </p> </li> <li><p>{{ <strong>variavel|endswith:outro_valor }}</strong> - retorna True se a string contida em &quot;variavel&quot; termina com a contida em &quot;outro_valor&quot; </p> </li> <li><p>{{ <strong>queryset|order_by:&quot;campo1,campo2&quot; }}</strong> - efetua a ordenação de uma queryset </p> </li> </ul> <p>Bom, há ainda alguns widgets, generic views, utilidades para Google Maps API e e o mais importante Model Info (para objeto e para listagem), que equivale ao ModelForm, porém exibe somente as informações para visualização. Entretanto, por hoje vamos ficar por aqui, logo eu publico a segunda parte :) </p> <ol> <li> <a href="http://www.djangosnippets.org/snippets/377/">http://www.djangosnippets.org/snippets/377/</a> </li> <li> <a href="http://code.google.com/p/django-plus/source/checkout">http://code.google.com/p/django-plus/source/checkout</a> </li> <li> <a href="http://www.djangosnippets.org/snippets/798/">http://www.djangosnippets.org/snippets/798/</a> </li> <li> <a href="http://www.djangosnippets.org/snippets/742/">http://www.djangosnippets.org/snippets/742/</a> </li> <li> <a href="http://www.stereoplex.com/two-voices/cookieless-django-sessions-and-authentication-without-cookies">http://www.stereoplex.com/two-voices/cookieless-django-sessions-and-authentication-without-cookies</a> </li> <li> <a href="http://django-plus.googlecode.com/files/django-plus-1.0-stable.tar.gz">http://django-plus.googlecode.com/files/django-plus-1.0-stable.tar.gz</a> </li> </ol> Oportunidade de trabalho para desenvolvedor Djangohttp://marinhobrandao.com/blog/p/oportunidade-de-trabalho-para-desenvolvedor-django/<p>O Sérgio Oliveira publicou a seguinte oportunidade de trabalho:</p> <blockquote> </p>A Interzone Entertainment está em busca de mais um membro para o seu time brasileiro de desenvolvedores web. No momento o time esta envolvido na integração do game Interzone Futebol com a página web <a href="http://www.interzonefutebol.com.br">http://www.interzonefutebol.com.br</a> (Django).</p> Conhecimentos requeridos: <ul> <li>Python</li> <li>Linux</li> <li>SVN</li> <li>Javascript</li> <li>HTML/CSS (W3C standards)</li> <li>SQL</li> <li>Inglês (para leitura)</li> </ul> Conhecimentos desejados: <ul> <li>Django</li> <li>Pylons</li> <li>ORMs</li> <li>JQuery</li> <li>WSGI</li> <li>PHP</li> <li>Postgres</li> <li>Shell Script</li> <li>Inglês fluente</li> </ul> <p>Sobre a Interzone Games: <a href="http://www.interzoneentertainment.com/about/">http://www.interzoneentertainment.com/about/</a></p> <p>Interessados enviem o cv para sergio.campos@interzonegames.com com o assunto "Web/Python"</p> </blockquote>DjangoStack - fácil para o iniciante aprender Djangohttp://marinhobrandao.com/blog/p/djangostack-facil-para-o-iniciante-aprender-django/<p>Muitas pessoas vêm para o Django de diversoss outros frameworks. Outras não passaram por outros frameworks, mas já possuem alguma (ou muita) experiência com o universo do Linux, Python e Software Livre.</p> <p>Entretanto, há um grupo cada vez maior de pessoas que estão conhecendo o Django a partir do nada, ou partindo de linguagens de paradigma muito diferente, como Delphi, Visual Basic, Java ou PHP.</p> <p>Essas pessoas são muito bem-vindas ao framework, mas muitas vezes começam sem uma definição clara de como a coisa funciona no geral, e normalmente o que elas mais enfrentam é uma quantidade muito grande de quebra de paradigmas e novidades, o que acaba deixando qualquer um sem um chao firme onde pisar.</p> <p>É por isso que resolvi fazer esse tutorial sobre iniciar no Django, usando <b>DjangoStack</b>.</p> <h3>Bitnami DjangoStack</h3> <p>O Bitnami é projeto que viabiliza instaladores passo-a-passo completos para algumas ferramentas.</p> <p>Lembra-se do conceito de "LAMP" (Linux + Apache + MySQL + PHP)? Pois é, em cima dessa idéia, surgiram diversas variantes: o WAMP por exemplo, é um dos mais populares.</p> <p>O DjangoStack é a mesma idéia do LAMP; você pega um só instalador, executa, ele abre uma janela e passo por passo você instala e configura seu primeiro projeto em Django, incluindo todo o ambiente que precisa para aprender e desenvolver: Python, Apache, MySQL e SQLite.</p> <h3>Download e versões disponíves</h3> <p>Primeiro de tudo, é preciso baixar o instalador. Ele está disponível para Linux, Windows e MacOSX (plataformas Intel e PowerPC). Portanto, vá até a página de download e escolha a versão desejada:</p> <p><a href="http://bitnami.org/stack/djangostack">http://bitnami.org/stack/djangostack</a></p> <p>Obs: Devido à procura ser maior em torno do Windows, este tutorial será feito usando o Windows como base.</p> <h3>Instalação</h3> <p>Após fazer o download (em torno de 38MB), execute o arquivo de instalação.</p> <p align="center"><img src="http://media.marinhobrandao.com/media/djangostack/djangostack_1.png"/></p> <p>Na janela exibida, clique em "Next" para continuar (mantendo os valores padrão) ate que a seguinte pergunta seja feita:</p> <p><b>"Please configure your Django project"</b></p> <p align="center"><img src="http://media.marinhobrandao.com/media/djangostack/djangostack_5.png"/></p> <p>Informe <b>"meu_projeto"</b>. Deixe o SQLite marcado como banco de dados do projeto e siga adiante.</p> <p>Na janela a seguir, apenas clique no botão "Next" para iniciar a cópia de arquivos.</p> <p align="center"><img src="http://media.marinhobrandao.com/media/djangostack/djangostack_6.png"/></p> <p>Obs: numa próxima vez você poderá informar com mais calma os valores da configuração, modificando de acordo com sua preferência. Entretanto, este tutorial e feito com base nos valores padrão.</p> <p>Ao final da instalação, o Apache e o MySQL serão iniciados e a página inicial será exibida no seu navegador padrão.</p> <p align="center"><img src="http://media.marinhobrandao.com/media/djangostack/djangostack_7.png"/></p> <p>A janela exibida pelo navegador mostrará uma introdução e um link no meio, em letras garrafais: <b>"Access Your Project"</b>.</p> <p align="center"><img src="http://media.marinhobrandao.com/media/djangostack/djangostack_8.png"/></p> <p>Clique no link para visualizar seu projeto em Django rodando pela primeira vez.</p> <p align="center"><img src="http://media.marinhobrandao.com/media/djangostack/djangostack_9.png"/></p> <h3>Primeiro projeto</h3> <p>Se achou tudo fácil demais até aqui, não fique em dúvida, o Django é fácil mesmo, mas agora começa a parte divertida.</p> <p>Abra agora no Explorer o caminho de instalação do DjangoStack (<b>"C:\Arquivos de programas\BitNami DjangoStack"</b>).</p> <p align="center"><img src="http://media.marinhobrandao.com/media/djangostack/djangostack_11.png"/></p> <p>Com a janela aberta, abra a pasta <b>"projects"</b> e em seguida <b>"meu_projeto"</b>. Os arquivos contidos nessa pasta são os arquivos criados para o seu projeto.</p> <p align="center"><img src="http://media.marinhobrandao.com/media/djangostack/djangostack_12.png"/></p> <h3>A primeira aplicação</h3> <p>Agora vamos criar uma pequena aplicação de <b>Anotações</b>.</p> <p>Crie uma pasta chamada <b>"anotacoes"</b> e dentro dela os arquivos vazios: <b>"__init__.py"</b> e <b>"models.py"</b>.</p> <p align="center"><img src="http://media.marinhobrandao.com/media/djangostack/djangostack_13.png"/></p> <p>Agora abra o arquivo <b>"models.py"</b> para edição (pode ser no <b>Bloco de Notas</b> ou em algum outro editor de texto de sua preferencia) e dentro, digite as seguintes linhas:</p> <pre><code>from datetime import datetime from django.db import models class Anotacao(models.Model): titulo = models.CharField(max_length=50) texto = models.TextField() data_hora = models.DateTimeField(default=datetime.now, blank=True) def __unicode__(self): return self.titulo </code></pre> <h3>Configurando as settings do projeto</h3> <p>Agora na pasta superior - a do projeto - edite o arquivo <b>"settings.py"</b>, e acrescente as seguintes linhas no início do arquivo:</p> <pre><code>import os PROJECT_ROOT_PATH = os.path.dirname(__file__) </code></pre> <p>Localize a setting <b>"DATABASE_NAME"</b> e modifique para ficar desta forma:</p> <pre><code>DATABASE_NAME=os.path.join(PROJECT_ROOT_PATH,'meu_projeto.db') </code></pre> <p>Localize a setting <b>"ADMIN_MEDIA_PREFIX"</b> e modifique para ficar desta forma:</p> <pre><code>ADMIN_MEDIA_PREFIX = '/admin_media/' </code></pre> <p>Agora por fim, localize a setting <b>"INSTALLED_APPS"</b> e modifique para ficar desta forma:</p> <pre><code>INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.admin', 'anotacoes', ) </code></pre> <h3>Configurando as URLs do projeto</h3> <p>Após fechar e salvar, edite na mesma pasta o arquivo <b>"urls.py"</b>, e modifique para ficar desta forma:</p> <pre><code>from django.conf.urls.defaults import * from django.contrib import admin admin.autodiscover() urlpatterns = patterns('', (r'^admin/(.*)', admin.site.root), ) </code></pre> <p>Modificações feitas, agora precisamos criar o banco de dados, mas pra isso, será preciso fazer algumas coisas antes.</p> <h3>Ajustando o ambiente de desenvolvimento</h3> <p>Quando se instala o Python no Windows, este deve ser adicionado à variável de ambiente PATH para que o comando "python" seja encontrado de qualquer caminho. Portanto, pressione as teclas <b>"bandeira+Pause"</b> para acessar a janela de <b>"Propriedades do Sistema"</b>, e vá até a aba <b>"Avançado"</b>, clicando no botão <b>"Variáveis de Ambiente"</b>.</p> <p align="center"><img src="http://media.marinhobrandao.com/media/djangostack/djangostack_14.png"/></p> <p>Na caixa <b>"Variáveis do sistema"</b>, localize o item <b>"Path"</b> e clique duas vezes. Será exibida outra janela, e no campo <b>"Valor da variável"</b>, adicione ao final <b>"C:\Arquivos de programas\BitNami DjangoStack\python"</b>. Feche todas as janelas clicando em <b>"Ok"</b>.</p> <p align="center"><img src="http://media.marinhobrandao.com/media/djangostack/djangostack_15.png"/></p> <h3>Gerando o banco de dados</h3> <p>Pronto, agora voltemos à janela do Explorer, posicionado na pasta do projeto. Vamos criar ali um batch para a geração do banco de dados.</p> <p>Crie um arquivo chamado <b>"criar_banco_de_dados.bat"</b> e digite dentro as seguintes linhas:</p> <pre><code>python manage.py syncdb pause </code></pre> <p>Feche o arquivo salvando e clique duas vezes sobre ele.</p> <p>Uma janela do DOS será exibida gerando as tabelas no banco de dados. Responda as perguntas assim:</p> <ul> <li>Would you like to create one now? (yes/no): <b>yes</b></li> <li>Username: <b>admin</b></li> <li>E-mail address: <b>seu_email@dominio.com</b></li> <li>Password: <b>1</b></li> <li>Password (again): <b>1</b></li> </ul> <p align="center"><img src="http://media.marinhobrandao.com/media/djangostack/djangostack_16.png"/></p> <h3>Configurando o Apache</h3> <p>Agora é necessário ajustar as configurações do Apache para que seu ambiente de desenvolvimento possa executar o que precisa.</p> <p>Na janela do explorer, vá até a pasta <b>"C:\Arquivos de programas\BitNami DjangoStack\apps\django\conf"</b> e edite o arquivo <b>"django.conf"</b> modificando para ficar desta forma:</p> <pre><code>&lt;location '/'&gt; SetHandler python-program PythonHandler django.core.handlers.modpython PythonPath "['C:\Arquivos de programas\BitNami DjangoStack/projects/', 'C:\Arquivos de programas\BitNami DjangoStack/projects/meu_projeto'] + sys.path" SetEnv DJANGO_SETTINGS_MODULE meu_projeto.settings PythonDebug On &lt;/location&gt; Alias /admin_media "C:\Arquivos de programas\BitNami DjangoStack\apps\django\django\contrib\admin\media" &lt;Location "/admin_media"&gt; SetHandler None &lt;/Location&gt; </code></pre> <p>Por motivos de performance, um projeto feito em Django não aplica as modificações recebidas automaticamente. É preciso reiniciar o Apache para que elas façam efeito.</p> <p>O DjangoStack não ativa o <b>ApacheMonitor</b> na inicialização do Windows. Portanto, é preciso que você faça isso.</p> <p>Na janela do explorer, vá até a pasta <b>"C:\Arquivos de programas\BitNami DjangoStack\apache2\bin"</b> e clique duas vezes no arquivo executável <b>"ApacheMonitor.exe"</b>. Será exibido um novo ícone na bandeja, ao lado do relógio do Windows.</p> <p>Obs: é recomendável que você crie um atalho do ApacheMonitor para iniciar automaticamente com o Windows, pois assim terá menos trabalho quando precisar reiniciar o Apache.</p> <p>Agora clique sobre o ícone da bandeja, selecione o serviço do Apache e escolha a opção <b>"Restart"</b> para reiniciar o apache.</p> <p align="center"><img src="http://media.marinhobrandao.com/media/djangostack/djangostack_17.png"/></p> <p>Após aguardar alguns segundos, vá até o seu navegador e carregue a URL: <b>"http://localhost/admin/"</b>. O usuário é <b>"admin"</b>, e a senha é <b>"1"</b>.</p> <p align="center"><img src="http://media.marinhobrandao.com/media/djangostack/djangostack_18.png"/></p>Criando Aplicações Plugáveishttp://marinhobrandao.com/blog/p/criando-aplicacoes-plugaveis/<p>Nos dias 18, 19 e 20 de setembro, estive na PyCon Brasil, participando das palestras excelentes que foram apresentadas por diversas pessoas-chave em assuntos relacionados direta ou indiretamente ao Python, Django, Pylons, TurboGears, Plone, Google App Engine, Zope e outros.</p> <p>No sábado, fiz a apresentação da palestra "Django: Criando Aplicações Plugáveis" quando falei sobre a criação de aplicações reusáveis para Django.</p> <p>Segue abaixo o documento dos slides. O <strong>Scribd</strong> permite ainda que você faça download em <a href="http://www.scribd.com/document_downloads/6137369?extension=pdf">formato PDF</a>. Para isso, basta logar</p> <object codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" id="doc_907293569706207" name="doc_907293569706207" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" align="middle" height="500" width="100%"> <param name="movie" value="http://documents.scribd.com/ScribdViewer.swf?document_id=6137369&access_key=key-lwjar4i1j6bw4qs3eat&page=&version=1&auto_size=true&viewMode="> <param name="quality" value="high"> <param name="play" value="true"> <param name="loop" value="true"> <param name="scale" value="showall"> <param name="wmode" value="opaque"> <param name="devicefont" value="false"> <param name="bgcolor" value="#ffffff"> <param name="menu" value="true"> <param name="allowFullScreen" value="true"> <param name="allowScriptAccess" value="always"> <param name="salign" value=""> <embed src="http://documents.scribd.com/ScribdViewer.swf?document_id=6137369&access_key=key-lwjar4i1j6bw4qs3eat&page=&version=1&auto_size=true&viewMode=" quality="high" pluginspage="http://www.macromedia.com/go/getflashplayer" play="true" loop="true" scale="showall" wmode="opaque" devicefont="false" bgcolor="#ffffff" name="doc_907293569706207_object" menu="true" allowfullscreen="true" allowscriptaccess="always" salign="" type="application/x-shockwave-flash" align="middle" height="500" width="100%"></embed> </object><div style="font-size:10px;text-align:center;width:100%"><a href="http://www.scribd.com/doc/6137369/Django-criando-aplicacoes-plugaveis">Django - criando aplicações plugáveis</a> - <a href="http://www.scribd.com/upload">Upload a Document to Scribd</a></div><div style="display:none"> Read this document on Scribd: <a href="http://www.scribd.com/doc/6137369/Django-criando-aplicacoes-plugaveis">Django - criando aplicações plugáveis</a> </div>Lançada a versão 1.0 alpha 2http://marinhobrandao.com/blog/p/lancada-a-versao-10-alpha-2/<div class="document"> <blockquote> <strong>Atenção:</strong> este artigo foi escrito para ser lançado no blog da DjangoBrasil <a class="footnote-reference" href="#id16" id="id1" name="id1">[8]</a> porém, por motivos técnicos, ele está sendo publicado aqui e será republicado lá dentro de algumas horas.</blockquote> <p>&quot;Fantástica&quot; é a palavra mais apropriada para esta última release, lançada ao final do dia desta sexta-feira.</p> <p>A nova release, mais um passo da fiel agenda rumo à versão 1.0 final foi anunciada <a class="footnote-reference" href="#id9" id="id2" name="id2">[1]</a> com 4 novidades de tirar o fôlego, além das centenas de correções e ajustes efetuadas ao longo das últimas duas semanas:</p> <p><strong>Signals refactoring</strong></p> <p>O sistema de signals foi redesenhado em sua totalidade, o que rendeu um resultado assombroso de 90% de melhora na performance. A forma de escrever signals agora mudou, e também mudou - ainda que permaneça suportando a forma antiga - a forma de conectar funções a signals. Veja mais detalhes em <a class="footnote-reference" href="#id10" id="id3" name="id3">[2]</a> .</p> <p>Fica aqui uma atenção especial sobre o <strong>django-tagging</strong>, <strong>django-ads</strong>, <strong>djapian</strong> e outras aplicações plugáveis que fazem uso de signals próprios.</p> <p><strong>GeoDjango</strong></p> <p>O branch para fazer o Django suportar GIS <a class="footnote-reference" href="#id11" id="id4" name="id4">[3]</a> foi finalizado e inserido ao trunk, destacado pela contrib <strong>django.gis</strong>. Essa novidade possibilita a criação de soluções baseadas em bancos de dados geográficos usando Django, o que dá um poder realmente grande ao framework.</p> <p><strong>Armazenamento de arquivos extensível</strong></p> <p>Esta novidade pode ser vista com detalhes em <a class="footnote-reference" href="#id12" id="id5" name="id5">[4]</a>. Agora é possível se criar backends para tratar o armazenamento de campos baseados em arquivo - especialmente <strong>FileField</strong> e <strong>ImageField</strong> - o que agora possibilita o armazenamento em banco de dados, locais remotos e em servidores de cloud computing de uma forma mais conceitualmente correta, sem necessitar de armazenamentos temporários com transferências.</p> <p>Aqui vale um cuidado especial e bons testes antes de migrar seu site ou sistema em produção, é provável que necessite de ajustes.</p> <p><strong>Compatibilidade com Jython</strong></p> <p>A novidade também recente <a class="footnote-reference" href="#id13" id="id6" name="id6">[5]</a> da versão 2.5 do Jython e os ajustes feitos no Django para esta release agora permitem rodar um projeto baseado em Django usando a Java Virtual Machine e explorar assim sua robustez.</p> <p>Outra melhoria conquistada nesta release é a de performance no suporte à internacionalização, que foi ajustado e otimizado. Diversas observações de incompatibilidade surgidas das últimas novidades podem ser encontradas em <a class="footnote-reference" href="#id14" id="id7" name="id7">[6]</a>.</p> <p>Continuamos em grade expectativa para a versão 1.0-final e sempre muito gratos à equipe de desenvolvedores <a class="footnote-reference" href="#id15" id="id8" name="id8">[7]</a>, testadores e lançadores de tickets.</p> <p><strong>Links relacionados</strong></p> <table class="docutils footnote" frame="void" id="id9" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id2" name="id9">[1]</a></td><td><a class="reference" href="http://www.djangoproject.com/documentation/release_notes_1.0_alpha_2/">http://www.djangoproject.com/documentation/release_notes_1.0_alpha_2/</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id10" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id3" name="id10">[2]</a></td><td><a class="reference" href="http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Signalrefactoring">http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Signalrefactoring</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id11" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id4" name="id11">[3]</a></td><td><a class="reference" href="http://geodjango.org/">http://geodjango.org/</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id12" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id5" name="id12">[4]</a></td><td><a class="reference" href="http://www.djangoproject.com/documentation/files/">http://www.djangoproject.com/documentation/files/</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id13" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id6" name="id13">[5]</a></td><td><a class="reference" href="http://fwierzbicki.blogspot.com/2008/07/jython-25-alpha-released.html">http://fwierzbicki.blogspot.com/2008/07/jython-25-alpha-released.html</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id14" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id7" name="id14">[6]</a></td><td><a class="reference" href="http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges">http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id15" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id8" name="id15">[7]</a></td><td><a class="reference" href="http://code.djangoproject.com/browser/django/trunk/AUTHORS">http://code.djangoproject.com/browser/django/trunk/AUTHORS</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id16" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id1" name="id16">[8]</a></td><td><a class="reference" href="http://djangobrasil.org/">http://djangobrasil.org/</a></td></tr> </tbody> </table> </div> Qual é a melhor IDE para Django?http://marinhobrandao.com/blog/p/qual-e-a-melhor-ide-para-django/<div class="document"> <p>Há tempos que venho mantendo o <strong>IMHO</strong> ligado a 100% quando este assunto é tocado por alguém pessoalmente ou na comunidade, mas enfim, resolvi tira-lo do OFF para este artigo, já que a sensação na realidade é de <strong>saco cheio</strong> com esse assunto.</p> <p>Longe de querer evangelizar pessoas a usarem o <strong>gVim</strong> <a class="footnote-reference" href="#id3" id="id1" name="id1">[1]</a> (veja também a lista de editores para Python <a class="footnote-reference" href="#id4" id="id2" name="id2">[2]</a>), a minha intenção aqui é alertar quanto à ilusão que se tem com IDEs e editores, focando em seus recursos preguiçosos no lugar de em suas ferramentas para produtividade <em>real</em>.</p> <p>Há uma visão distorcida generalizada quanto à palavra <strong>produtividade</strong>. A máxima foi evoluída por ferramentas consideradas &quot;produtivas&quot;, como Delphi, Visual Basic, Access e outras ferramentas menos populares onde a competição sempre foi oferecer mais e mais recursos prontos, movimentos de mouse, ferramentas visuais e code-templates.</p> <p>Ocorre que à época que essas ferramentas foram criadas as alternativas eram Clipper, DataFlex, FoxPro e até mesmo Cobol, e na febre do marketing da mudança do ambiente console para ambiente gráfico, foi criada a ilusão de que tudo que é visual é mais rápido e produtivo que em modo texto.</p> <p>Doce ilusão.</p> <p>Uma ferramenta como o Delphi, com milhares de itens no menu e seus complementos é tão fácil de operar quanto o painel colorido e piscante de um Boeing 747 na cabina do piloto. E o pior: <strong>o avião está caindo</strong>.</p> <p>Obviamente, não há comparações entre uma ferramenta exageradamente visual a alguns recursos de editor. Mas a premissa é a mesma, com dosagens diferentes.</p> <p>Mas vamos supor que você goste de <strong>autocomplete</strong>, um dos recursos mais cosiderados quando se procura um editor.</p> <p>A cada vez que você precisar digitar um import, basta digitar os primeiros caracteres e o editor irá exibir uma caixa de seleção com alternativas com o mesmo prefixo e você irá escolher uma das opções.</p> <p>Isso reserva pelo menos quatro reflexões (reserve pelo menos 1 minuto para cada ponto abaixo):</p> <blockquote> <p><strong>A quantidade de vezes que você pressionou uma tecla é realmente menor que a necessária para digitar a expressão manualmente?</strong></p> <p><strong>O trabalho de levar o braço até o mouse de forma a escolher a opção desejada é realmente menor que o trabalho de digitar a expressão manualmente?</strong></p> <p><strong>Se fosse em uma situação em um editor sem esses recursos (no servidor, por exemplo), você conseguiria digitar essa expressão sem dificuldade?</strong></p> <p><strong>A memória, processador e leitura no HD de forma a indexar, localizar e exibir as alternativas é tão curta que compense a espera?</strong></p> </blockquote> <p>Refletiu?</p> <p>Agora leia este meu caso: todas as vezes que eu precisava escrever um signal, eu copiava o bloco de codigo com uma linha de comentário, 2 imports e algumas linhas com um signal qualquer de algum arquivo já pronto e fazia as modificações. Consequência: eu nunca sabia fazer isso sozinho, eu não me lembrava da lógica de funcionamento dos signals, e simplismente não sabia onde no código do framework estariam os signals de models, o que atrapalhava meu raciocínio sobre como os signals funcionam.</p> <p>Tomei a seguinte medida: parei de fazer isso, nas primeiras vezes eu escrevi algumas coisas erradas mas fui acertando mais e mais até isso se tornar uma tarefa rotineira e então eu consegui ter uma visão ampla de como os signals funcionam e como eles estão modularizados.</p> <p>Parece bobo, mas é assim mesmo que funciona. E cá entre nós: <strong>você acha mesmo que procurar um arquivo, abrí-lo e copiar um bloco de codigo semelhante ao que eu precisava era mais produtivo e mais rápido do que digitar manualmente?</strong></p> <p><strong>DRY</strong> fala das coisas que devem ser feitas de forma reutilizável, não do seu hábito em copiar essas coisas e espalhar códigos semelhantes e mal pensados pela aplicação. E DRY também não se trata de ter preguiça de pensar, na verdade DRY existe para distribuir as coisas de forma clara de forma que você possa pensar mais amplamente, mas sem se esquecer da essência.</p> <p><strong>Agora, para finalizar: qual é o editor ou IDE ideal para se trabalhar com Django?</strong></p> <p>Isso - claro - vai depender de seu gosto pessoal, mas ao instalá-lo, desabilite todos os recursos criados para preguiçosos e use o que ele tem de melhor: <strong>teclas de atalho</strong>, <strong>abas</strong>, <strong>buscas por regex</strong>, <strong>integrações com software de versionamento</strong> e o que mais tiver. Faz bem para você.</p> <p>E também não é demais ressaltar: com um raciocínio rápido e bom conhecimento em profundidade, sua produtividade estará relacionada à rapidez que você consegue criar a solução e não na rapidez que consegue escrever uma gambiarra ;)</p> <table class="docutils footnote" frame="void" id="id3" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id1" name="id3">[1]</a></td><td><a class="reference" href="http://www.vim.org/">http://www.vim.org/</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id4" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id2" name="id4">[2]</a></td><td><a class="reference" href="http://www.pythonbrasil.com.br/moin.cgi/IdesPython">http://www.pythonbrasil.com.br/moin.cgi/IdesPython</a></td></tr> </tbody> </table> </div> Oportunidade de trabalho em Barueri-SPhttp://marinhobrandao.com/blog/p/oportunidade-de-trabalho-em-barueri-sp/<div class="document"> <p>O <strong>Rudá</strong> enviou a seguinte mensagem:</p> <blockquote> <p>Oportunidade de trabalho para programador(a) trainee Python</p> <p><strong>Segue os detalhes de vaga</strong></p> <p>Conhecimentos Necessários: Python, HTML, CSS, JavaScript, SQL</p> <p>Conhecimentos Desejáveis: Django, Zope, Plone, PostgreSQL, MSSQL Server, Linux</p> <p>Empresa: Mondial Eletrodomésticos (www.mondialline.com.br)</p> <p>Local de trabalho: Alphaville, Barueri - SP</p> <p>Carga horária: de 6 a 8 hs.</p> <p>Salário variando de R$ 1.500,00 à 2.500,00, dependendo da carga horária e do conhecimento do candidato.</p> <p>Foco de trabalho: análise de requisitos e desenvolvimento de aplicação web gerencial para extração de relatórios de faturamento/produção/custos.</p> <p>Obs: a vaga inicialmente é temporária (4 a 6 mêses) mas existe chance de efetivação</p> <p>Interessados favor enviar CV com pretensão salarial para: ruda em mondialline ponto com ponto br</p> </blockquote> </div> Oportunidade de trabalho em Fortalezahttp://marinhobrandao.com/blog/p/oportunidade-de-trabalho-em-fortaleza/<div class="document"> <p>O <strong>Nicholas</strong> enviou o seguinte anúncio:</p> <blockquote> <p>a <strong>gNial</strong> está contratando 1 (um) programador para atuar em Fortaleza-Ceará que possua os seguintes conhecimentos:</p> <pre class="literal-block"> * Python * Django * XML </pre> <p>Desejável:</p> <pre class="literal-block"> * FreeSwitch </pre> <p>Salário de acordo com o perfil.</p> <p>Favor enviar currículo para nicholas em gnial ponto com ponto br</p> </blockquote> <p>Veja também</p> <p><a class="reference" href="http://gnial.com.br/">http://gnial.com.br/</a></p> </div> Agora com domínio novohttp://marinhobrandao.com/blog/p/agora-com-dominio-novo/<div class="document"> <p>Before all, an english resume: my apologizes for the recent duplicates of entries in Django Community site. This ocurred because I changed the permalinks to slugs (they was id-based) and now I changed the domain. But I promise this won't happen again for the near time :)</p> <p>Bom, quem assina os feeds do blog (algo em torno de 50 pessoas diretamente, mais os assinantes do Django Brasil Planeta e do Django Community) deve ter notado uma anomalia nos últimos dias.</p> <p>Por duas vezes, os artigos tiveram seus permalinks alterados (a primeira vez por conta dos slugs e a segunda por conta do domínio), e como o permalink é a chave única para os leitores RSS, isso fez com que os últimos 20 artigos fossem contados como novos.</p> <p>Acontece que agora estou de domínio novo: <a class="reference" href="http://marinhobrandao.com/">http://marinhobrandao.com/</a> e isto causou o rebuliço pela segunda vez. Então ficam aqui meu sincero pedido de desculpas pelo ocorrido e também o pedido para que anote o novo endereço.</p> <p>Todas as URLs do antigo endereço estão apontadas para as respectivas no novo endereço, com redirect permanente de código 301, portanto, tudo continua funcionando normalmente, mas é recomendável utilizar o novo endereço :)</p> </div> Lançada a versão 1.0 alphahttp://marinhobrandao.com/blog/p/lancada-a-versao-10-alpha/<div class="document"> <p>Ontem ao fim do dia foi anunciada a versão 1.0 alpha <a class="footnote-reference" href="#id3" id="id1" name="id1">[1]</a>, que é o primeiro passo para a release 1.0 final, a ser lançada em setembro, na <strong>DjangoCon</strong>.</p> <p>A maior parte das <em>features</em> da versão 1.0 tem sido utilizada há meses (algumas há anos) através da versão trunk, pois havia muito tempo que a versão 0.96 foi liberada e a maior parte dos desenvolvedores usam a versão do trunk em produção.</p> <p>O que podemos destacar nesta versão</p> <ul class="simple"> <li><strong>Suporte a Unicode</strong> - a versão anterior não era 100% unicode, o que gerava algumas dificuldades na implantação e distorções entre máquina de desenvolvimento e servidor em produção. Essa feature mudou definitivamente o nosso trabalho pois a partir do momento em que ela foi liberada no trunk, deixamos de nos preocupar com codificação de caracteres e o trabalho passou a render mais.</li> <li><strong>Escape como default nas variáveis no template</strong> - esta feature fez uma diferença fundamental na segurança das aplicações, uma inversão pequena que melhorou bastante os resultados dos projetos.</li> <li><strong>ORM refatorado com heranças de modelo</strong> - há 2 ou 3 meses essa feature foi liberada, possibilitando trabalhar com heranças de classes de modelo, tanto de forma abstrata quanto de forma distribuída em tabelas, isso possibilita que você tenha classes de modelo que são baseadas em outras e seus dados são exibidos de forma transparente.</li> <li><strong>Admin baseado em NewForms</strong> - a última grande novidade, liberada no último sábado foi o merge do branch <strong>newforms-admin</strong>, que traz consigo todo o Admin refatorado para ser modular e compatível com NewForms. Assim, todo o acoplamento entre classes de modelo e admin foi removido e foi definido um módulo <strong>admin.py</strong> para conter as classes e definições da aplicação para o Admin. Com o novo admin é possível trabalhar de forma muito mais profissional com sistemas diversos, customizar sua interface de administração do site, adicionar recursos de Ajax, etc.</li> </ul> <p>As próximas versões a serem lançadas na programação da 1.0 serão: 1.0 beta 1, 1.0 beta 2, 1.0 RC 1, 1.0 RC 2 e finalmente, a 1.0 final, prevista para o dia 2 de setembro, na DjangoCon.</p> <p><strong>Vantagens da versão 1.0</strong></p> <p>A versão 1.0 é anciosamente esperada pois com o crescimento exorbitante do framework neste último ano fez com que ele ultrapassasse a fronteira técnica, chegando até os diretores de tecnologia, empresários e grandes empresas. Desta forma, uma versão padrão vai permitir uma melhor profissionalização do uso do framework, tanto no uso do Google App Engin <a class="footnote-reference" href="#id4" id="id2" name="id2">[2]</a> quanto no treinamento de profissionais, edição de livros, redação de cursos, etc.</p> <p><strong>Links relacionados</strong></p> <table class="docutils footnote" frame="void" id="id3" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id1" name="id3">[1]</a></td><td><a class="reference" href="http://www.djangoproject.com/documentation/release_notes_1.0_alpha/">http://www.djangoproject.com/documentation/release_notes_1.0_alpha/</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id4" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id2" name="id4">[2]</a></td><td><a class="reference" href="http://code.google.com/appengine/">http://code.google.com/appengine/</a></td></tr> </tbody> </table> </div> Load balancing e Cache com MySQL Proxyhttp://marinhobrandao.com/blog/p/load-balancing-e-cache-com-mysql-proxy/<div class="document"> <p>Que tal aprender uma nova linguagem enquanto faz uma boa tarefa para melhorar a escalabilidade do banco de dados?</p> <p>Bom, foi isso que eu fiz hoje durante quase todo o dia. Descobri no início do dia que Lua é a linguagem de script do MySQL Proxy e mergulhei pra fazer duas coisas bastante interessantes: <strong>Load Balancing</strong> e <strong>Cache de resultados</strong>.</p> <p><strong>Load Balancing</strong></p> <p>Eu conheço 3 formas de fazer load balancing com MySQL:</p> <blockquote> <ul class="simple"> <li>MySQL Cluster <a class="footnote-reference" href="#id21" id="id1" name="id1">[1]</a></li> <li>MySQL Proxy <a class="footnote-reference" href="#id22" id="id2" name="id2">[2]</a></li> <li>MySQL Master/Slave <a class="footnote-reference" href="#id23" id="id3" name="id3">[3]</a></li> </ul> </blockquote> <p>Mas na verdade não conhecia em profundidade nenhum deles, apenas uma idéia superficial.</p> <p>No final de semana testei o Master/Slave. Parece ser o mais poderoso dentre os três métodos, mas usá-lo no Django significa mexer em boa parte do código do ORM. Não é a minha intenção. Cheguei a construir um backend pra isso, em cima do backend do MySQL, e até descobri que havia outro semelhante <a class="footnote-reference" href="#id24" id="id4" name="id4">[4]</a>. Mas nem o meu backend improvisado, nem o do <strong>Ivan Sagalaev</strong> me seduziram: muito limitado e cheio de falhas de design.</p> <p>Pois bem, o MySQL Cluster também me desanimou depois que eu li diversos depoimentos contrários <a class="footnote-reference" href="#id25" id="id5" name="id5">[5]</a>.</p> <p>Sobrou o MySQL Proxy.</p> <p><strong>MySQL Proxy</strong></p> <p>Este é um software um tanto recente da MySQL, que basicamente faz a ponte entre o(s) servidor(es) de MySQL e a sua aplicação. É vantajoso pois é independente de linguagem ou framework: você desenha as regras e o que vem depois delas pouco importa, funciona da mesma forma.</p> <img alt="http://marinho.webdoisonline.com/blog/p/diagrama_mysql_proxypng_172/?img=1" src="http://marinho.webdoisonline.com/blog/p/diagrama_mysql_proxypng_172/?img=1" /> <p>Não quero explicar como instala e usa este software, portanto vou me limitar ao script que montei para fazer load balancing. Não foi devidamente testado e em algumas situações foi exibido o erro <strong>(1105, '#07000(proxy) all backends are down')</strong>, portanto, antes de ir migrando seu servidor, faça muitos testes e verifique mais detalhes.</p> <p>O script, feito em Lua, ficou assim</p> <pre><code>function connect_server() local num = tonumber(os.date("%S")) % 2 proxy.connection.backend_ndx = num + 1 print("Using " .. proxy.backends[proxy.connection.backend_ndx].address) end</code></pre><p>Salve o arquivo como <strong>load_balancing.lua</strong> e execute da seguinte forma:</p> <pre><code>mysql-proxy \ --proxy-lua-script=load_balancing.lua \ --proxy-backend-addresses=127.0.0.1:3306 \ --proxy-backend-addresses=vs2:3306</code></pre><p>O parâmetro <strong>--proxy-backend-addresses</strong> pode ser repetido quantas vezes quiser, um para cada réplica do MySQL, quanto mais réplicas, mais poderoso é o seu &quot;cluster&quot;.</p> <p>Os hosts &quot;127.0.0.1&quot; e &quot;vs2&quot; são respectivamente, minha máquina e uma máquina virtual rodando no <strong>vmware-server</strong>, portanto, use os IPs ou hostnames conforme a sua realidade.</p> <p>Ele não suporta master/slave (não encontrei nada na documentação que orientasse como fazer nesse caso), portanto foi necessário configurar os hosts como master/master, que você pode ver como fazer em <a class="footnote-reference" href="#id26" id="id6" name="id6">[6]</a></p> <p>O funcionamento é assim: nos segundos pares as conexões são repassadas ao servidor 1, e nas ímpares as conexões são repassadas para o servidor 2. Se houvessem 10 servidores, o módulo de 2 (<strong>local num = tonumber(os.date(&quot;%S&quot;)) % 2</strong>) seria feito com 10 (<strong>local num = tonumber(os.date(&quot;%S&quot;)) % 10</strong>) e o comando de chamada do mysql-proxy teria 10 vezes o parâmetro <strong>--proxy-backend-addresses</strong>. Simples né?</p> <p><strong>Cache em memória</strong></p> <p>Bom, eu já havia feito o mesmo tipo de coisa hackeando a QuerySet e criando o método .cache() <a class="footnote-reference" href="#id27" id="id7" name="id7">[7]</a>, que me ajudou muito. Esta solução que eu criei hoje pode ser considerado inferior à anterior por oferecer menos granularidade e funcionar somente com MySQL, mas caso você não queira modificar o código original do Django, esta pode ser uma boa alternativa para você.</p> <p>Este segundo script funciona da seguinte forma: quando uma consulta do tipo SELECT é feita ao banco de dados, seu resultado é armazenado em um servidor <strong>Memcached</strong>, com tempo de expiração definido por pattern (cada pattern, ou seja, cada modelo de SELECT pode possuir um tempo de expiração diferente) e se uma cosulta idêntica for requisitada dentro do tempo de expiração, o servidor de cache será consultado, ao invés do banco de dados.</p> <p>Bacana né?</p> <p>Então lá vai</p> <pre><code>-- Packages required require("Memcached") -- http://luamemcached.luaforge.net/ require("json") -- http://www.chipmunkav.com/downloads/Json.lua -- Connect to memcached server local conn = Memcached.Connect('localhost', 11211) -- Default expire time for cache items local default_expire_time = 30 -- Prefix for cache keys local key_prefix = 'mysql-proxy-' -- Patterns to define expiration time for different types of queries. -- More details in: http://lua-users.org/wiki/PatternsTutorial local patterns_expire_time = { {'^%s*select .+from .*auth_user', 150}, {'^%s*select', default_expire_time}, } -- Converts a string to valid key function encode_key(str) return key_prefix .. string.gsub(str, ' ', '-') end -- Converts a resultset to JSON. This can't be done by -- Json library directly because it's a userdata datatype -- instance function resultset_to_str(resultset) local rfields = resultset.fields local rrows = resultset.rows local fields = {} local rows = {} local pos = 1 -- Rows for row in rrows do rows[pos] = row pos = pos + 1 end -- Fields pos = 1 for i = 0, #rfields do if rfields[i] then fields[pos] = { type = rfields[i].type, name = rfields[i].name, } end pos = pos + 1 end return Json.Encode({ fields = fields, rows = rows, }) end -- Callback called before request database server function read_query(packet) if string.byte(packet) == proxy.COM_QUERY then local sql = string.sub(packet, 2) if string.match(string.lower(sql), '^%s*select') then -- Transform to valid key string local key = encode_key(sql) -- Gets from cache local rset = conn:get(key) -- If not found in cache, requests from database server if rset == nil then proxy.queries:append(1, packet) return proxy.PROXY_SEND_QUERY end -- Print out print('from cache', key) -- Json -> table rset = Json.Decode(rset) -- Todo: check for error returns proxy.response.type = proxy.MYSQLD_PACKET_OK proxy.response.resultset = rset return proxy.PROXY_SEND_RESULT end end end -- Callback called after request to database server function read_query_result(inj) local res = resultset_to_str(inj.resultset) local sql = string.lower(string.sub(inj.query, 2)) local key = encode_key(sql) local expire_time = default_expire_time -- Looks at patterns for respective expire time for i = 0, #patterns_expire_time do if patterns_expire_time[i] and string.match(sql, patterns_expire_time[i][1]) then expire_time = patterns_expire_time[i][2] break end end -- Saves to cache conn:set(key, res, expire_time) end</code></pre><p>Lá no início, as linhas que definem <strong>local conn</strong> e <strong>local patterns_expire_time</strong> devem ser ajustadas à sua realidade (endereço do servidor e patterns). Para saber como definir expressões regulares em Lua veja em <a class="footnote-reference" href="#id28" id="id8" name="id8">[8]</a> (é um pouquinho diferente do convencional).</p> <p>Este script depende de dois pacotes externos à linguagem: <strong>json.lua</strong> <a class="footnote-reference" href="#id29" id="id9" name="id9">[9]</a> e <strong>Memcached.lua</strong> <a class="footnote-reference" href="#id30" id="id10" name="id10">[10]</a>, que por sua vez depende do <strong>LuaSocket</strong> <a class="footnote-reference" href="#id31" id="id11" name="id11">[11]</a>.</p> <p>No Ubuntu, quando se instala o pacote <strong>mysql-proxy</strong> a lib da Lua 5.0 é instalada também, mas acontece que os pacotes que eu citei acima - especialmente o LuaSocket - não funcionam corretamente com a versão 5.0.</p> <p>A solução foi instalar a versão 5.1 em paralelo (o LuaSocket possui um pacote no Ubuntu chamado <strong>liblua5.1-socket2</strong>) e eliminar a pasta antiga de bibliotecas da versão 5.0 (<strong>/usr/share/lua/50/</strong>), criando um symlink da versão 5.1 (<strong>/usr/share/lua/5.1/</strong>) com o mesmo nome.</p> <p>Ainda foi necessário definir a seguinte variável de ambiente</p> <pre><code>export LUA_INIT=@/usr/share/lua/50/compat-5.1.lua</code></pre><p>Por fim, para dar vida ao script, basta executar</p> <pre><code>mysql-proxy \ --proxy-lua-script=cached_queries.lua \ --proxy-backend-addresses=127.0.0.1:3306</code></pre><img alt="http://marinho.webdoisonline.com/blog/p/tela_mysql_proxypng/?img=1" src="http://marinho.webdoisonline.com/blog/p/tela_mysql_proxypng/?img=1" /> <p>Ao executar este comando, será aberta a porta <strong>4040</strong> que deve ser setada na setting <strong>DATABASE_PORT</strong>. Caso queira saber como sobrepor a porta 3306, veja em <a class="footnote-reference" href="#id39" id="id12" name="id12">[19]</a>.</p> <p>Uma observação importante: o MySQL possui um bug <a class="footnote-reference" href="#id32" id="id13" name="id13">[12]</a> (ou sei lá o que é) que obriga conexões para &quot;localhost&quot; serem via porta 3306, isso vale tanto para o client quanto para o pacote MySQLdb do Python. Você faz uma conexão para a porta 1365465321 ou qualquer outra e ele aponta para 3306. Portanto, ao usar a porta 4040, mude a setting <strong>DATABASE_HOST</strong> para <strong>&quot;127.0.0.1&quot;</strong> caso esteja usando <strong>&quot;localhost&quot;</strong>.</p> <p>No script acima há ainda uma séria limitação quanto ao tamanho da SELECT. Caso ela tenha mais que 245 caracteres, você terá um erro de tamanho da chave no cache. A solução é converter a expressão SELECT com MD5, SHA ou outor algorítimo que crie uma string única em cima de uma SELECT complexa. Eu não consegui fazer isso <strong>ainda</strong>, mas <strong>recomendo expressamente que faça esse ajuste eu espere que eu o faça antes de colocar em um servidor de produção</strong>.</p> <p>Para mais detalhes sobre MySQL Proxy, veja em <a class="footnote-reference" href="#id33" id="id14" name="id14">[13]</a>, <a class="footnote-reference" href="#id34" id="id15" name="id15">[14]</a>, <a class="footnote-reference" href="#id35" id="id16" name="id16">[15]</a> e <a class="footnote-reference" href="#id36" id="id17" name="id17">[16]</a></p> <p>Para mais detalhes sobre Lua, veja em <a class="footnote-reference" href="#id37" id="id18" name="id18">[17]</a> e <a class="footnote-reference" href="#id38" id="id19" name="id19">[18]</a>.</p> <p>É isso aí... artigo feito no fim da noite tem quer ser rápido assim. Dúvidas, é só falar :)</p> <p>PS: apesar de serem muito úteis para quem usa Django, todas essas configurações são compatíveis com qualquer linguagem ou sistema operacional, e com versões igual ou acima de 5.1 do MySQL.</p> <p>PS2: agradeço ao <strong>Javier Guerra</strong> e <strong>David Given</strong> pelos esclarecimentos sobre a arquitetura da Lua e ao <strong>Giuseppe Maxia</strong> pelo bom tutorial <a class="footnote-reference" href="#id39" id="id20" name="id20">[19]</a> que desenvolvou sobre MySQL Proxy</p> <p>PS3: essa foi a primeira vez na vida que escrevi algo em Lua, por favor, sinta-se à vontade para apontar eventuais falhas</p> <p><strong>Atualização:</strong> removendo a variável de ambiente LUA_INIT, o mysql-proxy rodou normalmente sobre a versão 5.1.</p> <p><strong>Links relacionados</strong></p> <table class="docutils footnote" frame="void" id="id21" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id1" name="id21">[1]</a></td><td><a class="reference" href="http://dev.mysql.com/downloads/cluster/index.html">http://dev.mysql.com/downloads/cluster/index.html</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id22" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id2" name="id22">[2]</a></td><td><a class="reference" href="http://forge.mysql.com/wiki/MySQL_Proxy">http://forge.mysql.com/wiki/MySQL_Proxy</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id23" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id3" name="id23">[3]</a></td><td><a class="reference" href="http://dev.mysql.com/doc/refman/5.1/en/connector-j-reference-replication-connection.html">http://dev.mysql.com/doc/refman/5.1/en/connector-j-reference-replication-connection.html</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id24" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id4" name="id24">[4]</a></td><td><a class="reference" href="http://softwaremaniacs.org/soft/mysql_cluster/en/">http://softwaremaniacs.org/soft/mysql_cluster/en/</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id25" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id5" name="id25">[5]</a></td><td><a class="reference" href="http://blog.globoi.com/producao/2008/04/16/brasileiros-na-mysql-conference/">http://blog.globoi.com/producao/2008/04/16/brasileiros-na-mysql-conference/</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id26" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id6" name="id26">[6]</a></td><td><a class="reference" href="http://www.howtoforge.org/mysql_master_master_replication">http://www.howtoforge.org/mysql_master_master_replication</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id27" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id7" name="id27">[7]</a></td><td><a class="reference" href="http://marinho.webdoisonline.com/blog/p/metodo-cache-para-queryset_158/">http://marinho.webdoisonline.com/blog/p/metodo-cache-para-queryset_158/</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id28" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id8" name="id28">[8]</a></td><td><a class="reference" href="http://lua-users.org/wiki/PatternsTutorial">http://lua-users.org/wiki/PatternsTutorial</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id29" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id9" name="id29">[9]</a></td><td><a class="reference" href="http://www.chipmunkav.com/downloads/Json.lua">http://www.chipmunkav.com/downloads/Json.lua</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id30" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id10" name="id30">[10]</a></td><td><a class="reference" href="http://luamemcached.luaforge.net/">http://luamemcached.luaforge.net/</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id31" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id11" name="id31">[11]</a></td><td><a class="reference" href="http://www.tecgraf.puc-rio.br/~diego/professional/luasocket/">http://www.tecgraf.puc-rio.br/~diego/professional/luasocket/</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id32" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id13" name="id32">[12]</a></td><td><a class="reference" href="https://bugs.launchpad.net/ubuntu/+source/mysql-dfsg-5.0/+bug/241802">https://bugs.launchpad.net/ubuntu/+source/mysql-dfsg-5.0/+bug/241802</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id33" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id14" name="id33">[13]</a></td><td><a class="reference" href="http://del.icio.us/marinho/mysql+escalabilidade">http://del.icio.us/marinho/mysql+escalabilidade</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id34" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id15" name="id34">[14]</a></td><td><a class="reference" href="http://dev.mysql.com/doc/refman/5.1/en/mysql-proxy.html">http://dev.mysql.com/doc/refman/5.1/en/mysql-proxy.html</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id35" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id16" name="id35">[15]</a></td><td><a class="reference" href="http://dev.mysql.com/doc/refman/5.1/en/mysql-proxy-scripting.html">http://dev.mysql.com/doc/refman/5.1/en/mysql-proxy-scripting.html</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id36" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id17" name="id36">[16]</a></td><td><a class="reference" href="http://classdump.org/articles/2008/02/14/mysql-proxy-enhancements">http://classdump.org/articles/2008/02/14/mysql-proxy-enhancements</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id37" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id18" name="id37">[17]</a></td><td><a class="reference" href="http://www.lua.org/manual/5.1/pt/">http://www.lua.org/manual/5.1/pt/</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id38" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id19" name="id38">[18]</a></td><td><a class="reference" href="http://lua-users.org/wiki/TutorialDirectory">http://lua-users.org/wiki/TutorialDirectory</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id39" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a name="id39">[19]</a></td><td><em>(<a class="fn-backref" href="#id12">1</a>, <a class="fn-backref" href="#id20">2</a>)</em> <a class="reference" href="http://dev.mysql.com/tech-resources/articles/proxy-gettingstarted.html">http://dev.mysql.com/tech-resources/articles/proxy-gettingstarted.html</a></td></tr> </tbody> </table> </div> Migrando do mod_python para o mod_wsgihttp://marinhobrandao.com/blog/p/migrando-do-mod_python-para-o-mod_wsgi/<div class="document"> <p>Já faz alguns meses que eu queria fazer alguns testes com o mod_wsgi, a alternativa ao mod_python que vem sendo cada vez mais comentada e sugerida na comunidade Django.</p> <p>Ontem eu aproveitei o dia tranquilo pra unir o útil ao agradável e colocar isso em prática.</p> <p><strong>O que é WSGI?</strong></p> <p>Vamos &quot;começar do começo&quot;: WSGI é mais simples do que parece, trata-se de uma interface definida pela PEP 333 <a class="footnote-reference" href="#id3" id="id1" name="id1">[1]</a> para intermediar a comunicação entre servidores web e frameworks Python. Ela surgiu pela necessidade qualquer um de nós nota com um pouco tempo: Python tem quilos de frameworksmuitas delas excelentes, e às vezes o mod_python parece ser um ornitorrinco em um ninho de coelhos quando implantamos alguns sites. É esquisito.</p> <img alt="/blog/p/diagrama_apache_wsgi_djangopng/?img=1" src="/blog/p/diagrama_apache_wsgi_djangopng/?img=1" /> <p><strong>Mudando para o mod_wsgi</strong></p> <p>Para usar o WSGI puramente, é bastante simples, vamos fazer um pequeno script para ilustrar</p> <pre><code>def application(environ, start_response): status_code = '200 OK' headers = [('Content-Type', 'text/html')] start_response(status_code, headers) return ['So <b>testando</b>!'] from paste import httpserver httpserver.serve(application, port='8000')</code></pre><p>Ao executar este script minúsculo e carregar em seu navegador a URL &quot;<a class="reference" href="http://localhost:8000">http://localhost:8000</a>&quot;, será exibido &quot;So <strong>testando</strong>&quot;. Simples não? Veja</p> <pre><code>$ python my_app.py serving on http://127.0.0.1:8000</code></pre><img alt="/blog/p/teste_wsgigif/?img=1" src="/blog/p/teste_wsgigif/?img=1" /> <p>No Django, a coisa exige só um pouco mais de detalhes, mas nada complexo. Como já explanado há poucos dias pelo Eric, da Metaphormedia, em <a class="footnote-reference" href="#id4" id="id2" name="id2">[2]</a>, é recomendável que se crie dentro de seu projeto uma pasta &quot;apache&quot; com um script dentro chamado &quot;django.wsgi&quot; - estes nomes são escolha sua, mas eles têm sido usados por mais de uma referência. Com o conteúdo abaixo (modificado segundo as suas necessidades)</p> <pre><code>#!/usr/bin/env python import os, sys sys.path.insert(0, os.path.abspath(os.path.dirname(__file__)+'/../')) os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' import django.core.handlers.wsgi application = django.core.handlers.wsgi.WSGIHandler()</code></pre><p>E configure seu apache, removendo o código que implementa o mod_python e adicionando o seguinte</p> <pre><code>WSGIScriptAlias / /caminho/do/seu/projeto/apache/django.wsgi</code></pre><p>Agora, para funcionar, é preciso que seu sistema operacional tenha o <strong>mod_wsgi</strong> instalado. No Ubuntu/Debian você instala com o seguinte comando</p> <pre><code>$ sudo apt-get install libapache2-mod-wsgi</code></pre><p>Pronto, ao reiniciar o Apache, seu projeto estará lá, bonitão, rodando através do WSGI :)</p> <p><strong>Porque mod_wsgi?</strong></p> <p>Bom, essa deve ser a pergunta que você deve estar se fazendo. Se o mod_python sempre foi a alternativa recomendada e funciona bem, porque mudar para o mod_wsgi?</p> <p>O mod_wsgi tem se comportado mais escalável que o mod_python. Por escalabilidade, entende-se a capacidade de um site de suportar o crescimento de uso sem perder a performance ou travar.</p> <p>Ao ajustar um servidor para usar o mod_wsgi, pude notar essas diferenças. Elas não são tão grandes que se possa perceber com um simples teste com Apache Bench. É necessário forçar para notar a diferença, que tem sido até 10% superior.</p> <p>Esse servidor que usei para fazer esses testes é uma VM (em Xen) com 512MB de RAM, rodando em um amd64, numa rede bastante estável, em Londres, enquanto que o Apache Bench foi executado em minha máquina.</p> <p>Veja abaixo como os dois modos se comportaram:</p> <p>Para compreender a legenda, o número após o &quot;n&quot; trata-se da quantidade de requisições enviadas e o número após o &quot;c&quot; trata-se da quantidade de requisições concorrentes, ou seja, &quot;n1000c5&quot; equivale a &quot;1000 requisições enviadas de 5 em 5).</p> <p><strong>Tempo de resposta por requisição</strong></p> <img alt="/blog/p/tempo_de_resposta_por_requisicaopng/?img=1" src="/blog/p/tempo_de_resposta_por_requisicaopng/?img=1" /> <p><strong>Requisições por segundo</strong></p> <img alt="/blog/p/requisicoes_por_segundopng/?img=1" src="/blog/p/requisicoes_por_segundopng/?img=1" /> <p><strong>Load de 1 minuto</strong></p> <img alt="/blog/p/load_de_1_minutopng/?img=1" src="/blog/p/load_de_1_minutopng/?img=1" /> <p><strong>Load de 5 minutos</strong></p> <img alt="/blog/p/load_de_5_minutospng/?img=1" src="/blog/p/load_de_5_minutospng/?img=1" /> <p><strong>RSS / Memória real ocupada</strong></p> <img alt="/blog/p/rss_memoria_ocupadapng/?img=1" src="/blog/p/rss_memoria_ocupadapng/?img=1" /> <p><strong>Percentual da CPU</strong></p> <img alt="/blog/p/percentual_da_cpupng/?img=1" src="/blog/p/percentual_da_cpupng/?img=1" /> <p><strong>Conclusões</strong></p> <p>Todos os números apresentados acima não podem ser tomados como base exata para comparação, por alguns motivos, como a influência que o teste anterior efetua no subsequente, a oscilação da internet e a influência de outros processos.</p> <p>Eu fiz questão de usar um servidor em produção onde está sendo servido um outro sistema da empresa, que mesmo que não estivesse em seu horário de pico, era usado normalmente por alguns usuários.</p> <p>Há ainda toda a questão de controvérsias em torno do uso real da CPU e da memória.</p> <p>Mas dá pra notar uma realidade clara: o mod_swgi se mostra mais vantajoso em memória e tempo de resposta. Não esqueçamos que 200 requisições concorrentes para um VPS com 512MB de RAM é uma senhora carga, equivalente a alguns milhões de pageviews por mês.</p> <p>No teste de 2000 requisições a 200 concorrentes, ficou visível que a configuração de MaxRequestsPerChild (de 1000) entrou em cena e fez diferença no desempenho, tanto para segurar a carga quando para mudar um pouco a evolução do desempenho.</p> <p>Por fim, devo avisar que no teste seguinte, de 5000 requisições a 200 concorrentes, o Apache caiu antes de completar 3000, em 3 tentativas.</p> <p>Mais detalhes sobre o WSGI podem ser encontrados em [4], [5] e [6]</p> <p><strong>Links relacionados</strong></p> <table class="docutils footnote" frame="void" id="id3" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id1" name="id3">[1]</a></td><td><a class="reference" href="http://www.python.org/dev/peps/pep-0333/">http://www.python.org/dev/peps/pep-0333/</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id4" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id2" name="id4">[2]</a></td><td><a class="reference" href="http://www.ericholscher.com/blog/2008/jul/8/setting-django-and-mod_wsgi/">http://www.ericholscher.com/blog/2008/jul/8/setting-django-and-mod_wsgi/</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id5" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a name="id5">[3]</a></td><td><a class="reference" href="http://code.google.com/p/modwsgi/wiki/IntegrationWithDjango">http://code.google.com/p/modwsgi/wiki/IntegrationWithDjango</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id6" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a name="id6">[4]</a></td><td><a class="reference" href="http://www.slideshare.net/hdiogenes/wsgi-a-resposta-para-a-questo-definitiva-sobre-python-a-web-e-tudo-mais-368429?src=embed">http://www.slideshare.net/hdiogenes/wsgi-a-resposta-para-a-questo-definitiva-sobre-python-a-web-e-tudo-mais-368429?src=embed</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id7" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a name="id7">[5]</a></td><td><a class="reference" href="http://en.wikipedia.org/wiki/Wsgi">http://en.wikipedia.org/wiki/Wsgi</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id8" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a name="id8">[6]</a></td><td><a class="reference" href="http://www.wsgi.org/wsgi/">http://www.wsgi.org/wsgi/</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id9" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a name="id9">[7]</a></td><td><a class="reference" href="http://del.icio.us/marinho/wsgi">http://del.icio.us/marinho/wsgi</a></td></tr> </tbody> </table> </div> Entrevista com Jacob Kaplan-Mosshttp://marinhobrandao.com/blog/p/entrevista-com-jacob-kaplan-moss/<div class="document"> <p>Esse é um cara que eu admiro pela simplicidade e capacidade de liderança que demonstra a cada dia onde eu tenho a oportunidade de acompanhar.</p> <p>Esta é a minha primeira entrevista e eu queria tirar minhas principais dúvidas envolvendo necessidades de grandes portais e sistemas corporativos, além de saber um pouco mais sobre as novidades mais quentes dos últimos mêses: Django Foundation, DjangoCon e versão 1.0.</p> <p>Segue abaixo a gostosa conversa que tive com Jacob Kaplan-Moss, que poderia ter se extendido por mais um bom tempo, se não fosse a necessidade de parar por aí :)</p> <p>Espero que gostem.</p> <p><strong>Marinho:</strong> bom, como eu disse, sou brasileiro, e o Brasil já tem muitos djangonautas, muita gente usando (o Django) para soluções corporativas e algumas grandes empresas começando a usá-lo em alguns projetos em fase inicial... a comunidade está crescendo e nós muito felizes :) então, minhas perguntas são relacionadas à nossa realidade.</p> <p>e antes de tudo, eu gostaria de deixar você sabendo que meu inglês não é dos melhores, então, tenha paciência comigo :)</p> <p><strong>Jacob:</strong> Seu inglês está <strong>legal</strong>, não se preocupe!</p> <p>É muito legal ouvir sobre o Django sendo usado ao redor do mundo; me faz realmente feliz.</p> <p><strong>Marinho:</strong> Eu noto :) você me respondeu com uma disponibilidade bacana, você parece ser um cara com boas habilidades em liderança. Ao longo da história do Django, você teve alguma situação nas relações pessoais que te desafiou?</p> <p>Digo: uma situação quando você perdeu a paciência ou uma discussão ruim...</p> <p><strong>Jacob:</strong> Yeah, certamente aconteceu!</p> <p><strong>Marinho:</strong> as recentes surpresas (para nós) como a DjangoCon <a class="footnote-reference" href="#id14" id="id1" name="id1">[5]</a> e a Django Foundation <a class="footnote-reference" href="#id13" id="id2" name="id2">[4]</a> tiveram algum desses desafios?</p> <p><strong>Jacob:</strong> Nós tentamos realmente manter um tom amigável (ou ao menos educado), mas... às vezes há pessoas que são realmente difíceis de se lidar.</p> <p><strong>Marinho:</strong> Eu sei um pouco disso, e liderar uma grande comunidade não é uma tarefa fácil</p> <p><strong>Jacob:</strong> Há uma ótima palestra que Ben Collins-Sussman <a class="footnote-reference" href="#id10" id="id3" name="id3">[1]</a> e Brian Fitzpatrick <a class="footnote-reference" href="#id11" id="id4" name="id4">[2]</a> dão, chamada &quot;How Open Source Projects Survive Poisonous People&quot; (&quot;Como Projetos de Código Aberto Sobrevivem a Pessoas Diabólicas&quot;) <a class="footnote-reference" href="#id12" id="id5" name="id5">[3]</a> cheio de bons conselhos -- as lições que eles dão têm salvado meu traseiro algumas vezes.</p> <p><strong>Marinho:</strong> legal, eu vou tirar um tempo pra assistí-lo...</p> <p>agora sobre a Django Foundation <a class="footnote-reference" href="#id13" id="id6" name="id6">[4]</a>:</p> <p>O que podemos esperar dela para os próximos anos? Que tipos de ações?</p> <p><strong>Jacob:</strong> Nós realmente não pensamos que haverá um grande avanço, honestamente. Nossa principal meta é ter um corpo &quot;oficial&quot; que pode representar o Django e ajudar financeiramente. Como a maioria das coisas em torno do Django, nós vamos esperar para ver o que a comunidade quer que façamos.</p> <p>A grosso modo, nós vamos bancar os sprints de desenvolvedores e reuniões para ajudar o Django 1.0 a sair no tempo (certo).</p> <p><strong>Marinho:</strong> Podemos esperar por uma certificação ou algo semelhante?</p> <p><strong>Jacob:</strong> Você quer dizer algum tipo de &quot;certificação para desenvolvedor&quot; oficial do Django?</p> <p><strong>Marinho:</strong> sim</p> <p><strong>Jacob:</strong> Nós não temos planos para isso por agora... não parece ser algo que nós queremos nos envolver, mas como eu disse antes, se isso for algo que a comunidade realmente quer, é claro que vamos tentar ajudar.</p> <p><strong>Marinho:</strong> A comunidade de Python não gosta de certificações, mas muitas empresas gostam... é algo que as faz se sentir mais seguras</p> <p><strong>Jacob:</strong> Yeah, eu entendo... é só algo que não é muito comum no Open Source em geral; não estou certo de como isso funciona.</p> <p><strong>Marinho:</strong> sim, eu concordo.</p> <p>e sobre a DjangoCon <a class="footnote-reference" href="#id14" id="id7" name="id7">[5]</a>, que mais detalhes podemos ter a respeito? Vocês já tem alguma data?</p> <p><strong>Jacob:</strong> Nós estamos só finalizando alguns detalhes, e devo estar pronto pra anunciá-los talvez hoje, talvez na segunda.</p> <p><strong>Marinho:</strong> ótimo :)</p> <p>agora sobre o ORM e novas funções...</p> <p>Você tem alguma idéia de quando poderemos ter suporte a SGBDOO (bancos de dados orientados a objetos), especialmente BigTable, Amazon SimpleDB e ZoDB? Quão difícil isso seria pra fazer?</p> <p><strong>Jacob:</strong> Isso realmente é algo que eu venho pensando <strong>muito</strong> ultimamente -- repentinamente há todo um punhado desses bancos de dados não-relacionais saindo, seria fascinante suportá-los.</p> <p>Eu penso realmente que nem tudo seria <strong>aquela</strong> dificuldade, no entanto você deve se lembrar que nenhum desses DBs realmente suportam joins ou outras características relacionais, então você nunca poderia mesmo só mudar o DATABASE_ENGINE da mesma forma e esperar que sua aplicação funcione.</p> <p><strong>Marinho:</strong> sim, isso é o que eu estava pensando...</p> <p>E sobre suporte (aos recursos de) master/slave <a class="footnote-reference" href="#id15" id="id8" name="id8">[6]</a> do MySQL e conexões com múltiplos bancos de dados?</p> <p><strong>Jacob:</strong> Eu não sou de todo familiar com o MySQL, então eu provavelmente sou a pessoa errada a se perguntar sobre ele. Para múltiplas conexões, há um bom trabalho sendo feito na área; ele não estará na (versão) 1.0 por causa do tempo, mas estou esperançoso de que nós teremos em breve.</p> <p><strong>Marinho:</strong> Entendi... essa (minha) pergunta é porque a maioria dos grandes sites, como o Flickr, YouTube, Slashdot e outros costumam adotar o armazenamento de dados em muitos (e às vezes aleatórios) servidores de banco de dados.</p> <p>Agora sobre o uso do Django e o futuro...</p> <p>Um ou dois anos atrás, você pensava sobre o Django sendo usado como framework para se criar softwares corporativos como ERPs, BIs, CRMs, Billing (Vendas) e outros softwares para uso em empresas, especialmente não relacionados ao gerenciamento de conteúdo?</p> <p><strong>Jacob:</strong> Quando nós lançamentos a primeira release do Django, nós <strong>nunca</strong> esperávamos que ela seria largamente usado como é (hoje). Nós pensamos que talvez alguns outros publicadores online se interessariam, e talvez nós pegássemos alguma relevância na comunidade (que usa) Python na web. Nós nunca pensamos que o Django seria usado em muitos lugares (redes sociais, software corporativo/empresarial, vendas online...)</p> <p><strong>Marinho:</strong> e agora como você sente quando vê que dia-após-dia mais pessoas usam-no em tipos totalmente diferentes de soluções? Surpreso? :)</p> <p><strong>Jacob:</strong> Surpreso, sim, mas principalmente feliz. É realmente ótimo, e todos os diferentes usos confirmam que o Django realmente funciona bem.</p> <p>Isto é, tendo todas essas pessoas com diferentes necessidades confirmando que nós não nos prendemos a um nicho em particular.</p> <p><strong>Marinho:</strong> sim, é verdade... Eu acho que você se sente orgulhoso quando sabe que grandes players como Google e Yandex <a class="footnote-reference" href="#id16" id="id9" name="id9">[7]</a> estão usando-o :)</p> <p><strong>Jacob:</strong> Exatamente -- é realmente bacana.</p> <p><strong>Marinho:</strong> O que você considera que serão os grandes desafios para o framework nos próximos anos?</p> <p><strong>Jacob:</strong> Eu acho que nosso grande desafio é lidar com nosso próprio sucesso. Nós aguardamos muito tempo para soltar a 1.0, principalmente porque nós não compreendemos quão grandes nós ficamos -- com uma pequena comunidade, você pode arriscar a se sujar. Agora que nós somos realmente um projeto de grandes proporções, nós precisamos formalizar mais o nosso fluxo de trabalho: versões planejadas e regulares, divisão mais explícita e delegação de responsabilidades, etc.</p> <p>Nós também precisamos estar dispostos para uma inevitável oposição quando ela vier. Como um grande projeto você inevitavelmente terá eventualmente pessoas que simplismente te <strong>odeiam</strong>, e lidar com o lado baixo do sucesso pode ser desagradável. Eu tenho acompanhado com cuidado como a comunidade de Rails segurou a carga de &quot;Rails não escala!&quot; e &quot;Rails não é seguro!&quot; neste último ano; eles realmente fizeram um grande trabalho ouvindo as vozes racionais e ignorando as pessoas que estavam só para contrariar.</p> <p>Nós vamos certamente ter &quot;controvérsias&quot; como essas no futuro; nós precisamos de ter certeza que nós podemos segurar isso profissionalmente.</p> <p><strong>Marinho:</strong> Eu entendo, agora o Django vive uma nova realidade, diferente de antes</p> <p><strong>Jacob:</strong> Exatamente. Nós temos que prestar atenção em como nossa comunidade se desenvolve e aceita nossa liderança ao seu encontro.</p> <p><strong>Marinho:</strong> sim, é verdade... bom, indo para o fim: o que mais você pode nos dizer? Algo quente? Alguma novidade que não sabemos?</p> <p><strong>Jacob:</strong> Hmm... nós fazemos a maioria das coisas publicamente -- a fundação e a conferência são realmente as únicas duas exceções -- então eu não acho que há algo &quot;em segredo&quot; por agora. A próxima grande coisa para nós é o Django 1.0 Alpha que está saindo <strong>em uma semana</strong> -- que será um grande marco, e nós precisaremos de grande ajuda para testá-la até que a (versão) 1.0 final esteja melhor possível.</p> <p><strong>Marinho:</strong> o.O ótimo, essa é uma grande novidade para a comunidade :)</p> <p>bom, eu agradeço por tudo, lhe parabenizo pelo sucesso e pela boa conversa, eu sou um orgulhoso e grato djangonauta e quero ajudar sempre que puder.</p> <p><strong>Jacob:</strong> Obrigado</p> <p><strong>Links relacionados</strong></p> <table class="docutils footnote" frame="void" id="id10" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id3" name="id10">[1]</a></td><td><a class="reference" href="http://www.red-bean.com/sussman/">http://www.red-bean.com/sussman/</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id11" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id4" name="id11">[2]</a></td><td><a class="reference" href="http://www.red-bean.com/fitz/">http://www.red-bean.com/fitz/</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id12" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id5" name="id12">[3]</a></td><td><a class="reference" href="http://vivixvideo.com/videos/ource-projects-survive-poisonous-people-and-you-can-too/">http://vivixvideo.com/videos/ource-projects-survive-poisonous-people-and-you-can-too/</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id13" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a name="id13">[4]</a></td><td><em>(<a class="fn-backref" href="#id2">1</a>, <a class="fn-backref" href="#id6">2</a>)</em> <a class="reference" href="http://www.djangoproject.com/foundation/">http://www.djangoproject.com/foundation/</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id14" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a name="id14">[5]</a></td><td><em>(<a class="fn-backref" href="#id1">1</a>, <a class="fn-backref" href="#id7">2</a>)</em> <a class="reference" href="http://www.ericholscher.com/blog/2008/jul/7/djangocon-2008/">http://www.ericholscher.com/blog/2008/jul/7/djangocon-2008/</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id15" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id8" name="id15">[6]</a></td><td><a class="reference" href="http://dev.mysql.com/doc/refman/5.1/en/connector-j-reference-replication-connection.html">http://dev.mysql.com/doc/refman/5.1/en/connector-j-reference-replication-connection.html</a></td></tr> </tbody> </table> <table class="docutils footnote" frame="void" id="id16" rules="none"> <colgroup><col class="label" /><col /></colgroup> <tbody valign="top"> <tr><td class="label"><a class="fn-backref" href="#id9" name="id16">[7]</a></td><td><a class="reference" href="http://kuda.yandex.ru/">http://kuda.yandex.ru/</a></td></tr> </tbody> </table> </div> Interview with Jacob Kaplan-Mosshttp://marinhobrandao.com/blog/p/interview-with-jacob-kaplan-moss_160/<div class="document"> <p><strong>Marinho:</strong> well, as I said, I'm brazilian, and Brazil has already many djangonauts many people using for corporate solutions and some big companies starting to use in some startup projects... the community is growing and we very happy :) so, my questions are related to our reality and first at all, I wish to let you know that my english is not the best, so, be patient with me :)</p> <p><strong>Jacob:</strong> Your English is <em>fine</em>, no worries! It's so cool hearing about Django getting used around the world; makes me really happy.</p> <p><strong>Marinho:</strong> I see :) you replied to me with a nice availability, you seem to be a guy with good skills of leadership. Along the Django's lifetime, did you have some situation about people relations that dare you? I mean: some situation when you lost the patience or bad discussion...</p> <p><strong>Jacob:</strong> Yeah, it's certainly happened!</p> <p><strong>Marinho:</strong> the recent suprises (for us) like DjangoCon and Django Foundation was some of those dares?</p> <p><strong>Jacob:</strong> We try really hard to maintain a friendly (or at least polite) tone, but... sometimes there are people who are just really difficult to deal with.</p> <p><strong>Marinho:</strong> I know a bit of this, and leading a great community is not an easy job</p> <p><strong>Jacob:</strong> There's a great talk that Ben Collins-Sussman and Brian Fitzpatrick give called &quot;How Open Source Projects Survive Poisonous People&quot;: <a class="reference" href="http://video.google.com/videoplay?docid=-4216011961522818645">http://video.google.com/videoplay?docid=-4216011961522818645</a> Full of good advice -- the lessons they give there have saved by butt a few times.</p> <p><strong>Marinho:</strong> cool, I will spare some time to watch it about the Django Foundation: What can we wait from it for the next years? What kind of acts?</p> <p><strong>Jacob:</strong> We haven't really thought all that far in advance, honestly. Our main goal is to have an &quot;official&quot; body that can represent Django and help out financially. Like most things around Django, we'll wait to see what the community wants us to do. In the short term, we'll be sponsoring developer sprints and meetups to help get Django 1.0 out on time.</p> <p><strong>Marinho:</strong> Can we wait for certification or something like this?</p> <p><strong>Jacob:</strong> Do you mean some sort of official Django &quot;developer certification&quot;?</p> <p><strong>Marinho:</strong> yep</p> <p><strong>Jacob:</strong> We don't have any plans for that right now... it doesn't seem like something we'd want to get involved in, but as I said before if it's something the community really wants, we'd of course try to help out.</p> <p><strong>Marinho:</strong> Python community doesn't like certifications, but many companies like... is something that they feels more safe understood</p> <p><strong>Jacob:</strong> Yeah, I understand... it's just something that's not very common in Open Source in general; not sure how it'd work.</p> <p><strong>Marinho:</strong> yes, I agree and about the DjangoCon, what more details can we have about? have you already the date?</p> <p><strong>Jacob:</strong> We're just finalizing some details, and should be able to announce them maybe today, maybe Monday.</p> <p><strong>Marinho:</strong> great :) about the ORM and new features... Have you some idea about when could we have support to OODBMS, especially BigTable, Amazon SimpleDB and ZoDB? How much hard could this to do?</p> <p><strong>Jacob:</strong> This is actually something I've been thinking a lot about lately -- there's all of a sudden a bunch of these non-relational databases coming out, it would be awesome to support them. I think it's actually not all that hard, though you have to remember that none of these DBs really support joins or other relational features so you could never really just switch DATABASE_ENGINE on the fly and expect your app to work.</p> <p><strong>Marinho:</strong> yes, this is what I was thinking about And about MySQL master/slave support and multiple database connections?</p> <p><strong>Jacob:</strong> I'm not all that familiar with MySQL, so I'm probably the wrong person to ask there. As for multiple connections, there's some good work being done in that area; it won't make it into 1.0 be