Primeiros passos no desenvolvimento com o web2py – Tutorial parte 1

logo

Web2py é um framework livre de código aberto, utilizado para o desenvolvimento ágil de aplicações web seguras,
baseadas em banco de dados; foi desenvolvido em Python e é programável com Python.
Trata-se de um framework “full-stacked”, ou seja, ele já vem com todos os componentes necessários para o desenvolvimento básico de aplicações para web.
As aplicações no web2py são desenvolvidas seguindo o padrão MVC, que permite a melhor organização entre as camadas de dados, lógica e apresentação.
Além de diversos facilitadores para as tarefas de modelagem de banco de dados, criação de formulários com validação e Ajax, marcação de templates e upload de arquivos, o web2py também possui uma estrutura pronta para servir conteúdo nos mais diversos formatos, como por exemplo feeds RSS, webservices XML-RPC e permite a utilização de módulos e plugins adicionais.
O web2py possibilita que o desenvolvedor dedique-se integralmente à criação da aplicação, pois não demanda instalações nem configurações complicadas: basta baixar o framework e começar a desenvolver.

Uma das principais características do web2py é o fato de não demandar instalação para o início do desenvolvimento: basta baixar o binário executável, que já vem, inclusive, com o interpretador Python, banco de dados e o WebServer. Caso seu sistema operacional já tenha o Python 2.5+ instalado, você pode baixar o fonte do web2py e executar.

Apesar de você ter a possibilidade de desenvolver utilizando seu IDE ou editor favorito, o web2py vem com uma interface administrativa bastante completa, que permite administrar bases de dados, gerenciar aplicações e editar códigos em um editor web-based. O web2py já vem com o plugin do Zen-Coding habilitado.

O web2py utiliza o padrão de MVC, que possibilita uma separação inteligente entre o modelo de dados (Model), a lógica da aplicação (Controller) e a interface de apresentação (View). Este padrão permite que o programador desenvolva os Models e Controllers, e que um designer trabalhe com as Views de uma forma muito intuitiva, com liberdade de criação do HTML. Porém, o web2py tem um modelo próprio de marcação de template que dá a liberdade de, em alguns casos, executar o código Python mais complexo diretamente na VIEW, sendo assim um framework bastante dinâmico.

De acordo com o seu criador, o web2py foi inspirado no Ruby on Rails, por permitir o desenvolvimento ágil. Também foi inspirado no Django, no sentido de fornecer facilitadores, geradores de formulários e camada de abstração do acesso a dados.

Antes de começar o tutorial, que irá mostrar como desenvolver um blog com administração de posts e inserção de comentários com pesquisa via Ajax, vou listar as principais características do framework.

  • Livre, OpenSource
  • Mecanismos de segurança em formulários
  • Autenticação integrada RBAC
  • Garantia de compatibilidade entre versões
  • Fácil de executar, não necessitando instalação, permitindo executar e armazenar projetos em um pen-drive
  • Não necessita de configuração inicial (mas possibilita configuração refinada, caso seja necessário)
  • MVC – Model – View – Controller
  • Roda em Mac, Unix, Linux, Windows, Google App Engine, Amazon EC2, e quase todos os webservers através do Python ou via Java com o Jython
  • Roda no Apache e em qualquer webserver com CGI, fastCGI, WSGI, mod_proxy ou mod_python
  • Conversa com a maioria dos bancos SQL, incluindo SQlite, PostGre, MySQL, MSSQL ORACLE, entre outros
  • Muitas maneiras de servir informações como HTML/XML, ATOM, RSS, RTF, JSON, AJAX, XML-RPC, REST, RDF
  • Permite distribuir as aplicações compiladas
  • Sistema facilitado para internacionalização, permitindo múltiplas línguas em uma única aplicação

Agora vamos ao que interessa! Na próxima página, daremos início ao tutorial.

TUTORIAL – Construindo um blog com banco de dados, administração de posts, comentários, imagens e documentos anexos, RSS e busca via Ajax – Parte 1

1. Faça o download do web2py

Isso depende do seu sistema operacional e também do fato de você ter ou não o Python instalado. Eu aconselho você a instalar o Python 2.5+ no seu sistema operacional e baixar a versão de código fonte, mas você pode pular este passo e baixar um executável caso esteja usando Mac ou Windows:

Baixe aqui o executável completo para windows

Baixe aqui o executável completo para Mac

Caso prefira usar a versão código-fonte:

Baixe e instale o Python caso ainda não tenha

Baixe aqui o web2py source-code

2. Executando o web2py

Caso tenha baixado as versões executáveis, apenas descompacte e execute no Mac ou Windows.
Já no caso da versão source code, após ter instalado o Python abra um terminal do seu sistema operacional e digite:

 python web2py.py

Ao executar, o web2py exibirá uma tela com algumas configurações iniciais:

Choose a password: neste campo coloque uma senha qualquer, ela será exigida para que você possa acessar o admin do web2py.
Running from host: não altere este campo, ele é a porta de loopback que indica que o webserver está rodando na sua própria máquina.
Running from port: deixe como está e ele rodará na porta 8000, mude apenas se quiser publicar o conteúdo na porta 80 ou se a porta 800 já estiver ocupada; para desenvolvimento não é necessário alterar.

Após clicar em Start Server, você terá o web2py rodando e o seu navegador abrirá automaticamente a aplicação de boas vindas. Caso isso não aconteça, abra seu navegador (eu disse navegador não I.E) e vá para o seguinte endereço:

http://127.0.0.1:8000

Você verá a tela de boas vindas, o que significa que já está tudo rodando e que podemos iniciar o desenvolvimento.

Clique no link [click here for administrative interface], será necessário colocar a senha configurada no início…

… e então você terá acesso ao Admin do web2py (clique na imagem abaixo para ampliar)

Nesta tela de administração, é possível acessar os seguintes itens:

  1. Lista de aplicações instaladas
  2. Informações sobre versão e link para atualização automática do web2py
  3. Formulário para criação de novas aplicações
  4. Formulário para importação de aplicação empacotada
  5. Últimos anúncios oficiais via Twitter do web2py

As aplicações instaladas são pastas que estão dentro da pasta APPLICATIONS do framework, por isso, você pode utilizar seu editor ou IDE favorita e chamar via terminal algumas funções específicas do core do web2py. Porém, neste tutorial, iremos abordar apenas o trabalho efetuado diretamente na interface web – assim, todo o trabalho será feito no navegador.

Para começar, crie uma nova aplicação chamada BLOG

Insira o nome BLOG e clique em Create.

Você será redirecionado para a página de DESIGN da sua aplicação

Nesta página, 3 seções distintas são apresentadas:

MODELS, CONTROLLERS, VIEWS

Em cada uma dessas seções iremos desenvolver o código da aplicação. Mais abaixo, você verá outras opções como a LANGUAGES, que permite a definição das linguagens da app;  STATIC FILES, que contém arquivos estáticos que não exigem processamento (como imagens, arquivos de upload etc); MODULES, que é usado para armazenar módulos Python externos; e PLUGINS, que permite adicionar uma série de plugins para paginação, grids, interfaces, entre outras coisas.

Vamos começar pelo MODEL. Procure a linha que indica o arquivo db.py e clique em edit

Por padrão, este arquivo inicialmente se chama db.py, mas poderia ter qualquer outro nome. Neste arquivo iremos definir o banco de dados e o modelo das tabelas. Como o web2py já vem nativamente com o SQLite (na verdade já vem no Python), usaremos o SQLite, mas seria o mesmo processo para qualquer outro banco suportado, necessitando apenas a alteração da string de conexão.

A String de Conexão

Ignore os comentários e todo o restante do código que está no arquivo db.py. Deixe-o como está e procure a linha:

db = DAL('sqlite://storage.sqlite')


Na linha acima definimos uma variável “db” que será nossa referência para o banco sqlite (armazenado na pasta databases do framework com o nome storage.sqlite). Este nome é um padrão,
mas poderíamos mudar o nome do banco na string de conexão, de acordo com o tipo de banco de dados.

O web2py tem uma camada de abstração de banco de dados “DAL”, que traduz nosso código Python em comandos SQL de acordo com o banco de dados definido na string de conexão. Isso é feito de maneira transparente, o que permite a mudança de SQLite para MySQL (por exemplo).

A sintaxe para modelagem de tabelas é:

db.define_table(
        'mytable',
        Field('myfield1','string',required=true),
        Field('myfield2','integer',requires=IS_NOT_EMPTY())
        )

Onde

db é nossa referência para a instância da classe DAL, que por sua vez está ligada ao banco SQLite
define_table é o método mais importante da classe DAL que instancia objetos do tipo Table – nossa tabela no banco de dados
‘mytable’ é o nome que damos à tabela; este parâmetro será sempre o primeiro a ser passado ao método
Field é outro método da classe DAL que define cada campo da tabela e recebe como parâmetros: nome do campo, tipo de dados e validadores (para informações detalhadas a respeito dos validadores, acesse o web2py-book – os validadores utilizados aqui serão autoexplicativos)

Vá até a última linha do arquivo, dê um enter e começaremos então a modelar as tabelas do nosso banco de dados “db”.

A primeira coisa que faremos é importar o módulo datetime do Python. Este módulo se comunica com o sistema operacional e nos permite registrar a data e hora da postagem.

Na versão atual do framework, já é possível utilizar diretamente o comando response.now para retornar data e hora. Apesar disso, quis mostrar aqui como importar um módulo py externo.

Após esta linha, modelaremos nossa principal tabela que irá conter os posts do blog.

import datetime
db.define_table(
         'posts',
         Field('post_title',notnull=True,required=True,
               requires=[IS_NOT_EMPTY(error_message=T('Fill the field!')),
               IS_NOT_IN_DB(db, 'posts.post_title')]),
         Field('post_text', 'text'),
         Field('post_time', 'datetime', default=datetime.datetime.today()),
         Field('post_category',db.categories, required=True,
               requires = IS_IN_DB(db, 'categories.id', 'categories.category_name')),
         Field('author', db.auth_user, default=user_id)
)


A tabela acima chama-se ‘posts’
Ela possui os campos:


‘post_title’ é do tipo ‘string’ (definido automaticamente quando não informamos um tipo) e armazenará o
título da postagem. Ele é um campo de preenchimento obrigatório –  note que neste exemplo coloquei 3 validadores para definir a obrigatoriedade, sendo eles:
o notnull=True define a propriedade NOT NULL do banco de dados e não permite a inserção nula, retornando erro
required=True informa à classe DAL que esta é uma propriedade obrigatória. Este processo ocorre no nível do Controller, sendo assim, será validado mesmo que não exista um formulário (por exemplo, quando acessado via webservice ou xml-rpc)
o
requires=IS_NOT_EMPTY(error_message=T(‘Fill the field!’)) define o validador de formulários, desta forma, quando criarmos um FORM utilizando os helpers do web2py, o formulário será automaticamente validado 😉 e será exibida a mensagem ‘Fill the Field’ (que poderá ser traduzida pelo T() para qualquer idioma previamente definido).

post_text’ é do tipo ‘text’, responsável pela armazenagem do corpo do texto da postagem, e não será validado
‘post_time’ é do tipo ‘datetime’, campo que recebe uma data no formato ‘YYYY-MM-DD HH:MM:SS’; seu valor padrão será a data atual
‘post_category’ é um campo obrigatório que recebe a categoria da postagem
‘post_author‘ armazena o id do usuário logado no sistema

Seguindo o mesmo esquema iremos modelar as demais tabelas, mas antes é preciso entender 2 novos conceitos do DAL no web2py:

‘upload’ – quando declaramos o campo do tipo ‘upload’, o web2py automaticamente cria um campo que recebe o path para um arquivo, desta forma, quando criarmos um formulário ele irá conter um controle de upload e este arquivo será salvo na pasta UPLOADS da aplicação – o web2py irá nomear automaticamente e criar a referência no banco de dados.

‘db.<reference_table>’ por exemplo, na tabela ‘posts’ iremos ter um relacionamento com a tabela ‘categories’, onde a tabela posts receberá o id da categoria. Logo, o campo post_category será um campo que recebe o parâmetro ‘db.categories’, significando que existe um relacionamento do tipo ‘Um para Muitos’ dessa tabela com a tabela categories (não se preocupe muito com isso, pois irá entender esse conceito na prática).

Também incluiremos mais um bloco de código que determina o user_id do usuário que está logado; o web2py já implementa automaticamente o sistema de autenticação, então apenas usuários logados poderão criar posts.

Colocaremos um campo do tipo ‘boolean’ que receberá True ou False para o controle da moderação dos comentários.

Na sequência, definiremos mais algumas validações para o nível do formulário que nos permitirão o relacionamento entre as tabelas e a criação automática dos combo-box para preenchimento dos campos do formulário.

IS_EMAIL() garante que o valor inserido seja um e-mail válido

IS_IN_DB(db.table,’table.field’) define que o valor deve estar contido em outra tabela

IS_NOT_IN_DB() define que o valor não pode estar contido em outra ou na mesma tabela, trata-se de um validador muito útil para evitar cadastros em duplicidade. Você perceberá que podemos definir os validadores diretamente na declaração do Field() (como fizemos na tabela posts) e que também podemos fazer isso no final do arquivo de Model (como será feito abaixo – *recomendado*)

Clique para ver a Listagem completa dos validadores

As opções writable e readable, como os próprios nomes dizem, definem se o campo será visível ou editável nos formulários.

O código pronto do MODEL db.py deve ficar como o código abaixo. Caso não queira digitar todo o código, basta baixar e substituir o seu arquivo, que se encontra em web2py/applications/BLOG/models/db.py, pelo arquivo: [ download MODEL do BLOG db.py ]

import datetime

if auth.is_logged_in():
    user_id = auth.user.id
else:
    user_id = None

db.define_table(
        'categories',
        Field('category_name', required=True)
        )

db.define_table(
         'posts',
         Field('post_title',notnull=True,required=True,
               requires=[IS_NOT_EMPTY(error_message=T('Fill the field!')),
               IS_NOT_IN_DB(db, 'posts.post_title')]),
         Field('post_text', 'text'),
         Field('post_time', 'datetime', default=datetime.datetime.today()),
         Field('post_category',db.categories, required=True,
               requires = IS_IN_DB(db, 'categories.id', 'categories.category_name')),
         Field('author', db.auth_user, default=user_id)
)

db.define_table(
        'comments',
         Field('post_id', db.posts, required=True),
         Field('comment_author'),
         Field('comment_author_email', required=True),
         Field('comment_author_website'),
         Field('comment_text', 'text', required=True),
         Field('comment_time', 'datetime', required=True,
              default=datetime.datetime.today()),
         Field('approved','boolean')
               )

db.define_table(
        'document',
        Field('post_id', db.posts, required=True),
        Field('name'),
        Field('file', 'upload'),
        Field('created_on', 'datetime', default=request.now),
        Field('created_by', db.auth_user, default=user_id)
        )

db.posts.post_text.requires = IS_NOT_EMPTY()
db.posts.author.readable = False
db.posts.author.writable = False

db.comments.post_id.requires = IS_IN_DB(db, 'posts.id', '')
db.comments.comment_text.requires = IS_NOT_EMPTY()
db.comments.comment_author_email.requires = IS_EMAIL()
db.comments.comment_author_website.requires = IS_URL()
db.comments.post_id.readable = False
db.comments.post_id.writable = False

db.document.post_id.requires = IS_IN_DB(db, 'posts.id', '')
db.document.name.requires = [IS_NOT_EMPTY(), IS_NOT_IN_DB(db, 'document.name')]
db.document.post_id.readable = False
db.document.post_id.writable = False
db.document.created_by.readable = False
db.document.created_by.writable = False
db.document.created_on.readable = False
db.document.created_on.writable = False

As 64 linhas acima definiram a modelagem do banco de dados, os relacionamentos e os validadores necessários para nossos formulários. Neste momento, você já pode se certificar de que o Model está salvo e, então, voltar à interface administrativa clicando em EDIT no menu superior do web2py.

Agora iremos executar uma aplicação chamada APPADMIN. Essa aplicação, que é acessada através do link Database Administration, é a responsável por gerenciar o banco de dados. Ao clicar no link, o web2py irá verificar se o banco de dados existe e, caso não exista, ele irá criar um automaticamente ;). Esse processo ocorrerá também para cada tabela que definimos no modelo, e a qualquer momento que alterarmos o modelo das tabelas o web2py irá efetuar todas as alterações necessárias (você poderá verificar os comandos SQL executados clicando no link sql.log).

Clique em database administration para ter acesso ao admin das tabelas, onde você poderá efetuar operações CRUD em suas tabelas e efetuar selects, joins e outros comandos de consulta (discutiremos depois). Note que o web2py já criou para você algumas tabelas de controle de usuários, grupos e autenticação.

Clicando no nome da tabela você verá a tela de pesquisa: nela poderá executar comandos do DAL que são equivalentes aos comandos SQL. Veja exemplos aqui (os exemplos serão revistos no momento de criação dos Controllers).

Clicando em insert newcategories você verá formulários de inserção criados automaticamente pelo web2py; insira 3 categorias apenas para teste (Receitas, Viagens, Fotos):

Se você já chegou até aqui, então significa que já temos o modelo das tabelas e o banco de dados prontos! Vamos passar para o Controller que será a camada lógica da nossa aplicação

Controllers

Controller faz exatamente o que o nome diz: “controla” as ações da aplicação. Os Controllers definem as ACTIONS que, analisando de uma maneira bem simplificada, são funções Python. Se você já programa em Python deve conhecer a sintaxe de uma função ou método.

def ActionName():
      [execute algum código]
      return [alguma resposta]

Algumas regras devem ser seguidas na construção dos controllers. Obviamente que a primeira delas é a regra de endentação do Python, portanto, no controller é necessário escrever corretamente o código Python – se você não conhece muito a respeito de endentação, indico a leitura do post Codifique como um verdadeiro pythonista e do PEP257.

O web2py tem um esquema de mapeamento de URLs baseado nas actions, e este esquema funciona da seguinte forma:

http://127.0.0.1:8000/BLOG/Controller/ActionName/

Onde :

http://… é o endereço do seu servidor
BLOG é o nome da aplicação e o nome da pasta dentro de applications
Controller é o nome do arquivo de controller ex. default.py, cadastros.py etc
ActionName é o nome da action que você criou no controller

Portanto, quando você define uma função Python que não recebe argumentos e retorna um dicionário ou um texto, o web2py irá expor esta função; se você não criar uma VIEW contendo o mesmo nome da função, o web2py irá utilizar a VIEW genérica e exibirá o output do conteúdo, além disso o web2py tem VIEWS genéricas que permitem fazer output do retorno de uma função via RSS, XML, JSON e HTML de forma automática.
Mas você obviamente irá customizar o visual e os detalhes da apresentação, ou então irá contratar um designer para criar as VIEWS.

Algumas regras são estabelecidas no ambiente do web2py para expor as funções Python:

  1. Funcões que recebem argumentos ou que comecem com __ não são expostas; as funções __ActionNAme() e ActionName(param1,param2) podem ser acessadas via chamada interna, xml-rpc ou até mesmo via Ajax, mas não são expostas como views.
  2. As actions devem retornar String ou algum outro objeto serializável, e geralmente optamos pelo retorno de um dicionário.

