Boas práticas para um desenvolvedor Django - Parte1 - Evite Hardcodes!
Autor: Marcos Petry | Publicado: 2010-03-10T17:28:00-03:00

Há um tempo atrás vi um post em um blog que dava algumas dicas para desenvolvedores Django iniciantes, bom aí vai a minha versão, em português, e com alguns comentários que acho interessantes.Os posts estão ficando bem maiores do que eu imaginei, vou publica-los em partes, para iniciar série, a primeira parte:Evite hardcodesPrimeiramente, se você não sabe o que é hardcode, clique aqui!Hardcode no settings.pyÉ bem comum ver no código especialmente nas variáveis dos settings MEDIA_ROOT e TEMPLATE_DIRS utilizar o caminho absoluto de um diretório do sistema.TEMPLATE_DIRS = ( "/home/html/project/templates",)MEDIA_ROOT = "/home/html/project/appmedia/"Esse tipo de prática dificulta a manutenção do sistema, especialmente quando o projeto está sendo migrado de um servidor para outro, ou simplesmente passando o projeto da máquina de desenvolvimento para a produção, sempre que uma mudança dessas é feita, é necessário alterar o conteúdo destas variáveis.O ideal é, através do método os.path.realpath, pegar o endereço absoluto do arquivo settings.py, e a partir deste valor, incluir os endereços necessários, como é visto abaixo:import osSITE_ROOT = os.path.realpath(os.path.dirname(__file__))MEDIA_ROOT = os.path.join(SITE_ROOT, 'appmedia')TEMPLATE_DIRS = ( os.path.join(SITE_ROOT, 'templates'),)Hardcode em templatesNos templates, também é possível ver hardcodes nos caminhos de links, imagens, javascript e css.Embora seja muito tentador (pois é mais rápido) utilizar este caminho, é bem melhor você utilizar a variável MEDIA_URL que foi declarada no settings.py, dentro do seu template. Como fazer isso? Adicione um context processor!TEMPLATE_CONTEXT_PROCESSORS = ('django.core.context_processors.request',"django.core.context_processors.auth","django.core.context_processors.debug","django.core.context_processors.i18n","django.core.context_processors.media")Os Context processors, falando a grosso modo, adicionam itens dentro dos templates. Como visto acima, o Django possui uma série de processors, para as mais variadas funcionalidades (autenticação, debug, internacionalização, etc...)O context processor django.core.context_processors.media adiciona uma variável nos templates chamada MEDIA_URL, ela possui o caminho especificado na constante MEDIA_URL do settings.py, dessa forma ao invés de chamar um css assim:você chama assim:Fica mais bonito? Talvez não, mas vai facilitar a sua manutenção! Isso vai :)Hardcode em linksVamos direto a um exemplo mais prático:Digamos que você tem uma aplicação que exibe os seus produtos:from django.conf.urls.defaults import *urlpatterns = patterns('produtos.views',   (r'^$', 'inicio',),   (r'^cadastro/$', 'cadastro'),   (r'^(?P\d+)/$', 'detalhe'),   (r'^(?P\d+)/comprar/$', 'comprar'),   (r'^carrinho/$', 'carrinho'),)Até a versão 1.0 do Django o modo de exibir estas urls é colocando o endereço absoluto dela direto no template, algo como:&lt;a href="/produtos/"&gt;Lista&lt;/a&gt; - &lt;a href="/produtos/cadastro"&gt;Novo&lt;/a&gt; - &lt;a href="/carrinho/"&gt;Carrinho de compras&lt;/a&gt;&lt;ul&gt;{%for p in produtos%}&lt;li&gt;&lt;a href="/produtos/{{p.id}}/"&gt;{{p.nome}}&lt;/a&gt;&lt;/li&gt;{%endfor%} &lt;/ul&gt;Mas, e se por algum motivo, a seção de produto não estiver em /produtos/ e sim em um outro endereço qualquer? Teremos que alterar "no braço" qualquer vestígio nos templates, caso contrário, enviaremos um erro 404 para o usuário!Ainda bem, que após a versão 1.0 do Django possuímos urls nomeadas! dessa forma podemos construir nossas urls da seguinte maneira:from django.conf.urls.defaults import *urlpatterns = patterns('produtos.views',   url(r'^$', 'inicio', name='produto-home'),   url(r'^cadastro/$', 'cadastro', name='produto-novo'),   url(r'^(?P\d+)/$', 'detalhe', name='produto-exibe'),   url(r'^(?P\d+)/comprar/$', 'comprar', name='produto-compra'),   url(r'^carrinho/$', 'carrinho', name='produto-carrinho'),)Reparem que não possuímos mais uma tupla normal, e também foi adicionado um atributo "name" em cada url, cujo objetivo tem justamente nomear as urls, possuímos também uma templatetag chamada "url", que serve para pegar dinamicamente a nossa url {%url nome-da-nossa-url%}. Com este recurso, podemos construir nosso template da seguinte forma:&lt;a href="{%url produto-home %}"&gt;Lista&lt;/a&gt; - &lt;a href="{%url produto-novo %}"&gt;Novo&lt;/a&gt; - &lt;a href="{%url produto-carrinho%}"&gt;Carrinho de compras&lt;/a&gt;&lt;ul&gt;{%for p in produtos%}&lt;li&gt;&lt;a href="{%url produto-exibe p.id %}"&gt;{{p.nome}}&lt;/a&gt;&lt;/li&gt;{%endfor%} &lt;/ul&gt;Também podemos enviar parâmetros para a templatetag url, que foi o que fizemos para a listagem dos produtos. Para mais recursos sobre url nomeadas, acesse a documentaçãoBônus: O Método get_absolute_urlNo exemplo acima, vimos como listar objetos utilizando a tempaltetag url e passando o id do objeto por parâmetro, no entanto, esta não é uma das melhores práticas, o ideal seria você acrecentar o método get_absolute_url dentro da classe do seu model, como visto abaixo:class Receita(models.Model):   data_publicacao = models.DateTimeField(auto_now_add=True)   nome = models.CharField(max_length=100)   categoria = models.ForeignKey(Categoria, null=True)   destaque = models.BooleanField(u"Destaque?")   class Meta:       ordering = ['-data_publicacao']       get_latest_by = 'data_publicacao'      def __unicode__(self):       return self.nome   def get_absolute_url(self):       return reverse('produto-exibe', args=[self.id])O método get_absolute_url retorna a url absoluta do objeto através da função reverse, que possui a mesma função da templatetag "url". Assim podemo listar os objetos de forma um pouco mais elegante:&lt;ul&gt;{%for p in produtos%}&lt;li&gt;&lt;a href="{{p.get_absolute_url}}/"&gt;{{p.nome}}&lt;/a&gt;&lt;/li&gt;{%endfor%}&lt;/ul&gt;Não é regra termos o método get_absolute_url, você pode criar qualquer outro método que faça a mesma função, mas o método get_absolute_url, é necessário para exibirmos no admin, um link para o objeto, como visto abaixo: