Desenvolvimento - Python

Plone: Documentação do CMFFormController

O pacote CMFFormController ajuda os desenvolvedores ao simplificar o processo de validação de formulários. Ele também facilita para os administradores de sites a sobreposição de alguns dos comportamentos de pacotes sem modificar o código, facilitando a atualização de pacotes sem atrapalhar as modificações.

por Fábio Rizzo Matos



Tradução da Documentação de /seusiteplone/portal_form_controller/manage_docs

Tradutores:
Fábio Rizzo Matos - fabiorizzo@gmail.com - www.fabiorizzo.com
Hugo Barbosa - hugo.barbosa@gmail.com

Usando o CMFFormController

O pacote CMFFormController ajuda os desenvolvedores ao simplificar o processo de validação de formulários. Ele também facilita para os administradores de sites a sobreposição de alguns dos comportamentos de pacotes sem modificar o código, facilitando a atualização de pacotes sem atrapalhar as modificações.

Como funciona:

  • Desenvolvedores associam um conjunto de variáveis padrão para seus Page Templates. Essas variáveis controlam a validação que acontece depois que o formulário é submetido e as ações que ocorrem depois da validação. As variáveis são armazenadas no filesystem no arquivo de propriedades .metadata.
  • Administradores de sites podem substituir as validações padrão e as ações usando a ZMI. Uma vez que um conjunto de validações ou ações foi especificado na ZMI, as validações e ações padrão serão ignoradas.

Formulários

Para tirar vantagem do CMFFormController, você precisa usar Controller Page Templates ao invés de Page Templates normais. Controller Page Templates agem exatamente como Page Templates normais, mas eles fazem algum trabalho extra quando são visualizados.

Aqui está um formulário básico que usa CMFFormController:

  <form tal:define="errors options/state/getErrors"
        tal:attributes="action string:${here/absolute_url}/${template/id};"
       method="post">
     <input type="hidden" name="form.submitted" value="1" />
     <p tal:define="err errors/foo|nothing" tal:condition="err" tal:content="err" />
     <input type="text"
            name="foo"
            tal:define="val request/foo|nothing"
            tal:attributes="value val" />

     <input type="submit" name="submit" value="submit" />
  </form>

Vejamos.

  • Primeiro, notamos que o formulário é configurado para submeter a si mesmo. Formulários devem submeter a eles mesmos.
  • Segundo, vemos a variável escondida (hidden) especial form.submitted. O page template controlado checa o REQUEST pelo form.submitted para ver se o formulário foi submetido ou se, ao contrário, ele foi apenas acessado, por exemplo, através de um link. Formulários devem conter a variável escondida (hidden) form.submitted
  • No começo do formulário setamos a variável errors. O dicionário errors vem do objeto state que é passado nas opções do template. O objeto state permite aos validadores e scripts passarem informação um ao outro e para formulários. Para nossos propósitos, a informação mais importante é o dicionário errors, que tem entradas da forma {nome_do_campo:mensagem_de_erro}
Antes que possamos usar esse formulário precisamos especificar os validadores que serão usados para checar os valores do formulário, e precisamos especificar a ação que irá ocorrer depois da validação.

Especificando Validators

Existem duas formas básicas de especificar form"s validators.

  1. Você pode especificar os validators nas propriedades de um arquivo .metadata para arquivos de controller page templates baseados no filesystem.
  2. Você pode especificar validators através da ZMI (ou programando). Esses valores serão armazenados no ZODB como atributos do objeto portal_form_controller.

Se você especificar validators em ambos os locais, os validators especificados na ZMI terão precedencia sobre os especificados no form.

Especificando Validators no Filesystem

Você pode especificar validators no filesystem usando objetos arquivos de propriedades .medata.

Para criar um arquivo .metadata, simplesmente crie um arquivo com o mesmo nome do seu page template, e coloque no final do nome do arquivo o .metadata . Para a instancia, vou precisa ter um Controller Page Template chamado document_edit_form.cpt . E as propriedades serão armazenadas em um arfquivo chamado document_edit_form.cpt.metadata

