MarinhoBrandão.com
"O risco que se corre ao se introduzir novas tecnologias é menor do que aquele que se corre ao não introduzi-las." ;)

Django-plus: para que serve? (parte 2)

Publicado por marinho, há 3 anos, 3 meses | django

Template Filters de comparação

Continuando a explanação sobre os recursos disponíveis no django-plus [1], eu gostaria somente de esclarecer um pouco mais sobre as template filters que disponibilizei no artigo anterior [2].

Vamos tomar a template filter is_equal como melhor exemplo.

O sistema de templates do Django disponibiliza apenas um meio para se fazer comparativo de igualdade, assim:
{% ifequal user.username 'marinho' %}
    exibe ou faz alguma coisa
{% endifequal %}

Notou a limitação? Não? Imagine que você precise colocar ali um simples AND, ou um OR. 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:

{% load djangoplus_tags %}

{% if user.username|is_equal:'marinho' and user.is_staff %}
    exibe ou faz alguma coisa
{% endif %}

Compreendeu? Agora siga a mesma linha de raciocínio para as template filters is_not_equal (diferente), is_lt (menor que), is_lte (menor ou igual), is_gt (maior que), is_gte (maior ou igual) e in_list (na lista).

Agora, com as coisas esclarecidas, vamos adiante.

Model Info

Hoje vamos falar especificamente do pacote de funcionalidades model_info. Para que ele serve?

Bom, quando você quer criar um admin para uma classe de modelo, como você faz? Assim:

class AdminPessoa(ModelAdmin):
    fields = ('nome','idade',)

admin.site.register(Pessoa, AdminPessoa)

Simples né? Sim, muito. E para formulários de inclusão/alteração? Assim:

class FormPessoa(ModelForm):
    class Meta:
        model = Pessoa

Sim, isso é suficiente para seu formulário funcionar bem para a classe de model. Simples assim.

Agora, e quando você precisa listar os registros de uma queryset ou exibir as informações de um deles?

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.

Então, para isso servem as classes e template tags do Model Info do django-plus.

Vamos a um exemplo de exibição de dados de um objeto de uma classe Pessoa:

views.py

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())

info.py

from djangoplus.model_info import ModelInfo
from models import Pessoa

class InfoPessoa(ModelInfo):
    class Meta:
        model = Pessoa

detalhes_da_pessoa.html (template)

{% load djangoplus_tags %}

<table class="info">
    {% model_info_for_object 'minha_aplicacao.info.InfoPessoa' pessoa %}
</table>

Absurdamente simples, não? Neste caso, não importa se sua classe Pessoa possua 2 ou 50 campos, eles serão todos (à exceção de ManyToManyField) listados comportadamente na tabela informada e funcionará bem.

Vamos fazer uma listagem? Ok, vamos lá:

views.py (acrescente as linhas ao fim do arquivo)

def lista_de_pessoas(request):
    pessoas = Pessoa.objects.all()
    return render_to_response('lista_de_pessoas.html', locals())

info.py (acrescente as linhas ao fim do arquivo)

from djangoplus.model_info import ModelList

class ListPessoa(ModelList):
    class Meta:
        model = Pessoa

lista_de_pessoas.html (template)

{% load djangoplus_tags %}

<table class="list">
    {% model_info_for_list 'minha_aplicacao.info.ListPessoa' pessoas %}
</table>

Da mesma forma, será exibida uma lista dos objetos da queryset pessoas, e se a classe Pessoa possuir o método get_absolute_url declarado, serão exibidos também ícones para edição e exclusão, seguindo as convenções comuns "{{ objeto.get_absolute_url }}edit/" e "{{ objeto.get_absolute_url }}delete/", respectivamente.

Mas é só isso? Para funcionar o básico, sim, mas aquela subclasse "Meta" permite diversas outras formas de customização.