Vamos criar nosso primeiro controller:

Lembre-se que para voltar à home do ADMIN, basta clicar em EDIT no menu superior do web2py. Depois, procure abaixo da seção Controllers o arquivo default.py e clique em edit:

Dentro do arquivo default.py, que é o nosso controller padrão, teremos algumas actions já definidas – não as altere, deixe-as  como estão. Comecemos alterando a função que expõe a View index.html, definida como def index(): no arquivo do controller.

Essa é nossa página inicial e, como é um blog, já começaremos exibindo os posts e as categorias. Repare que faremos acesso ao Model e executaremos queries que irão retornar os registros da tabela ‘posts’ e da tabela categories; também será necessário exibir a quantidade de comentários para cada post e, para isso, executaremos um count() na tabela de comments.

Seu código deve ficar como o código abaixo (altere apenas o index(), não mude nada abaixo disso):

#essas 2 linhas serão o titulo e subtitulo na view
response.title = T('My Blog')
response.subtitle = T('My blog is Python powered')

def index():
    #enviamos uma mensagem de boas vindas
    response.flash = T('Welcome to my BLOG')

    #retornando todos os posts ordenados descendentemente
    #aqui fazemos um Inner join entre as tabelas de posts e categories
    posts = db(db.categories.id==db.posts.post_category)
                      .select(db.posts.ALL,db.categories.ALL,
                                 orderby=~db.posts.post_time)

    # aqui retornamos as categorias e a contagem de posts de cada uma
    # Existem outras formas de fazer isso, mas preferi mostrar as diferentes possibilidades de retorno e iteração
    cats = db().select(db.categories.ALL)
    categories = []
    for cat in cats:
        count = len(db(
                   db.posts.post_category == cat.id
                   ).select(db.posts.ALL))
        categorie = {'id':cat.id,'name':cat.category_name,'posts':str(count)}
        categories.append(categorie)

    #aqui retornamos os dois objetos populados
    return dict(posts=posts,categories=categories)

Já temos nosso Controller pronto para retornar o que precisamos para a View. Se você abrir o navegador e apontar para http://127.0.0.1:8000/BLOG/default/index, verá a view default do web2py retornando os dados em formato de tabela, e mesmo que o seu controller não tenha uma view correspondente, o web2py irá utilizar a view genérica para exibir as variavéis ‘posts’ e  ‘categories’ que foram retornadas (isso é muito útil para debug).

Agora vamos trabalhar esse retorno para colocá-lo em um layout mais apresentavél.

Views

No web2py o template das views é marcado utilizando linguagem Python, e podemos fazer quase tudo que faríamos em um programa Python ou Controller diretamente dentro da View. Apesar de ser recomendado deixar a lógica toda no Controller, às vezes é muito útil colocar um pouco de inteligência nas Views.

O padrão para marcação segue os modelos abaixo, e os demais detalhes veremos no decorrer do tutorial:

  1. O código da view não precisa seguir regras de endentacão do Python
  2. A marcação para escape é feita dentro de {{ e }}
  3. Blocos de código começam nas linhas {{}} terminadas em :
  4. Blocos de código teminam onde encontram a instrução {{pass}}
  5. Nos casos em que a construção do bloco for clara, não será preciso usar o {{pass}}

Exemplo de uso:

<div class="header">
<H1>{{=meutitulo}}</H!>
{{bands = ['Dream Theater','Gathering','Morcheeba']}}
<ul>
{{for band in bands: }}
<li>{{=band}}</li>
{{pass}}
</ul>
</div>

Resultará em:

<div class="header>
<H1>TITULO AQUI</H1>
<ul>
<li>Dream Theater</li>
<li>Gathering</li>
<li>Morcheeba</li>
</ul>
</div>

O mesmo resultado poderia ser obtido em uma única linha com o uso de HTML Helpers:

{{=DIV(H1(meutitulo),UL(*[LI(*band) for band in bands ]),_class='header')}}

Veja como o web2py faz para converter as views em output HTML:

O código abaixo…

<html><body>
{{for x in range(10):}}{{=x}}hello<br />{{pass}}
</body></html>

… será convertido em:

response.write(" <html><body> ", escape=False)
for x in range(10):
    response.write(x)
    response.write("""hello <br />""", escape=False)
response.write(" </body></html> ", escape=False)

Para facilitar, o web2py permite a definição de um arquivo de layout padrão, que possa ser usado como “MAster-Page” para todas as outras views através da utilização das cláusulas {{extend}}  e {{include}}.

Um pequeno exemplo de view que estende um layout padrão:
Código da View:

{{extend meulayout.html}}
<H1>Olá Mundo {{=minhasVariaveis}}</H1>
{{include outrapagina.html}}

