Passo-a-Passo web2py, 3º Passo

No 2º passo vimos como montar as tabelas utilizando a DAL (Database Abstraction Layer) do web2py e construímos as tabelas que iremos utilizar na nossa locadora. Para ver o código pronto, você pode clonar o projeto do github em: https://github.com/juliarizza/locadora. Cada passo está separado em um branch com o respectivo código e o branch master contém a aplicação completa até o momento.

Segue o índice para que você acompanhe também os outros passos:

Agora, vamos continuar!

Criação de Páginas

Para uma aplicação web conseguir realizar suas tarefas o usuário tem que ser capaz de interagir com ela e, para isso, ele precisa de locais acessíveis para passar informações. Ou seja, nosso usuário vai precisar de novas páginas para poder cadastrar filmes, novas locações, alterar estoque, etc.

No web2py, cada página da nossa aplicação é uma função Python nos controllers. Para entendermos como isso funciona, primeiro temos que entender como ler uma URL do web2py. Por exemplo, quando estamos na index da nossa aplicação: http://localhost:8000/locadora/default/index.

Vamos analisar parâmetro por parâmetro, do primeiro ao último:

  • locadora é o nome da nossa aplicação;
  • default é o nome do nosso controller;
  • index é o nome da nossa página/função (o web2py também as chama de actions);

Basicamente, o que temos aqui é que esta página é uma função chamada index que está dentro do controller default que pertence à aplicação locadora. Ou seja, se você abrir a pasta controllers dentro da pasta da sua aplicação, verá que lá existe um controller chamado default.py e que, neste arquivo, existe uma função chamada index.

O controller default.py vem por padrão nas novas aplicações e traz funções padrão também, como a index, a user (que lida com páginas de autenticação, cadastro, perfil, etc de usuários), a download, entre outras.

Como você pode ver neste arquivo, as páginas são todas definidas como uma função Python e tem a seguinte estrutura:

def index():
    return dict(message=T('Welcome to web2py!'))

A nome da função é o que irá definir o nome da página, ou seja, a função index() define uma página que será roteada para a URL /index.

Além disso, todas as funções retornam um dicionário. Esse dicionário é o que diz ao web2py o que deve ser exibido na sua página. Ou seja, quando passo um dicionário:

dict(message=T('Welcome to web2py!'))

Eu estou falando que a página index irá receber uma variável chamada mensagem que possui o valor ‘Welcome to web2py!’ para poder exibir isso na tela.

Obs: A função T() é utilizada para tradução de mensagens, mas no momento ela não será o foco do nosso estudo.

Criando nossas páginas

Vamos pensar na primeira coisa que precisamos fazer na nossa locadora.: para a locadora funcionar, precisamos ter filmes. Ou seja, precisamos conseguir cadastrar filmes. Então nossa primeira página tem que ser para cadastrar novos filmes.

Vá ao final do arquivo default.py e adicione:

def novo_filme():
    return dict()

Obs: você pode criar um novo controller caso prefira, mas lembre-se de alterar sempre a URL para acessar suas funções pelos respectivos controllers.

Feito isso, temos nossa nova página pronta. Acesse http://localhost:8000/locadora/default/novo_filme.

A página que você criou está em branco, a não ser pelos elementos que o web2py traz por padrão em suas views genéricas.

Views Genéricas

Nem sempre quando estamos trabalhando no desenvolvimento back-end de uma aplicação temos pronto desde o início todo o front-end do que queremos fazer e isso geralmente dificulta nossa vida. Por isso, o web2py traz por padrão um html gerado automaticamente quando estamos em ambiente de desenvolvimento, para que possamos visualizar aquilo que estamos fazendo e só por último nos preocuparmos com a parte “bonita” da aplicação.

O web2py utiliza o Bootstrap para gerar essas telas padrão. Por isso, quando você cria uma nova página, já é capaz de ver uma tela com informações básicas mesmo que você ainda não tenha criado todo o html.

View genérica da página Novo Filme

View genérica da página Novo Filme

Essas views genéricas que o web2py aparecem com os seguintes elementos:

  • Navbar e footer padrão do web2py;
  • Título da nova página, que é uma versão tratada do nome que você deu pra sua função;
  • Menu de botões com opções para visualizar informações sobre a página atual. Iremos entender melhor como interpretar estes dados que os botões trazem no futuro, no momento eles não são importantes.

Agora, sempre que você enviar uma informação para a sua página utilizando a partir do dicionário retornado pela função, essas informações devem ser apresentadas na página. Faça um teste escrevendo:

def novo_filme():
    return dict(mensagem="Cadastre novos vídeos!")

Construindo formulários

Tendo nossa página criada, precisamos fazer com que ela realmente funcione para algo. Agora nossa página de novos filmes tem que realmente adicionar novos filmes.

Vamos adicionar o seguinte código à nossa função novo_filme():

def novo_filme():
    form = SQLFORM(Filmes)
    if form.process().accepted:
        session.flash = 'Novo vídeo cadastrado: %s' % form.vars.titulo
        redirect(URL('novo_filme'))
    elif form.errors:
        response.flash = 'Erros no formulário!'
    else:
        response.flash = 'Preencha o formulário!'
    return dict(form=form)

Aqui temos várias coisas novas e, mesmo que o código seja pouco, há muito o que se aprender deste bloco. Então, vamos analisar com calma:

form = SQLFORM(Filmes)

SQLFORM é mais um construtor do web2py. É quase como mágica: nós informamos para ele qual tabela que vamos usar para inserir registros no banco de dados e ele gera um formulário a partir dos campos dessa tabela. Ou seja, se na nossa tabela de filmes nós temos um campo chamado titulo, agora temos um formulário com um campo titulo também.

Lembre-se que Filmes é a variável que está guardando a tabela filmes que criamos no passo anterior!

Formulário para adicionar novo filme

Formulário para adicionar novo filme

Depois de criar nosso formulário, fizemos o seguinte:

if form.process().accepted:
    '''código'''
elif form.errors:
    '''código'''
else:
    '''código'''

O que estamos fazendo é perguntar: o formulário foi processado sem errors? Se a resposta for sim, o código que está no nosso if será processado. Se ocorrer algum erro, o código do elif será processado. Se nada acontecer, ou seja, se não houver nenhuma submissão do formulário e nenhum erro, o código do else é processado.

Agora, o que temos dentro do if, do elif e do else?

if form.process().accepted:
    session.flash = 'Novo vídeo cadastrado: %s' % form.vars.titulo
    redirect(URL('novo_filme'))

O método process() do nosso formulário já verifica se tudo ocorreu sem errors e insere o registro no banco de dados, então não precisamos fazer nenhum tipo de inserção. Resta-nos apenas falar para o usuário que o filme foi cadastrado, para ele saber que ocorreu tudo como planejado.

É isso que fazemos com o session.flash. Ambos session.flash e response.flash recebem uma mensagem que deve ser exibida para o usuário na forma de notificação. A diferença entre eles é que o response.flash é exibido na mesma página em que foi chamado, já o session.flash só será exibido depois de uma redireção ou mudança de página.

Faça um teste e adicione um filme. Você verá que tivemos um problema, pois a mensagem do nosso session.flash não será exibida, apenas o response.flash do else. Isso acontece porque o session.flash depois da redireção é “convertido” em response.flash e, quando a página recarrega, o response.flash do else substitui o do if. Para evirtarmos isso, podemos adicionar essa linha ao else:

else:
    if not response.flash:
         response.flash = 'Preencha o formulário!'

No nosso session.flash do if informamos também um valor: form.vars.titulo. Isso é porque depois de processado o formulário, todos os valores recebidos pela submissão podem ser acessados no dicionário form.vars. Assim, cada campo do formulário pode ser acessado por form.vars.nome_do_campo e o valor submetido pode ser visto.

Após essas mensagens, chamamos a função redirect() que faz uma redireção para a URL informada. Para montarmos a URL da nossa página, utilizamos a função URL() do web2py. Essa função recebe os seguintes parâmetros:

URL(a=’application’, c=’controller’, f=’action’, args=[], vars={}, host=False, scheme=’http’)

Cada parâmetro especifica:

  • a: a aplicação na qual estamos procurando a página;
  • c: o controller em que esta página deve estar;
  • f: o nome da página para qual queremos ir;
  • args: argumentos da página;
  • vars: variáveis da página;
  • host: se queremos montar uma URL absoluta (com o domínio) ou parcial (sem o domínio). Por exemplo: http://meusite.com/index é uma URL  absoluta, e /index é uma URL parcial.
  • scheme: o protocolo. Por exemplo, http, https, etc.

Para aqueles que não estão acostumados com esse conceito, variáveis e argumentos são valores que podemos passar via URL. Por exemplo:

http://meusite.com/index/arg1/arg2?var1=2&var2=3

Os argumentos são aqueles separados do nome da página por / e são um valor apenas. Já as variáveis são aquelas separadas por ? e são um par chave-valor, onde o que vem antes do sinal = é a chave e o que vem depois do sinal = é o valor. Várias variáveis são separadas por &.

No momento, não temos interesse em passar esses valores pela URL. No futuro, veremos como resgatá-los. Por enquanto, vamos utilizar a função URL() apenas informando o nome da página para qual queremos ir, pois o nome da aplicação e do controller são, por padrão, os que nós já estamos utilizando.

Feito isso, terminamos nossa página! Tente adicionar um novo filme.

Validação

Espero que tenha reparado que tivemos alguns problemas. Conseguimos cadastrar filmes com campos que não deviam ficar em branco, pois isto pode provocar um caos no nosso estoque. Além disso, podemos cadastrar o mesmo filme mais de uma vez.

Para evitarmos que esse tipo de problema ocorra, devemos adicionar validadores nas nossas tabelas do banco de dados. Para isso, irei criar um novo model chamado valitadors.py e adicionar as seguintes linhas:

# -*- coding: utf-8 -*-

## Validadores de Filmes
Filmes.titulo.requires = [IS_NOT_EMPTY(),
IS_NOT_IN_DB(db, 'filmes.titulo')]
Filmes.generos.requires = IS_NOT_EMPTY()
Filmes.diretor.requires = IS_NOT_EMPTY()
Filmes.capa.requires = IS_EMPTY_OR(IS_IMAGE())

## Validadores de Estoque
ItemsEstoque.filme.requires = IS_IN_DB(db, 'filmes.id', '%(titulo)s', _and=IS_NOT_IN_DB(db, 'items_estoque.filme'))

## Validadores de Locação
Locacao.filmes.requires = IS_IN_DB(db, 'filmes.id', '%(titulo)s')
Locacao.cliente.requires = IS_IN_DB(db, 'auth_user.id', '%(first_name)s %(last_name)s')
Locacao.data_locacao.requires = IS_DATETIME(format='%d/%m/%Y')
Locacao.data_devolucao.requires = IS_DATETIME(format='%d/%m/%Y')

Validadores são as estruturas que o web2py traz para garantir que algo ocorra na submissão de um registro, para que ele não tenha problemas em ser gravado no banco de dados. Você pode consultar uma lista deles na documentação do web2py.

Vamos entender apenas os validadores que utilizamos e como estamos utilizando eles:

Para associar um tipo de validador a um campo das nossas tabelas, nós especificamos o campo e falamos que ele requer que algo ocorra. Por exemplo: Filme.titulo.requires. Depois, precisamos apenas falar o que deve ocorrer.

Obs: Um campo pode ter mais de um validador, e estes devem ser passados dentro de uma lista.

  • IS_NOT_EMPTY(): requer que o campo esteja preenchido com algum tipo de valor;
  • IS_NOT_IN_DB(banco, campo): requer que o valor já não exista no campo especificado do banco especificado;
  • IS_EMPTY_OR(validador): o campo pode ficar vazio, mas , caso não fique, o valor deve ser válido para o validador secundário especificado;
  • IS_IMAGE(): o tipo de arquivo do upload deve ser um tipo de imagem;
  • IS_IN_DB(banco, campo, formato): o valor deve ser um registro existente no banco de dados especificado e será salvo pelo campo especificado. O formato é apenas para exibição. Por exemplo: estou fazendo referência a um usuário já existente a partir do seu id, mas no formulário quero que aparece o seu nome, por isso meu valor de campo será o id e meu valor de formato será o nome;
    Este validador irá gerar um campo do tipo select no seu formulário. Caso você não queira um campo do tipo select, deverá colocar o validador dentro de uma lista, mesmo que ele seja o único elemento.
    No caso de ele não ser o único validador dentro da lista e você ainda quiser que ele seja transformado em um campo select, você deve passar os outros validadores no parâmetro _and deste validador.
  • IS_DATETIME(formato): requer que o valor seja uma data e/ou hora no formato especificado;

Todos os validadores também tem um parâmetro chamado error_message que recebe a mensagem que deve aparecer quando o usuário inserir um valor que não bate com o validador. Por exemplo, quando o usuário deixa em branco um campo com validador IS_NOT_EMPTY deve aparecer uma mensagem avisando que o campo não pode ser deixado em branco.

O Appadmin

Você já deve ter reparado nos seus controllers que existe um chamado appadmin. Ele existe como um complemento da interface administrativa e é muito simples utilizá-lo. Acesse http://localhost:8000/locadora/appadmin.

O appadmin

O appadmin

No appadmin você pode ver todas as tabelas do seu banco de dados, visualizar os registros existentes e fazer operações de CRUD. É tão simples quanto parece: você entra na tabela desejada, visualiza os registros existentes, pode criar novos, pode editar aqueles que vê, pode excluí-los.

Utilize o appadmin para monitorar e controlar seus registros. Nossos usuários farão uso de operações semelhantes, mas tenha em mente o seguinte: para acessar o appadmin, você também precisa da senha da interface administrativa e nós não podemos liberar essa senha para todos utilizarem, tampouco permitir que mantenham controle sobre todos os registros, inclusive aqueles que devem ser restritos.

Por isso, iremos aos poucos aprender a desenvolver páginas para realizar as operações CRUD e controlar o que pode ser feito por usuários no próximo passo.

Conclusão

Neste passo acompanhamos como podemos criar novas páginas na nossa aplicação, como podemos criar formulário e inserir registros no banco de dados e como validar esses dados do formulário.

Agora, a sua missão é continuar com o que aprendemos criando a formulários para dar entrada de itens no estoque e para registrar locações. Depois de terminado, pode comparar seu código ao que está no repositório do github para o passo 3.

Nos vemos no próximo passo!

Anúncios

17 comentários sobre “Passo-a-Passo web2py, 3º Passo

  1. Carlos Eduardo Gloria disse:

    Muito bom, “fessora”! Eu não sabia da importância de um framework, muitas coisas só sabemos de ouvir falar…Gostaria tbm de reforçar a importância dos hungouts, e foi no do Eric Hideki, que eu ví sua palestra falando sobre web2py. para mim o desenvolvimento se limitava ao meu pc, com alguns programinhas locais em Python, nem pensar em me aventurar pala web, servidor para mim era um mundo monstruoso.
    Meus sinceros agradecimentos e respeitos pela sua bondade de nos presentear com seu tempo e sua atenção, não há o que flr sobre sua didática, não penso que nasceu só pra programar vc tem é que dar aulas de programação o Brasil precisa muito de grandes mestres como voce.

    Curtir

    • Júlia Rizza disse:

      Muito obrigada, Carlos!

      Admiro muito sua história também e é muito bom poder ver você avançando neste mundo com possibilidades infinitas que é a programação.

      Agradeço muito por suas palavras e desejo a você muito sucesso nessa jornada!

      Curtido por 1 pessoa

  2. Damião Souza disse:

    Professora, estou estudando a linguagem Python e o framework web2py, e esse material disponibilizado gentilmente por você está sendo nota 10 pra o meu aprendizado. Muito obrigado por sua dedicação e atenção a todos. Parabéns pela excelente didática e objetividade na exposição do material.

    Curtir

  3. Joilson disse:

    Julia!
    Parabéns pela iniciativa, vai ajudar bastante.

    Pode me dizer o porque desse erro:
    Internal error

    Ticket issued: locadora/127.0.0.1.2016-02-06.11-09-38.49ee22d9-d16d-4c5c-a46d-4a6fdd9a0f43

    abraços

    Curtir

    • Júlia Rizza disse:

      Rapaz, apenas pelo ID do ticket eu não sou capaz de acessá-lo e verificar o que está ocorrendo, pois não tenho acesso ao seu sistema. Para saber mais sobre o erro, quando esse ticket aparecer você deve clicar no ID, que deve ser um link, e acessar o conteúdo do erro 🙂

      Curtir

      • Joilson disse:

        Desculpe Julia, foi um post tipicamente nub.

        O erro deu-se devido a falhas no código

        abraços
        P.S – Estou seguindo todos os passos e diga-se de passagem, gostando bastante

        Curtir

  4. rise disse:

    Como fazer das palavras de CARLOS EDUARDO GLORIA e DAMIÃO SOUZA as minhas não é o suficiente, deixo aqui meus sinceros agradecimentos. Pela didática e pelo conhecimento compartilhado. Somente até o passo 2 já expandiu minha mente em alguns anos luz, eu que só usava console python como canculadora e para scripts toscos feitos com remendos da net que só executava algumas rotinas simples… sinto que vou conseguir de fato resolver/criar algumas coisas que sempre quis sem saber como. Muito obrigado.

    Curtir

  5. Thiago Silva disse:

    Olá Júlia.
    Estou seguindo a risca tudo que foi ensinado aqui, porém, chegou nessa terceira lição, acontece o seguinte erro:
    ‘dict_values’ object does not support indexing

    Pesquisando no Google eu percebi que está relacionado com a versão do python que estou usando, mas não encontrei a solução.

    Será que você poderia me ajudar?

    Curtir

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s