O arquivo .metadata usa o padrão de sintaxe do python ConfigParser. A sessão do Validator do arquivo .metadata deverá ser semelhante a esse:

  [validators]
  validators = validate_script1, validate_script2

Os scripts de validação validate_script1 e validate_script2 serão chamados na ordem.

Type-Specific Validators

Suponhamos que você deseja diferentes validator para serem chamados dependendo do tipo no contento que o form foi chamado.

Você pode fazer o seguinte:

[validators]
validators = validate_script1
validators.Document = validate_script2

No Exemplo Acima, se o contexto é um objeto Document, o validate_script2 será chamado para a validação. Para qualquer outro, apenas o validate_script1 será chamado.

Note que a ordem no qual as variáveis são especificadas não tem importância. Type-specific validators sobrepoem não-specific validators se ambos forem aplicados.

Button-Specific Validators

Imagine que você tenha dois diferentes botões em seu form, e você deseja que seja feita diferentes versões de validações dependendo de qual botão que for pressionado. Você pode fazer isso da seguinte maneira:

Primeiro, nomeio os seus botões como button1 e button2:

   <input type="submit"
          name="form.button.button1"
          value="First Button" />
   <input type="submit"
          name="form.button.button2"
          value="Second Button" />

Agora, especifique os validators no arquivo .metadata para o button1 e para o button2:

   [validators]
   validators..button1 = validate_script1, validate_script3
   validators..button2 = validate_script2, validate_script4

Note a presença dos .. (dois pontos). Ele é utilizado para especificar um tipo em especifico. Você pode especificar validate_script5 que seja chamado se o button2 for pressionado e o contexto seja um documento:

   [validators]
   validators.Document.button2 = validate_script5

Lembrando que specific validators tem precedencia sobre não-specific validators. Especificando Validators na ZMI

Se você olhar uma Controller Page Template na ZMI, você verá que ele é igual a uma Page Template, com o acréscimo de duas novas tabs, Validations e Actions. Clique na tab Validation.

A tab validation irá mostrar todos os validators para a page template em questão. Você poderá especificar validators com algum tipo de especialização, via form.

As informações dos validators de todos os forms são armazenados no tool portal_form_controller tde seu portal. Isso permite que você especifique validators para o filesystem sem problemas, desde que a informação for persistida no ZODB. Note que os validators é colocado baseado no id do form, então, todos os forms com o mesmo id utilizarão o mesmo validators. Isto é simples quando se tem multiplas skins:

Forms com o mesmo Id usão os mesmos validators, não importa a skin que ele estiver.

Quando um form é enviado, primeiro ele checa se existe algum validator aplicável e foi especificado via ZMI. Se ele encontra algum, ele é utilizado. Se ele não encontra nenhum validator especificado via ZMI, ele checa o objeto REQUEST para procurar se existe algum validator especificado em uma variável ocultaes it. Como resultado, os validators especificados na ZMI, tem procedencias sobre os especificados nos forms.

Especificando Validators por programação

A ferramenta portal_form_controller tem todos os metos que você pode usar para especificar validators para uma Controller Page Template. A API é a seguinte:

  portal_form_controller.addFormValidators(id,
                                           context_type,
                                           button,
                                           validators)

Aqui o id é o id do Controller Page Template, context_type é a classe de mesmo nome da classe do contexto do objeto, button é o nome do botão pressionado, e validators é uma string ou lista de strings separados via ponto e vírgula. Se você quer um validator para qualquer classe, faça o context_type como None. Similar, se você quer um validator para funcionar em qualquer botão, faça a variável button como None.

Especificando Ações

A sequência de validadores que é executada retorna um estado no objeto state. O estado padrão é success, i.e. se nenhum validador é executado, o estado será success. Se um validador encontra um erro, ele tipicamente irá setar o estado para failure. A próxima coisa que precisamos fazer em nosso formulário é especificar o que acontece quando um determinado estado é retornado.

