BOMBOLOM.COM

(django) Admin Widgets vrs User Forms (caso AdminDateWidget)

Este é um post de José Lopes.

O admin do Django tem uma série de widgets que podem ser utilizados em qualquer form do utilizador.

Na documentação do Django é feita referência a isso ( Media and Django Admin) mas nenhum exemplo prático é dado. Em buscas pela internet verificamos que muita gente tentou utilizar esse widgets sem sucesso, especialmente o AdminDateWidget que possibilita ter um agradável calendário popup para escolher a data.

Este post mostra como utilizar este widget, explicando o porquê de tantas tentativas falhadas.

A informação apresentada também é válida para o widget AdminTimeWidget. Outros widgets poderão ser objecto de outros posts caso se justifique.

Vamos começar pela causa de não se conseguir por a funcionar o widget, que ao mesmo tempo é a solução.

Se analisarmos a página da administração encontramos no header a seguinte linha:

<script type="text/javascript" src="../../../jsi18n/"></script>

Esta linha executa o script jsi18n que, dito de uma forma resumida, chama um código em javascript responsável pela substituição de certos divs (no caso do AdminDateWidget é o div declarado no input como class="vDateField") por um display visual como o botão para o calendário popup. Este código varia consuante se esteja a utilizar ou não a internacionalização.

O problema é que mesmo incluindo esta linha nas nossas forms nada se passa. O problema é devido a este script necessitar de autenticação do administrador. Mais ingrato se torna quando não temos nenhuma mensagem de erro nem nenhum pedido de autenticação.

A solução passa então por forçarmos a correr o conteúdo do script nas nossas forms.

Por uma questão de filosofia do Django (o famoso DRY) vamos criar um ficheiro no media do nosso projecto, mais concretamente em media/js/, com o nome de adminwidgets.js. Dentro deste ficheiro vamos colocar o output de http://127.0.0.1:8000/admin/jsi18n/ que será:

function gettext(msgid) { return msgid; }
function ngettext(singular, plural, count) { return (count == 1) ? singular : plural; }
function gettext_noop(msgid) { return msgid; }

function interpolate(fmt, obj, named) {
  if (named) {
    return fmt.replace(/%\(\w+\)s/g, function(match){return String(obj[match.slice(2,-2)])});
  } else {
    return fmt.replace(/%s/g, function(match){return String(obj.shift())});
  }
}

Para o caso de um projecto sem internacionalização.

var catalog = new Array();

function pluralidx(count) { return (count == 1) ? 0 : 1; }


function gettext(msgid) {
  var value = catalog[msgid];
  if (typeof(value) == 'undefined') {
    return msgid;
  } else {
    return (typeof(value) == 'string') ? value : value[0];
  }
}

function ngettext(singular, plural, count) {
  value = catalog[singular];
  if (typeof(value) == 'undefined') {
    return (count == 1) ? singular : plural;
  } else {
    return value[pluralidx(count)];
  }
}

function gettext_noop(msgid) { return msgid; }

function interpolate(fmt, obj, named) {
  if (named) {
    return fmt.replace(/%\(\w+\)s/g, function(match){return 
String(obj[match.slice(2,-2)])});
  } else {
    return fmt.replace(/%s/g, function(match){return String(obj.shift())});
  }
}

Para o caso de um projecto com internacionalização. De qualquer forma eu sugeria a utilização do segundo caso, pois no futuro poderemos querer fazer a internacionalização e nesse caso já temos o projecto preparado.

Posto isto, para ter o calendário popup a funcionar numa form basta seguir os dois pontos seguintes:

1. Criar a Form

Vamos assumir que temos uma form baseada num modelo, modelo esse que tem um campo (mydatefield) do tipo data. O código da form terá de ser algo do tipo:

    from django import forms
(1) from my_project.myuserapp.models import MyUserModel
(2) from django.contrib.admin.widgets import AdminDateWidget

    class MyUserForm(forms.ModelForm):
(3)     mydatefield = forms.DateField(widget=AdminDateWidget)
        class Meta:
            model = MyUserModel

Explicando o código:
  (1)  importamos o nosso modelo.
  (2)  importamos o widget AdminDateWidget.
  (3)  definimos o campo mydatefield do nosso modelo como um campo de data com o widget AdminDateWidget.

2. Criar o template da form

No template da form, que podemos chamar myformtemplate.html, temos de colocar as seguintes linhas no header:

(1) <link rel="stylesheet" type="text/css" href="/media/css/widgets.css"/></link>
(2) <script type="text/javascript" src="/admin_media/js/core.js"></script>
(3) <script type="text/javascript" src="/media/js/adminwidgets.js"></script>
(4) {{ form.media }}

Explicando o código:
  (1)  aponta para uma cópia do ficheiro com o mesmo nome em django/contrib/admin/media/css e que formata o calendário (copiando o ficheiro sempre o podemos alterar ao nosso gosto);
  (2)  javascript da administração;
  (3)  inclui o ficheiro de javascript que criámos para resolver a questão do jsi18n;
  (4)  inclui os javascripts pré-definidos do AdminDateWidget, mais concretamente o calendar.js e o DateTimeShortcuts.js

Não é necessário mais nada e já temos o calendário a funcionar!
Até agora não encontrei nada disto documentado pelo que espero que vos seja útil.

07.09.2008 | Ler mais | Comentários | Tags , ,

Voltar à Página principal | Made with PyBlosxom