O layout padrão a ser estendido deve conter a cláusula {{include}}, para servir de placeholder para o conteúdo renderizado na view.
Código do arquivo meulayout.html:

<html>
<head>
<title>Page Title</title>
</head>
  <body>
    {{include}}
  </body>
</head>

Para facilitar nosso trabalho, deixei pronto o código do layout padrão para o blog, que resultará em:

Sei que não é a coisa mais linda do mundo, mas nada que um designer não possa dar um jeito com CSS! 😉

Faça o download do arquivo [ bloglayout.html ] e o descompacte para web2py/applications/BLOG/views/bloglayout.html. Esse é o arquivo de layout padrão que iremos utilizar em nossas views; vamos entender o conteúdo principal dele:

bloglayout:

Logo no início visualizamos o comando {{include}}, utilizado para a inclusão do arquivo web2py_ajax.html, que engloba a JQUERY e muitas funções para trabalhar com Ajax.

Este layout é um pouco diferente do comum, pois precisamos declarar mais de um placeholder na página, necessitamos de um local para incluir o conteúdo principal e outro para a inclusão dos itens da barra lateral. Por conta destas particularidades, colocaremos a cláusula {{include}} logo no inicio do <body>. O layout também contém uma DIV com a classe “flash”, que servirá para exibir mensagens de notificação.

<body>
{{include}}
<div>{{=response.flash or ''}}</div>
<body>{{include}}<div>{{=response.flash or ''}}</div>

Placeholder para o conteúdo da barra lateral, que será renderizado pela View e declarado na função sidebar():

<div class="categories">

{{if 'sidebar' in globals(): sidebar()}}

</div>

Após a visualização do código acima, será exibida uma sequência de código que define os links para a administração de usuário, login e senha (falaremos sobre Auth na segunda parte deste tutorial).

Na sequência, teremos um placeholder que será o responsável por receber o conteúdo principal das views – definido na função maincontent():

<div class="col2">

{{maincontent()}}

</div>

Agora que esse arquivo já está no diretório de Views, podemos editar a View que responde para a função index() do nosso controller default.py, acessível através da URL http://127.0.0.1:8000/BLOG/default/index. O web2py irá automaticamente rotear a chamada da URL /default/index para o controller default.py. Dentro do default.py ele procurará uma função exposta com o nome index() e irá executar este método.

Então, irá procurar na pasta de Views o arquivo default/index.html e executará o código Python + HTML deste arquivo, respondendo ao navegador.

Dentro da view, as variáveis de retorno do controller, as variáveis globais, os objetos response e request, entre outras coisas, ficarão acessíveis.

Edite a view default/index.html de acordo com o código abaixo:

{{def sidebar():}}
        <h1>{{=T('Sidebar')}}</h1>
        <h3>{{=T('Categories')}}</h3>
        {{if auth.user:}}
                [ {{=A(T('Add Category'),
                        _href=URL(r=request,c='default',f='addcategory'))}} ]
        {{pass}}
        <ul>
        {{for categorie in categories:}}
                {{=LI(A(categorie['name']+' ('+categorie['posts']+')',
                        _href=URL(r=request, f='categoryposts', args=categorie['id'])))}}
        {{pass}}
        </ul>
{{return}}


Nessa primeira parte definimos a função sidebar() que será renderizada em nosso placeholder sidebar, definido no arquivo de layout.

Linhas 2 e 3: exibimos um título e um subtítulo, inseridos na função T() – função que serve para internacionalizar strings (na segunda parte do tutorial falaremos dela)
Linhas 4 a 7: verificamos se o usuário está logado e, em caso positivo, exibimos o link para as funções AddCategory e AddPost que iremos criar em nosso Controller
Linhas 8 a 13 : Varremos a coleção de categorias retornadas pelo Controller e exibimos o nome e a contagem de posts para cada categoria. Existem inúmeras maneiras de se fazer isso, mas optei por esta forma para mostrar uma outra possibilidade

No código abaixo, iremos definir a função maincontent() que será nosso conteúdo principal, responsável pela exibição das postagens, detalhes das postagens e contagem de comentários. Colocaremos links para funções de exibição individual de posts, exibição de comentários e edição de posts (vou dividir em pequenas partes para facilitar o entendimento).

{{def maincontent():}}
        <h1>{{=T('Blog Posts')}}</h1>
        {{for item in posts:}}
                {{=H1(A(item.posts.post_title,
                       _href=URL(r=request, f='showpost', args=item.posts.id)))}}
               <small>{{=item.posts.post_time.strftime("%d/%m/%Y")}}</small>
               {{=P(XML(item.posts.post_text,sanitize=True))}}