Assim como com validadores, há duas maneiras básicas de especificar uma ação de formulário.

  1. Para Controller Page Templates e Controller Python Scripts baseados no filesystem, você pode especificar as ações no arquivo de propriedades .metadata.
  2. Você pode especificar as ações através da ZMI (ou de maneira programática). Esses valores serão armazenados no ZODB como atributos do objeto portal_form_controller

Se você especificar ações em ambos os lugares, as ações especificadas na ZMI terão precedência sobre aquelas especificadas no filesystem.

Especificando Ações no Filesystem

Você pode especificar ações no filesystem usando um arquivo de propriedades de objetos .metadata.

Ações são armazenadas no mesmo arquivo .metadata que os validadores. A sintaxe para a seção de ações de seu arquivo se pareceria com:

  [actions]
  action.success = traverse_to:string:script1

No exemplo acima, quando o formulário é submetido e os scripts de validação retornam um estado de success, a ação traverse_to é chamada com o argumento string:script1, i.e. se os dados do formulário são válidos, rodamos o script script1. De maneira alternativa, poderíamos especificar action.success = redirect_to:string:http://minha_url_aqui, o que causaria o redirecionamento do navegador para http://minha_url_aqui

A ação padrão para o estado failure é recarregar o formulário atual. O formulário terá acesso a todas as mensagens de erro, através do objeto state em suas opções.

Ações Específicas por Tipos

Suponha que você queira que diferentes ações ocorram dependendo do tipo de contexto que o formulário tem. Você pode fazer da seguinte maneira:

   [actions]
   action.success = traverse_to:string:script1
   action.success.Document = traverse_to:string:document_script

No exemplo acima, se o contexto é um objeto Document, document_script será executado se a validação for bem sucedida; para tudo mais, script1 será executado. Note que a ordem em que as variáveis são especificadas não importa; as ações específicas por tipo irão sobrepor as ações não-específicas se ambas são aplicáveis. Ações Específicas por Botões

Suponha, por outro lado, que você tenha dois diferentes botões em seu formulário, e você quer que diferentes ações ocorram dependendo de qual botão é pressionado. Você pode efetuar isso da seguinte forma:

Primeiro, nomeie seus botões como button1 e button2:

   <input type="submit"
          name="form.button.button1"
          value="Primeiro Botão" />
   <input type="submit"
          name="form.button.button2"
          value="Segundo Botão" />

Em seguida, especifique ações para button1 e para button2:

   [actions]
   action.success..button1 = traverse_to:string:script1
   action.success..button2 = traverse_to:string:script2

Note a presença do ... Esse é um espaço reservado para um especificador de tipo. Você poderia adiante especificar que document_script2 é chamado se button2 é pressionado e o contexto é um Document, adicionando:

[actions]
action.success.Document.button2 = traverse_to:string:document_script2

Especificando Ações na ZMI

Se você visualizar um Controller Page Template na ZMI, você verá que ele se parece exatamente com um Page Template comum com duas abas extras, Validation e Action. Clique na aba Actions.

A aba Actions mostra todas as ações para o page template em questão. Você pode especificar ações com o mesmo tipo de opções de especialização que as acima através de um formulário.

A informação de ação para todos os formulários é armazenada na ferramenta portal_form_controller em seu portal. Isso significa que você pode especificar ações para objetos no filesystem sem problemas, já que a informação é persistida no ZODB. Note que a informação de ação é atrelada ao Id do formulário, de maneira que todos os formulários com o mesmo Id usam as mesmas ações. Isso mantém as coisas simples quando você tem múltiplas skins: formulários com o mesmo Id usam as mesmas ações, não importando em que skin estão.

Quando um formulário é submetido, ele primeiro checa para ver se há ações aplicáveis que foram especificadas através da ZMI. Se ele acha uma, ele a usa. Se ele não acha uma ação através da ZMI, ele então checa o objeto REQUEST para ver se ações foram especificadas em variáveis escondidas. Como resultado, ações especificadas na ZMI têm precedência sobre aquelas especificadas em formulários.

Especificando Ações de maneira Programática

