Todo desenvolvedor web sabe o trabalho que dá desenvolver formulários de qualidade para suas aplicações. Por esse motivo, praticamente todos os frameworks web fornecem algum tipo de geração de formulários automáticos e/ou componentes para faciltar a criação de formulários customizados.
No caso do Plone (o framework) não é diferente e o framework Archetypes já incluia nativamente a geração de formulários para seus tipos de conteúdo. Porém, além de ser complicado para customizar e debugar, existiam claros limites no que se poderia realizar com eles.
Durante o desenvolvimento do BlueBream Aplication Server (antigo Zope3), uma variedade de componentes foram desenvolvidos que hoje fazer parte do ZTK (Zope Tool Kit). Um desses componentes é o pacote zope.formlib, que justamente foi criado para possibilitar a criação automática de formulários para objetos de conteúdo (models) e também para definição de formulários customizados. Porém, apesar da grande melhoria, a customização do formulário, definição de novas widgets ainda não eram satisfatórios.
Com base da experiência e problemas do zope.formlib, que surgiu em 2007 o framework z3c,form. Após anos de amadurecimento, hoje ele está totalmente integrado ao Plone e atualmente é considerado a melhor opção para a criação de formulários. O z3c.form possui um excelente nível de maturidade, cobertura de testes e mais importante: documentação detalhada e grande flexibilidade, sem que isso torne o torne tão complexo e difícil de se aprender.
O objetivo dessa palestra e fornecer uma visão geral do framework e seus componentes, e realizar um passeio por exemplos de formulários desenvolvidos com z3c.form que demonstrem seus recursos e sua integração com Plone.
3. Introdu¸˜o
ca
Por que formularios sao importantes?
Como posso simplificar a cria¸ao de formularios?
c
Como posso melhorar o reuso?
Como os formul´rios podem ser mais resistentes a falhas?
a
4. CMFFormController
Recursos:
Cola que conecta scripts com formul´rios e vice-versa
a
Simplifica a codifica¸˜o de formul´rios / scripts para
ca a
manipular formul´rios
a
Gerencia a transi¸˜o entre formul´rios e scripts
ca a
Conponentes: Forms (.cpt), Validators(.vpy),
Actions(Butt˜es), Scritps(.cpy), State
o
Problemas:
Arquivos espalhados dificultam o entendimento do fluxo
Cria¸˜o e exibi¸˜o de widgets n˜o s˜o tratadas
ca ca a a
N˜o funciona com ZCA packages / views
a
5. zope.formlib
Caracteristicas:
pacote padr˜o do ZTK para cria¸ao de formularios
a c
formularios sao um tipo de BrowserPage (context, request)
define classes reus´veis para cria¸˜o de formul´rios
a ca a
possui template padrao (plone.app.form overrrides)
permite a customiza¸ao de widgets, actions, validators, etc
c
define o padr˜o update() e render() ao ser invocado
a
Problemas:
Nivel de customiza¸ao e limitado para casos mais complexos
c
Nao e pensado para a definicao de formularios aninhados (pai
e filho)
Documenta¸ao escassa: ”luke use de force: read the source”
c
6. z3c.form
O objetivo geral do z3c.form e tornar o desenvolvimento de
formul´rios o mais simples poss´ e ao mesmo tempo prover
a ıvel
hooks que permitam a customiza¸˜o dos formul´rios em qualquer
ca a
n´ de acordo com as necessidades reais de diferentes casos de
ıvel
uso. Principais componentes (m´dulos):
o
form formul´rios base: Form, AddForm, EditForm,
a
DisplayForm
groups formul´rios compostos de grupos de campos
a
(fieldsets)
subform formul´rios aninhados
a
field API para manipulal¸˜o dos campos do formul´rio
ca a
button API para manipula¸˜o dos bot˜es do formul´rio
ca o a
validator API de valida¸˜o dos dados do formul´rio
ca a
widget API de cria¸˜o das widgets
ca
action API para defini¸˜o e manipula¸˜o de actions handlers
ca ca
7. z3c.form: processamento do formulario
Elementos principais:
self.request objeto representando a requis¸ao HTTP atual
c
self.context Item associado ao formul´rio de acordo com contexto
a
no qual ele foi invocado;
self.getContent() Objeto retirado do contexto e que ser´
a
manipulado pelo formul´rio a n˜o ser que
a a
ignoreContext seja definido como True;
self.status A mensagem que ser´ exibida no topo da regi˜o do
a a
conte´do quando o formul´rio for renderizado.
u a
updateWidgets atualiza todas as widgets de acordo com os dados
enviados
updateActions invoca os actions handlers do formul´rio de acordo
a
com o bot˜o pressionados
a
render invoca o template padr˜o que gera o HTML do
a
formul´rio e retorna esse conte´do
a u
8. Exemplo: Person Interface
1 import z o p e . i n t e r f a c e
2 import z o p e . schema
3
4 c l a s s IPerson ( zope . i n t e r f a c e . I n t e r f a c e ) :
5
6 i d = z o p e . schema . TextLine (
7 t i t l e =u ' ID ' ,
8 r e a d o n l y=True ,
9 r e q u i r e d=True )
10
11 name = z o p e . schema . TextLine (
12 t i t l e =u ' Name ' ,
13 r e q u i r e d=True )
14
15 g e n d e r = z o p e . schema . C h o i c e (
16 t i t l e =u ' Gender ' ,
17 v a l u e s =( ' male ' , ' f e m a l e ' ) ,
18 r e q u i r e d=F a l s e )
19 ...
9. Exemplo: Person
1 from z o p e . schema . f i e l d p r o p e r t y import F i e l d P r o p e r t y
2
3 c l a s s Person ( o b j e c t ) :
4 z o p e . i n t e r f a c e . implements ( I P e r s o n )
5
6 id = FieldProperty ( IPerson [ ' id ' ])
7 name = F i e l d P r o p e r t y ( I P e r s o n [ ' name ' ] )
8 gender = F i e l d P r o p e r t y ( IPerson [ ' gender ' ] )
9
10 def init ( s e l f , i d , name , g e n d e r=None ) :
11 s e l f . id = id
12 s e l f . name = name
13 i f gender :
14 s e l f . gender = gender
10. Exemplo: PersonAddForm
1 from z 3 c . form import form , f i e l d
2
3 c l a s s PersonAddForm ( form . AddForm ) :
4
5 f i e l d s = f i e l d . Fields ( IPerson )
6
7 def create ( s e l f , data ) :
8 return Person (** data )
9
10 d e f add ( s e l f , o b j e c t ) :
11 s e l f . context [ object . id ] = object
12
13 d e f nextURL ( s e l f ) :
14 r e t u r n ' i n d e x . h t ml '
11. Exemplo: processamento do formul´rio
a
1 from z 3 c . form . t e s t i n g import T e s t R e q u e s t
2
3 request = TestRequest ()
4 addForm = PersonAddForm ( app , r e q u e s t )
5 addForm . u p d a t e ( )
Nesse momento, ocorre uma s´ria de a¸˜es:
e co
As widgets s˜o inicializadas de acordo com os campos
a
definidos no atributo fields do formul´rio
a
O atributo addForm.widgets ´ o widget manager que cont´m
e e
todas as widgets inicializdas
Por´m n˜o s˜o criadas widgets para todos os campos: se o
e a a
dado n˜o estiver acess´ no objeto ou um se houver uma
a ıvel
configura¸˜o para ignorar esse campo
ca
12. Exemplo: inicializa¸˜o das widgets
ca
V´rias informa¸˜es dos campos s˜o transferidas para as widgets,
a co a
mas podem ser alterados posteriormente:
1 addForm . w i d g e t s [ ' age ' ]
2
3 # f i e l d . t i t l e −> age . l a b e l
4 age . l a b e l
5 u ' Age '
6
7 #f i e l d . r e q u i r e d −> age . r e q u i r e d
8 age . r e q u i r e d
9 False
O pr´ximo passo ´ determinar o valor que deve ser exibido na
o e
widget, na seguinte ordem:
O valor padr˜o definido no schema
a
O valor atual do objeto que est´ sendo manipulado
a
O valor armazenado no request, se o formul´rio ainda n˜o foi
a a
submetido ou ocorreu erros na valida¸˜o
ca
13. Exemplo: propriedades das widgets
A propriedade mode define se a widget ser´ renderizada para
a
edi¸˜o (input) ou visualiza¸˜o (display):
ca ca
1 age . mode
2 ' input '
O modo da widget ´ inicializado seguindo a seguinte ordem (os
e
itens posteriores sobrescrevem os anteriores):
O valor global definido no widget manager
A permiss˜o do attributo do objeto que est´ sendo manipulado
a a
A flag readonly definida no schema
O atributo mode definido no field
Outras propriedades que podem ser customizadas na widget s˜o os
a
atributos: label e required.
14. Exemplo: encontrar um action manager e execut´-lo
a
O bot˜o ”Add” ´ ao mesmo tempo um action e um widget:
a e
1 l e n ( addForm . a c t i o n s )
2 1
3
4 a d d A c t i o n = addForm . a c t i o n s [ ' add ' ]
5 addAction . t i t l e
6 u ' Add '
7
8 addAction . value
9 u ' Add '
O action handler do AddForm realiza sequencia de a¸oes:
c
chama o metodo create(data) passando os dados enviados no
formulario
chama o metodo add(object) com o resultado do create()
redireciona para a URL retornada pelo metodo nextURL() se
foi finalizada a cria¸˜o do novo objeto
ca
15. Exemplo: form.AddForm.handleAdd
1 c l a s s AddForm ( Form ) :
2 ”””A f i e l d and b u t t o n b a s e d add form . ”””
3 z o p e . i n t e r f a c e . implements ( i n t e r f a c e s . IAddForm )
4 i g n o r e C o n t e x t = True
5 i g n o r e R e a d o n l y = True
6 finishedAdd = False
7
8 @button . b u t t o n A n d H a n d l e r ( ( ' Add ' ) , name= ' add ' )
9 d e f handleAdd ( s e l f , a c t i o n ) :
10 data , e r r o r s = s e l f . e x t r a c t D a t a ( )
11 if errors :
12 s e l f . status = s e l f . formErrorsMessage
13 return
14 o b j = s e l f . createAndAdd ( data )
15 i f o b j i s not None :
16 # mark o n l y a s f i n i s h e d i f we
17 # g e t t h e new o b j e c t
18 s e l f . f i n i s h e d A d d = True
16. Exemplo: form.AddForm.createAndAdd
1 c l a s s AddForm ( Form ) :
2 ”””A f i e l d and b u t t o n b a s e d add form . ”””
3 z o p e . i n t e r f a c e . implements ( i n t e r f a c e s . IAddForm )
4 i g n o r e C o n t e x t = True
5 i g n o r e R e a d o n l y = True
6 finishedAdd = False
7
8 # c u t ##
#
9
10 d e f createAndAdd ( s e l f , d a t a ) :
11 obj = s e l f . c r e a t e ( data )
12 event = l i f e c y c l e e v e n t . ObjectCreatedEvent ( obj )
13 zope . event . n o t i f y ( event )
14 s e l f . add ( o b j )
15 return obj
17. Exemplo: renderiza¸˜o do formulario PersonAddForm
ca
1 >>> p r i n t addForm ( ) # u p d a t e ( ) e r e n d e r ( )
2 <form a c t i o n=” . ”>
3 <d i v c l a s s=” row ”>
4 < l a b e l f o r=” form−w i d g e t s −i d ”>ID</ l a b e l>
5 <i n p u t t y p e=” t e x t ” i d=” form−w i d g e t s −i d ”
6 name=” form . w i d g e t s . i d ”
7 c l a s s=” t e x t −w i d g e t r e q u i r e d t e x t l i n e − f i e l d ”
8 v a l u e=” ” />
9 </ d i v>
10 . . .
11 <d i v c l a s s=” a c t i o n ”>
12 <i n p u t t y p e=” s u b m i t ”
13 i d=” form−b u t t o n s −add ”
14 name=” form . b u t t o n s . add ”
15 c l a s s=” s u b m i t −w i d g e t
16 b u t t o n − f i e l d ” v a l u e=”Add” />
17 </ d i v>
18 </ form>
18. Exemplo: form.EditForm
1 c l a s s EditForm ( Form ) :
2 ”””A s i m p l e e d i t form w i t h an a p p l y b u t t o n . ”””
3 z o p e . i n t e r f a c e . implements ( i n t e r f a c e s . I E d i t F o r m )
4
5 s u c c e s s M e s s a g e = ( ' Data s u c c e s s f u l l y u p d a t e d . ' )
6 noChangesMessage = ( ' No c h a n g e s were a p p l i e d . ' )
7
8 @button . b u t t o n A n d H a n d l e r ( ( ' A p p l y ' ) , name= ' a p p l y ' )
9 d e f ha n d l eA p p ly ( s e l f , a c t i o n ) :
10 data , e r r o r s = s e l f . e x t r a c t D a t a ( )
11 if errors :
12 s e l f . status = s e l f . formErrorsMessage
13 return
14 changes = s e l f . applyChanges ( data )
15 i f changes :
16 s e l f . status = s e l f . successMessage
17 else :
18 s e l f . s t a t u s = s e l f . noChangesMessage
19. Exemplo: form.EditForm
1 c l a s s EditForm ( Form ) :
2 ..
3 d e f applyChanges ( s e l f , d a t a ) :
4 content = s e l f . getContent ()
5 c h a n g e s = a p p l y C h a n g e s ( s e l f , c o n t e n t , d a t a ) #* *
6 # `` changes `` i s a d i c t i o n a r y ;
7 # i f empty , t h e r e were no c h a n g e s
8 i f changes :
9 # C o n s t r u c t change−d e s c r i p t i o n s
10 # f o r t h e o b j e c t −m o d i f i e d e v e n t
11 descriptions = []
12 f o r i n t e r f a c e , names i n c h a n g e s . i t e m s ( ) :
13 d e s c r i p t i o n s . append (
14 lifecycleevent . Attributes ( interface ,
15 * names ) )
16 # Send o u t a d e t a i l e d o b j e c t −m o d i f i e d e v e n t
17 event . n o t i f y (
18 l i f e c y c l e e v e n t . ObjectModifiedEvent (
19 content ,
20 * descriptions ))
21 return changes
20. z3c.form e a integra¸ao com Ploe:plone.app.z3cform
c
Para usar o template do pacote plone.app.z3c.form, ´ necess´rio
e a
registrar um novo skin Layer. No profile do seu produto, ao incluir
plone.app.z3c.form como dependencia, ele automaticamente
instala esse novo Layer e dessa forma teremos a valida¸ao in-line e
c
outras melhorias no template padrao:
Arquivo: meuproduto/profiles/default/metadata.xml
1 <m et a da t a>
2 < v e r s i o n>1</ v e r s i o n>
3 <d e p e n d e n c i e s>
4 <dependency>
5 p r o f i l e −p l o n e . app . z 3 c f o r m : d e f a u l t
6 </ dependency>
7 </ d e p e n d e n c i e s>
8 </ m et a d a t a>
21. 1 from p l o n e . z 3 c f o r m . l a y o u t import w r a p f o r m
2 from z 3 c . form import form , f i e l d
3
4 c l a s s ApybMemberDisplayForm ( form . Form ) :
5 mode = ' d i s p l a y '
6 f i e l d s = f i e l d . F i e l d s ( IApybMember ) . s e l e c t ( ' f u l l n a m e ' ,
7 ' organization ' , ' email ' , ' c i t y ' , ' uf ' , ' type ' ,
8 ' community relation ' )
9 f i e l d s [ ' type ' ] . widgetFactory = radio . RadioFieldWidget
10 l a b e l = ( u ' P e r f i l do Membro ' )
11 d e s c r i p t i o n = ( u ' ' ' Segue a b a i x o o s d e t a l h e s
12 do p e r f i l do membro : ' ' ' )
13 ...
14
15 MemberDisplayFormView = w r a p f o r m ( ApybMemberDisplayForm )
22. 1 from p l o n e . z 3 c f o r m . l a y o u t import w r a p f o r m
2 from z 3 c . form import form , f i e l d
3
4 c l a s s ApybMemberDisplayForm ( form . Form ) :
5 ..
6 d e f getContent ( s e l f ) :
7 session = Session ()
8 e m a i l = s e l f . r e q u e s t . g e t ( ' e m a i l ' , None )
9 q u e r y s e s s i o n . q u e r y ( model . ApybMember )
10 member = q u e r y . f i l t e r b y ( e m a i l=e m a i l )
11 i f not member :
12 status = IStatusMessage ( s e l f . request )
13 message = ” C a d a s t r o de membro nao e n c o n t r a d o ”
14 s t a t u s . a d d S t a t u s M e s s a g e ( message , type= ' i n f o ' )
15 url = s e l f . context . absolute url ()
16 return s e l f . request . response . r e d i r e c t ( u r l )
17 else :
18 r e t u r n member . one ( )
19
20 MemberDisplayFormView = w r a p f o r m ( ApybMemberDisplayForm )
23. z3c.form: alterando o template de renderiza¸˜o do
ca
formulario
1 # Do n o t mix w i t h :
2 # Products . Five . browser ViewPageTemplateFile
3 from z o p e . app . p a g e t e m p l a t e import ViewPageTemplateFile
4 as Z o p e 3 P a g e T e m p l a t e F i l e
5
6 c l a s MyForm ( form . AddForm ) :
7 ”””MyForm Example ”””
8 t e m p l a t e = Z o p e 3 P a g e T e m p l a t e F i l e ( ”my−t e m p l a t e . p t ” )
24. z3c.form: alterando o template de renderiza¸˜o do
ca
formulario
1 # Do n o t mix w i t h :
2 # Products . Five . browser ViewPageTemplateFile
3 from z o p e . app . p a g e t e m p l a t e import ViewPageTemplateFile
4 as Z o p e 3 P a g e T e m p l a t e F i l e
5
6 c l a s MyForm ( form . AddForm ) :
7 ”””MyForm Example ”””
8 t e m p l a t e = Z o p e 3 P a g e T e m p l a t e F i l e ( ”my−t e m p l a t e . p t ” )
25. Perguntas?
Contato: Rud´ Porto Filgueiras
a
rudazz@gmail.com
@rudaporto
github.com/rudaporto