Linha 1: definimos a função maincontent()
Linha 2: exibimos um título
Linha 3: iniciamos um Loop for nos itens da coleção de posts retornada
Linhas 4 a 5: Exibimos o título da postagem com permalink para exibição do post
Linha 6: exibimos a data de postagem formatada
Linha 7: Criamos um parágrafo com um parser XML para a exibição do texto de cada postagem, considerando tags HTML. O sanitize=True serve para prevenir inserção de código malicioso

              <p class="postmetadata">
                      {{=T('Posted in %s',item.categories.category_name)}} |
                             {{if auth.user:}}
                                    {{=A(T('Edit'),
                                              _href=URL(r=request, f='editpost', args=item.posts.id))}}|

                             {{pass}}

                        {{count = db.posts.id.count()}}
                        {{for row in db((db.posts.id==db.comments.post_id)&
                                                (db.posts.id==item.posts.id))
                                                 .select(db.posts.ALL, count, groupby=db.posts.id):}}

                                 {{count = row._extra[count]}}
                         {{pass}}

                         {{try:}}
                                  {{=A(T('Comments %s', int(count)),
                                         _href=URL(r=request, f='showpost', args=item.posts.id)+'#comments')}}
                         {{except TypeError:}}
                                  {{=T('No Comments')}}
                         {{pass}}
             </p>
         {{pass}}
{{return}}


Aqui criamos uma DIV para exibir os metadados da postagem (Postado em | Edit| Comentários X)
Linha 2: Mostrado em qual categoria o post está inserido
Linha 3 a 8: Caso o usuário esteja logado, damos a opção de editar a postagem
Linhas 10 a 23: Efetuamos a contagem de comentários para cada post e a exibimos – aqui utilizamos uma outra maneira para efetuar count()
Finalizamos o Loop e retornamos a função

{{extend 'bloglayout.html'}}

A linha acima estende o arquivo de layout padrão, deixando as funções sidebar() e maincontent() disponíveis para serem chamadas em seus placeholders.

Você pode fazer o download do arquivo [ index.html]  e descompactar para /web2py/applications/BLOG/views/default/index.html

Neste momento seu blog já deverá estar funcionando, porém não irá exibir nenhum post, ao menos que você efetue o cadastro via APPADMIN.

Vamos criar agora duas funções dentro do Controller default.py, que permitirão Adicionar Categorias, Adicionar Posts, Exibir Posts e Adicionar Comentários.

Criaremos os formulários automaticamente, utilizando o helper SQLFORM(); ele cria os campos do formulário de acordo com o modelo de dados e regras de validação que já estabelecemos no model.

Dentro do default.py crie as funções conforme o código abaixo (explicação nos comentários):

#decorator que exige login para acesso a esta função
@auth.requires_login()
def addcategory():
       form = SQLFORM(db.categories)
       if form.accepts(request.vars, session):
                   response.flash = 'form accepted'
       return dict(form=form)

#decorator que exige login para acesso a esta função
@auth.requires_login()
def addpost():
      form = SQLFORM(db.posts)
      if form.accepts(request.vars, session):
                  response.flash = 'form accepted'
      return dict(form=form)

def showpost():
      #recebemos o parametro do ID do post e carregamos o registro referente
      post = db.posts[request.args(0)]
      #se não tiver parametro/post selecionado então redirecionamos
      if not post:
           redirect(URL(r=request, f='index'))
      #definimos o parametro para query na tabela de comentários ( dentro deste escopo)
      db.comments.post_id.default = post.id
      #propriedades adicionais do form enquanto ainda nào temos admin de comments
      db.comments.approved.default = True
      db.comments.approved.readable = False
      db.comments.approved.writable = False
      #criamos o formulário para inclusão de posts
      form = crud.create(db.comments)
      #retornamos os comentários já existentes para este post
      comments = db(db.comments.post_id==post.id).select()
      #retornamos para a view
      return dict(post=post,comments=comments,form=form)

Se preferir, faça o download do arquivo default.py aqui: [ default.py]

Crie agora uma view para cada uma das funções criadas acima:

As views criadas serão: default/index.html, default/addpost.html, default/addcategory.html, default/showpost.html

Para cada uma dessas views, que já devem estar funcionando, iremos editar e estender o layout padrão bloglayout.html

Edite os arquivos:

default/addpost.html

{{def maincontent():}}
    <h1>{{=T('Add a post')}}</h1>
        {{=form}}
{{return}}
{{extend 'bloglayout.html'}}

default/addcategory.html

{{def maincontent():}}
    <h1>{{=T('Add a category')}}</h1>
        {{=form}}
{{return}}
{{extend 'bloglayout.html'}}

default/showpost.html

{{def maincontent():}}
    <div class="post">
        <h1>{{=post.post_title}}</h1>
         {{=P(XML(post.post_text,sanitize=True))}}
    </div>

    <div class="comments">
        <h2>{{=T('Comments on this post')}}</h2>
            {{for comment in comments:
                response.write(DIV(comment.comment_author,
                    UL(LI(comment.comment_text))))
            pass}}

    <h3>{{=T('Post a comment')}}</h3>

    {{=form}}

    </div>
{{return}}