A ferramenta portal_form_controller do portal tem métodos que você pode usar para especificar as ações para um determinado ControllerPageTemplate. A API é a seguinte:

   portal_form_controller.addFormAction(id,
                                        status,
                                        context_type,
                                        button,
                                        action_type,
                                        args)

Aqui id é o Id do ControllerPageTemplate, status é o estado para o qual a ação será executada, context_type é o nome da classe para a classe do objeto de contexto, button é o nome do botão pressionado, action_type é uma string (tipicamente uma expressão TALES) que será passada para a ação. Se você quer que uma ação seja executada por qualquer classe, coloque o valor None em context_type. Similarmente, se você quer que uma ação seja executada por qualquer botão, coloque o valor None em button.

Scripts

Ao escrever scripts que fazem algum processamento depois de um formulário validado, você pode usar Controller Python Scripts no lugar de Python Scripts comuns para permitir aos gerentes de sites que sobreponham suas ações através da ZMI. No filesystem, Controller Python Scripts usam a extensão .cpy ao invés de .py. Note que Controller Validators e Controller Python Scripts diferem de maneira significativa. Assegure-se de usar o tipo de script apropriado (Controller Validator ou Controller Python Script) e/ou a extensão de arquivo apropriada (.cpy ou .vpy).

Vamos dar uma olhada em um script básico que atribui um atributo de contexto ao valor n que é passado através do "REQUEST":

context.n = context.REQUEST.get("n")

# Opcionalmente atribui a próxima ação padrão (isso pode ser
# sobreposto na ZMI)

state.setNextAction("redirect_to:string:view")

# Opcionalmente passa uma mensagem que será mostrada ao usuário
state.setKwargs({"portal_status_message":"Você atribuiu context.n para o valor %s." % str(context.n)})
return state

Note que normalmente você desejará usar a ação traverse_to para chamar seu script. Isso irá garantir que variáveis de formulário atribuídas no objeto REQUEST estão disponíveis para seu script.

Esse script atribui sua ação para redirecionar a url relativa view para o objeto de contexto atual. O estado não foi atribuído, então ele mantém o estado padrão, success.

A diretiva state.setNextAction acima é análoga a ter a seguinte linha no seu arquivo .metadata:

  [actions]
  action.success = redirect_to:string:view

Assim como com o arquivo .metadata, a ação padrão especificada no script pode ser sobreposta através da ZMI. Isso permite aos gerentes de site sobrescreverem ações pros-script sem precisarem customizar seu código.

Por fim, retornamos o objeto state.

Validação para Scripts

Ter scripts de validação separados tipicamente significa que a validação é retirada dos scripts. Isso simplifica os scripts, mas significa que é possível chamá-los diretamente com dados inválidos. Podemos prevenir esse problema adicionando validadores para scripts. Controller Python Scripts usam a mesma ZMI e/ou mecanismos do arquivo .metadata que os Controller Page Templates para adicionarem validadores.

Cada vez que um validador é chamado, ele registra a chamada no objeto state. A validação é esperta o suficiente para saber que se um validador é chamado por um formulário, ela não será chamada novamente pelo script.

Note que se você associar validadores com um script, você precisará atribuir uma ação de estado failure sensata, uma vez que scripts não atribuem tal ação por padrão. Você pode querer definir um estado de falha (failure) diferente para falhas que ocorram dentro do script, por exemplo, script_failure. Então você pode especificar um comportamento para falhas que ocorram como resultado de parâmetros inválidos sendo passados e para falhas que ocorram dentro do script.

Fábio Rizzo Matos

Fábio Rizzo Matos - Membro ativo da Comunidade Python/Zope e Plone, para qual escreve diversos artigos. Arquiteto de Software e Desenvolvedor, trabalha atualmente na ThreePointsWeb (contato@threepointsweb.com), empresa especializada em desenvolvimento e treinamentos Python, Zope e Plone, realizando treinamentos e consultorias em Plone. Mantenedor do site http://www.fabiorizzo.com além de responsável pela tradução de conteúdo.