Vamos falar primeiramente da classe ModelInfo:

  • fields - lista dos campos a serem exibidos;
  • exclude - lista dos campos a não serem exibidos (não é muito inteligente usar o fields e o exclude ao mesmo tempo);
  • show_if_none - informe True ou False para mostrar campos com valor None ou não;
  • show_if_empty - informe True ou False para mostrar campos com valor vazio ou não;
  • auto_urlize - informe True ou False para transformar URLs em links;
  • auto_linebreaks - informe True ou False para mostrar quebras de linha em campos do tipo TextField;
  • list_display_links - lista dos campos que serão exibidos como link para a página do objeto (raramente usada);
  • fieldsets - lista de fieldsets. Segue o mesmo padrão do fieldsets do Admin;
  • row_template (default: '<tr><th>%s</th><td>%s</td></tr>') - permite que você informe o formato para exibir cada campo e seu referido valor;
  • fieldset_title_template (default: '<h3>%s</h3>') - permite que você informe o formato para exibir o título da fieldset;
  • show_fieldset_title - informe True ou False para exibir os títulos de fieldsets ou não;

Bacana não? Agora vamos para a classe ModelList:

  • fields - lista dos campos a serem exibidos;
  • exclude - lista dos campos a não serem exibidos (não é muito inteligente usar o fields e o exclude ao mesmo tempo;
  • show_if_none - informe True ou False para mostrar campos com valor None ou não;
  • show_if_empty - informe True ou False para mostrar campos com valor vazio ou não;
  • auto_urlize - informe True ou False para transformar URLs em links;
  • auto_linebreaks - informe True ou False para mostrar quebras de linha em campos do tipo TextField;
  • list_display_links - lista dos campos que serão exibidos como link para a página do objeto (raramente usada);
  • td_template (default: '<td>%s</td>') - formato para exibir cada célula da tabela;
  • th_template (default: '<th>%s</th>') - formato para exibir cada célula do cabeçalho da tabela;
  • tr_template (default: '<tr>%s</tr>') - formato para exibir cada linha da tabela;
  • thead_template (default: '<thead><tr>%s</tr></thead>') - formato para exibir o cabeçalho da tabela;
  • tbody_template (default: '<tbody>%s</tbody>') - formato para exibir o corpo de dados da tabela;
  • icon_edit_template (default: '<a href="%(edit_url)s" title="Edit this"><img src="%(media_url)simg/admin/icon_changelink.gif" alt="Edit"/></a>') - formato para exibir o ícone de edição (informe vazio para não ser exibido);
  • icon_delete_template (default: '<a href="%(delete_url)s" title="Delete this"><img src="%(media_url)simg/admin/icon_deletelink.gif" alt="Edit"/></a>') - formato para exibir o ícone de exclusão (informe vazio para não ser exibido);
  • group_template (default: '<tr><td colspan="%(cols)s" class="group"><h3>%(display)s</h3></td></tr>') - formato para agrupamentos;
  • groups - lista dos campos pelos quais os registros serão agrupados; 

Ufa! Mais poderoso e flexível do que pensava, não? Mas ainda tem mais.

Tanto na classe ModelInfo quanto na ModelList, é possível informar métodos seguindo as sintaxes e regras abaixo:

  • get_CAMPO_display(self, f_name) - retorna um título customizado para o campo em questão;
  • get_CAMPO_value(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";
  • render_buttons_cell(self, instance, edit_url=None, delete_url=None) - caso sobrecarregue este campo, é possível mudar o padrão das URLs de edição e exclusão do objeto;

Por fim, a classe ModelInfo permite que também ser declarada diretamente na view e ser chamada para cada campo solitariamente, assim:

views.py

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())

detalhes_da_pessoa.html (template)

{% load djangoplus_tags %}
{{ info_pessoa.nome }}

Gostou? E algo semelhante pode ser feito com a classe ModelList, que é iterável:

views.py (acrescente as linhas ao fim do arquivo)

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())

lista_de_pessoas.html (template)

{% load djangoplus_tags %}

<table class="list">
    {% for pessoa in lista_pessoas %}
    <tr><td>{{ pessoa }}</td></tr>
    {% endfor %}
</table>

Bom, é isso aí, espero que ajude. Eu ganho um bom temp usando esses recursos, acho que podem ser úteis para você também ;)

Links relacionados

  1. http://code.google.com/p/django-plus/
  2. http://marinhobrandao.com/blog/p/django-plus-para-que-serve/