{{extend 'bloglayout.html'}}

Agora seu blog já está funcionando, você já pode adicionar posts, adicionar categorias, incluir comentários nos posts.

Visao geral

O layout que criamos não ficou uma maravilha, mas um designer com experiência em HTML e CSS pode facilmente customizar tudo, inclusive os formulários e Helpers
Repare que em todos os formulários o web2py criou os validadores de campos. Tente entrar com um e-mail incorreto, uma URL ilegal ou um campo obrigatório vazio para constatar.

O web2py também utiliza JQuery para criar DatePickers automaticamente para os campos do tipo ‘datetime’ e ‘date’:

Caso não esteja logado, o web2py exige que o usuário efetue login para poder cadastrar posts e categorias.

default/index

default/addcategory

default/addpost

default/showpost

Aqui você pode fazer o download da aplicação [ BLOG.w2p ], basta importar no admin do web2py.

Nas próximas partes deste tutorial, que serão publicadas em breve, iremos efetuar as seguintes alterações:

  1. Inclusão das funções de listagem de categorias (categoryposts)
  2. Formatação dos comentários com markdown
  3. Upload de imagens e documentos
  4. Busca via Ajax com auto complete
  5. Internacionalização
  6. Melhorias na autenticação de usuários
  7. Publicação de feed RSS
  8. Output em formatos XML e CSV
  9. Integração com o Twitter

Espero que este post tenha sido útil para quem está começando com o web2py.
Qualquer dúvida, elogio ou sugestão é só comentar!

Obrigado

[]’s

Referência: http://rochacbruno.com.br/blog/2010/06/desenvolvimento-com-web2py-tutorial-pt1/

Bruno Rocha

Mais artigos deste autor »

Desenvolvedor e consultor web. Possui mais de 9 anos de experiência em desenvolvimento web e já trabalhou com as linguagens C, PHP, ASP, C#.

Atualmente, ministra treinamentos, palestras e desenvolve soluções com Python para web com os frameworks Pylons e web2py.

Um dos colaboradores do http://web2pybrasil.appspot.com e as vezes escreve também no blog http://rochacbruno.com.br

Além dos projetos com Python, atua como desenvolvedor e coordenador de projetos (ScrumMaster) na empresa GENTE - http://servicogente.com.br

Contato:
http://twitter.com/rochacbruno / rochacbruno[gmail.com]


10 Comentários

Fernando Souza
1

Olá, gostei muito do tutorial, já estava na minha lista de tarefas pegar para entender e decidir qual framework utilizar e seu tutorial me ajudou a decidir na escolha pelo web2py.
Você sabe me dizer se é possivel rodar ele em um servidor que está atualmente hospedando PHP? e se consigo trocar informações com uma aplicação de backend(ERP) desenvolvida em VB.net ?
Obrigado pelo tutorial tão bem detalhado, agora entendi mesmo e vou em busca de mais infromações no site oficial
Fernando

Bruno Rocha
2

O web2py pode sim rodar no apache juntamente com sua app em PHP sem problemas basta criar um outro virtual host e apontar para a raiz do web2py, o apache deve estar configurado com mod_python, mod_proxy ou mod_wsgi, como mostram estes links: http://www.web2py.com/AlterEgo/default/show/136 ou http://docs.google.com/viewer?url=http://www.web2py.com/examples/static/lamp_ssl.pdf&pli=1
Para integrar com uma aplicação backend você pode utilizar SOAP / XML-RPC, isso pode ser feito com VB.net, C#, SQLServer ou qualquer outra tecnologia que implemente estes protocolos, dicas de como fazer neste link http://web2py.com/book/default/section/9/2
Qualquer dúvida entre em contato ou envie para o grupo web2py-users-br no google groups.
Obrigado por comentar!
{ }’s

van vegan
3

Ainda não conheço o web2py mas parece ser bem divertido e pythonico mesmo! Já foi pra lista de “coisas a estudar” e seu link direto pros favoritos. Quando testar dou meu parecer ! Valeu pela contribuição !

Jose
4

Se agrade enormemente la ayuda!!! Recién estoy aprendiendo a usar Web2py y me vino muy bien tu tutorial ya que me es más fácil leer el portugués que el inglés. XD Espero ansioso tus próximos tutoriales. Abrazo desde Buenos Aires!!!

aldo jose pereira
7

Bruno gostaria de saber o que aconteceu com a traduçao do livro do web2py ???
gostaria de colaborar.
como acesso seu github ??
obrigado

Felipe Costa
8

Bruno, bom dia.
Algumas imagens não estão aparecendo. Será q vc poderia disponibiliza-las?
Att
Felipe Costa

Rafael
9

Bruno, boa noite.
Os links dos arquivos estão quebrados, poderia disponibilizá-los novamente?
Obrigado.

Deixe seu comentário

Seu endereço de e-mail não será publicado. Campos com * são obrigatórios!