Chapter 3: Visão Geral
Visão geral
Comece
O web2py vem em pacotes binários para Windows e Mac OS X. Eles incluem o interpretador Python para que você não precise instalá-lo previamente. Há também uma versão do código-fonte que roda no Windows, Mac, Linux e outros sistemas Unix. O pacote do código-fonte pressupõe que o Python já esteja instalado no computador.
web2py não requer instalação. Para começar, descompacte o arquivo zip baixado para o seu sistema operacional específico e execute o arquivo correspondente. web2py
Arquivo.
No Unix e Linux (distribuição de fontes), execute:
python web2py.py
No OS X (distribuição binária), execute:
open web2py.app
No Windows (distribuição web2py binária), execute:
web2py.exe
No Windows (fonte de distribuição web2py), execute:
c:/Python27/python.exe web2py.py
Atenção, para rodar o web2py no Windows a partir do código-fonte, você deve instalar as primeiras extensões win32 do Mark Hammond de
http://sourceforge.net/projects/pywin32/
.
O programa web2py aceita várias opções de linha de comando que serão discutidas posteriormente.
Por padrão, na inicialização, o web2py exibe uma janela de inicialização e exibe um widget de GUI que solicita a escolha de uma senha de administrador única, o endereço IP da interface de rede a ser usada para o servidor da Web e um número de porta para atender solicitações. Por padrão, o web2py executa seu servidor da web em 127.0.0.1:8000 (porta 8000 no host local), mas você pode executá-lo em qualquer endereço IP e porta disponíveis. Você pode consultar o endereço IP da sua interface de rede abrindo uma linha de comando e digitando ipconfig
no Windows ou ifconfig
no OS X e no Linux. A partir de agora, assumimos que o web2py está sendo executado no host local (127.0.0.1:8000). Use 0.0.0.0:80 para executar o web2py publicamente em qualquer uma das suas interfaces de rede.
Se você não fornecer uma senha de administrador, a interface de administração será desativada. Essa é uma medida de segurança para evitar a exposição pública da interface do administrador.
A interface administrativa, admin, só é acessível a partir do localhost, a menos que você execute web2py atrás do Apache com o mod_proxy. E se admin detecta um proxy, o cookie da sessão é configurado para proteger e admin o login não funciona a menos que a comunicação entre o cliente e o proxy passe por HTTPS; esta é uma medida de segurança. Todas as comunicações entre o cliente e admin deve sempre ser local ou criptografado; caso contrário, um invasor poderá executar um ataque do tipo man-in-the-middle ou um ataque de repetição e executar um código arbitrário no servidor.
Após a definição da senha de administração, o web2py inicia o navegador da web na página:
http://127.0.0.1:8000/
Se o computador não tiver um navegador padrão, abra um navegador da Web e digite o URL.
Clicar em "interface administrativa" leva você para a página de login da interface de administração.
A senha do administrador é a senha que você escolheu na inicialização. Observe que há apenas um administrador e, portanto, apenas uma senha de administrador. Por razões de segurança, o desenvolvedor é solicitado a escolher uma nova senha toda vez que o web2py for iniciado, a menos que a opção <recycle> seja especificada. Isso é diferente do mecanismo de autenticação em aplicativos web2py.
Depois que o administrador fizer login no web2py, o navegador será redirecionado para a página "site".
Esta página lista todos os aplicativos web2py instalados e permite que o administrador os gerencie. O web2py vem com três aplicativos:
- A admin aplicação, o que você está usando agora.
- A exemplos aplicativo, com a documentação interativa on-line e uma réplica do site oficial web2py.
- UMA
welcome
aplicação. Este é o template básico para qualquer outro aplicativo web2py. É referido como o aplicativo de scaffold . Esta é também a aplicação que acolhe um usuário na inicialização.
Aplicativos web2py prontos para uso são chamados de web2py appliances
. Você pode baixar muitos aparelhos disponíveis gratuitamente [appliances] . Os usuários do web2py são encorajados a enviar novos aparelhos, seja em código aberto ou fechado (compilado e empacotado).
De admin página do aplicativo site , você pode executar as seguintes operações:
- instalar um aplicativo preenchendo o formulário no canto inferior direito da página. Dê um nome ao aplicativo, selecione o arquivo que contém um aplicativo empacotado ou o URL onde o aplicativo está localizado e clique em "enviar".
- Desinstalar uma aplicação clicando no botão correspondente. Existe uma página de confirmação.
- crio um novo aplicativo, escolhendo um nome e clicando em "criar".
- pacote uma aplicação para distribuição clicando no botão correspondente. Um aplicativo baixado é um arquivo tar contendo tudo, incluindo o banco de dados. Você não deve descompactar este arquivo; ele é automaticamente descompactado pelo web2py quando instalado admin.
- Limpar arquivos temporários de um aplicativo, como sessões, erros e arquivos de cache.
- habilitar desabilitar cada aplicação. Quando um aplicativo está desativado, ele não pode ser chamado remotamente, mas não está desabilitado no host local. Isso significa que aplicativos desativados ainda podem ser acessados por trás de um proxy. Um aplicativo é desativado, criando um arquivo chamado "DISABLED" na pasta do aplicativo. Os usuários que tentarem acessar um aplicativo desabilitado receberão um erro 503 HTTP. Você pode usar routes_onerror para personalizar a página de erro.
- EDITAR uma aplicação.
Quando você cria um novo aplicativo usando admin, ele começa como um clone do aplicativo de boas-vindas scaffolding com um "models/db.py" que cria um banco de dados SQLite, conecta-se a ele, instancia Auth, Crud e Service e os configura. Ele também fornece um "controller/default.py" que expõe ações "index", "download", "user" para gerenciamento de usuários e "call" para serviços. A seguir, supomos que esses arquivos foram removidos; estaremos criando aplicativos a partir do zero.
web2py também vem com um bruxo, descrito mais adiante neste capítulo, que pode escrever um código alternativo de scaffolding baseado em layouts e plug-ins disponíveis na web e com base na descrição de alto nível dos modelos.
Exemplos simples
Diga olá
Aqui, como exemplo, criamos um aplicativo da web simples que exibe a mensagem "Hello from MyApp" para o usuário. Vamos chamar esta aplicação "myapp". Também adicionaremos um contador que conta quantas vezes o mesmo usuário visita a página.
Você pode criar um novo aplicativo simplesmente digitando seu nome no formulário no canto superior direito do local página em admin.
Depois de pressionar [criar], o aplicativo é criado como uma cópia do aplicativo de boas-vindas incorporado.
Para executar o novo aplicativo, visite:
http://127.0.0.1:8000/myapp
Agora você tem uma cópia do aplicativo de boas vindas.
Para editar um aplicativo, clique no botão editar do aplicativo recém-criado.
o editar página informa o que está dentro do aplicativo. Cada aplicativo web2py consiste em determinados arquivos, a maioria dos quais se enquadra em uma das seis categorias:
- modelos: descreve a representação de dados.
- controladores: descreve a lógica e o fluxo de trabalho da aplicação.
- visualizações: descreve a apresentação de dados.
- línguas: descreve como traduzir a apresentação do aplicativo para outros idiomas.
- módulos: Módulos do Python que pertencem ao aplicativo.
- arquivos estáticos: imagens estáticas, arquivos CSS [css-w,css-o,css-school] Arquivos JavaScript [js-w,js-b] etc.
- plugins: grupos de arquivos projetados para trabalhar juntos.
Tudo está bem organizado seguindo o padrão de design Model-View-Controller. Cada seção na página editar corresponde a uma subpasta na pasta do aplicativo.
Observe que clicar nos títulos das seções alternará seu conteúdo. Nomes de pastas em arquivos estáticos também são reduzidos.
Cada arquivo listado na seção corresponde a um arquivo fisicamente localizado na subpasta. Qualquer operação executada em um arquivo via admin interface (criar, editar, excluir) pode ser executada diretamente do shell usando o seu editor favorito.
O aplicativo contém outros tipos de arquivos (banco de dados, arquivos de sessão, arquivos de erro, etc.), mas eles não estão listados na página editar porque eles não são criados ou modificados pelo administrador; eles são criados e modificados pelo próprio aplicativo.
Os controladores contêm a lógica e o fluxo de trabalho do aplicativo. Cada URL é mapeada em uma chamada para uma das funções nos controladores (ações). Existem dois controladores padrão: "appadmin.py" e "default.py". appadmin fornece a interface administrativa do banco de dados; nós não precisamos disso agora. "default.py" é o controlador que você precisa editar, aquele que é chamado por padrão quando nenhum controlador é especificado na URL. Edite a função "index" da seguinte maneira:
def index():
return "Hello from MyApp"
Aqui está o editor on-line:
Salve e volte para a página editar . Clique no link do índice para visitar a página recém-criada.
Quando você visita o URL
http://127.0.0.1:8000/myapp/default/index
a ação de índice no controlador padrão do aplicativo myapp é chamada. Ele retorna uma string que o navegador exibe para nós. Deve ficar assim:
Agora, edite a função "index" da seguinte maneira:
def index():
return dict(message="Hello from MyApp")
Também na página editar , edite a visualização "default/index.html" (o arquivo de visualização associado à ação) e substitua completamente o conteúdo existente desse arquivo pelo seguinte:
<html>
<head></head>
<body>
<h1>{{=message}}</h1>
</body>
</html>
Agora a ação retorna um dicionário que define um message
. Quando uma ação retorna um dicionário, o web2py procura uma exibição com o nome
[controller]/[function].[extension]
e executa isso. Aqui [extension]
é a extensão solicitada. Se nenhuma extensão for especificada, o padrão será "html", e é isso que vamos assumir aqui. Sob essa suposição, a visualização é um arquivo HTML que incorpora o código Python usando tags {{}} especiais. Em particular, no exemplo, o {{=message}}
instrui o web2py a substituir o código marcado pelo valor do message
retornado pela ação. Notar que message
aqui não é uma palavra-chave web2py, mas é definida na ação. Até agora não usamos palavras-chave web2py.
Se o web2py não encontrar a visualização solicitada, ele usa a visualização "generic.html" que acompanha cada aplicativo.
Mac MailGoogle MapsjsonpSe uma extensão diferente de "html" for especificada ("json" por exemplo) e o arquivo de visualização "[controller]/[function] .json" não for encontrado, o web2py procurará pela exibição "generic.json". O web2py vem com o generic.html, o generic.json, o generic.jsonp, o generic.xml, o generic.rss, o generic.ics (para o Mac Mail Calendar), o generic.map (para incorporar o Google Maps) e o generic.pdf (com base no fpdf). Essas visualizações genéricas podem ser modificadas para cada aplicativo individualmente e visualizações adicionais podem ser adicionadas facilmente.
As visualizações genéricas são uma ferramenta de desenvolvimento. Na produção, toda ação deve ter sua própria visão. Na verdade, por padrão, as visualizações genéricas são ativadas apenas no host local.
Você também pode especificar uma exibição com
response.view = 'default/something.html'
Leia mais sobre este tópico no Capítulo 10.
Se você voltar para "EDIT" e clicar no índice, você verá a seguinte página HTML:
Barra de Depuração
Para fins de depuração, você pode inserir
{{=response.toolbar()}}
ao código em uma view e ele mostrará algumas informações úteis, incluindo os objetos request, response e session, e listará todas as consultas do banco de dados com seu tempo.
Vamos contar
Vamos agora adicionar um contador a essa página que contará quantas vezes o mesmo visitante exibirá a página.
O web2py rastreia de maneira automática e transparente os visitantes usando sessões e cookies. Para cada novo visitante, ele cria uma sessão e atribui um "session_id" exclusivo. A sessão é um contêiner para variáveis que são armazenadas no lado do servidor. O ID exclusivo é enviado para o navegador por meio de um cookie. Quando o visitante solicita outra página do mesmo aplicativo, o navegador envia o cookie de volta, ele é recuperado pelo web2py e a sessão correspondente é restaurada.
Para usar a sessão, modifique o controlador padrão:
def index():
if not session.counter:
session.counter = 1
else:
session.counter += 1
return dict(message="Hello from MyApp", counter=session.counter)
Notar que counter
não é uma palavra-chave web2py, mas session
é. Estamos pedindo ao web2py para verificar se existe uma variável de contador na sessão e, se não, para criar um e configurá-lo para 1. Se o contador estiver lá, pedimos ao web2py para aumentar o contador em 1. Finalmente, passamos o valor do contador para a vista.
Uma maneira mais compacta de codificar a mesma função é esta:
def index():
session.counter = (session.counter or 0) + 1
return dict(message="Hello from MyApp", counter=session.counter)
Agora modifique a visão para adicionar uma linha que exibe o valor do contador:
<html>
<head></head>
<body>
<h1>{{=message}}</h1>
<h2>Number of visits: {{=counter}}</h2>
</body>
</html>
Quando você visitar a página de índice novamente (e novamente), deverá obter a seguinte página HTML:
O contador está associado a cada visitante e é incrementado sempre que o visitante recarrega a página. Diferentes visitantes veem contadores diferentes.
Diga meu nome form request.vars
Agora crie duas páginas (primeira e segunda), onde a primeira página cria um formulário, pergunta o nome do visitante e redireciona para a segunda página, que recebe o visitante pelo nome.
Escreva as ações correspondentes no controlador padrão:
def first():
return dict()
def second():
return dict()
Em seguida, crie uma visualização "default/first.html" para a primeira ação. e entre:
{{extend 'layout.html'}}
<h1>What is your name?</h1>
<form action="{{=URL('second')}}">
<input name="visitor_name" />
<input type="submit" />
</form>
Por fim, crie uma visualização "default/second.html" para a segunda ação:
{{extend 'layout.html'}}
<h1>Hello {{=request.vars.visitor_name}}</h1>
Em ambas as visualizações, ampliamos a visualização "layout.html" básica que vem com o web2py. A visualização do layout mantém a aparência das duas páginas consistentes. O arquivo de layout pode ser editado e substituído facilmente, pois contém principalmente código HTML.
Se você agora visitar a primeira página, digite seu nome:
e envie o formulário, você receberá uma saudação:
Postbacks redirect URL postback
O mecanismo de submissão de formulários que usamos antes é muito comum, mas não é uma boa prática de programação. Todas as entradas devem ser validadas e, no exemplo acima, a carga de validação recai sobre a segunda ação. Assim, a ação que realiza a validação é diferente da ação que gerou o formulário. Isso tende a causar redundância no código.
Um padrão melhor para o envio de formulários é enviar formulários para a mesma ação que os gerou, em nosso exemplo, o "primeiro". A "primeira" ação deve receber as variáveis, processá-las, armazená-las no lado do servidor e redirecionar o visitante para a "segunda" página, que recupera as variáveis. Esse mecanismo é chamado de postback .
Modifique o controlador padrão para implementar a auto-apresentação:
def first():
if request.vars.visitor_name:
session.visitor_name = request.vars.visitor_name
redirect(URL('second'))
return dict()
def second():
return dict()
Em seguida, modifique a visualização "default/first.html":
{{extend 'layout.html'}}
What is your name?
<form>
<input name="visitor_name" />
<input type="submit" />
</form>
e a visualização "default/second.html" precisa recuperar os dados do session
em vez de do request.vars
:
{{extend 'layout.html'}}
<h1>Hello {{=session.visitor_name or "anonymous"}}</h1>
Do ponto de vista do visitante, a auto-apresentação se comporta exatamente da mesma forma que a implementação anterior. Nós não adicionamos validação ainda, mas agora está claro que a validação deve ser realizada pela primeira ação.
Essa abordagem é melhor também porque o nome do visitante permanece na sessão e pode ser acessado por todas as ações e visualizações no aplicativo sem ter que ser passado explicitamente.
Observe que, se a "segunda" ação for chamada antes de um nome de visitante ser definido, ela exibirá "Olá anônimo" porque session.visitor_name
retorna None
. Alternativamente, poderíamos ter adicionado o seguinte código no controlador (dentro do second
função):
if request.function != 'first' and not session.visitor_name:
redirect(URL('first'))
Este é um mecanismo ad hoc que você pode usar para impor a autorização em controladores, embora veja o Capítulo 9 para um método mais poderoso.
Com o web2py, podemos avançar um pouco e solicitar que o web2py gere o formulário para nós, incluindo a validação. O web2py fornece ajudantes (FORM, INPUT, TEXTAREA e SELECT/OPTION) com os mesmos nomes das tags HTML equivalentes. Eles podem ser usados para construir formulários no controlador ou na exibição.
Por exemplo, aqui está uma maneira possível de reescrever a primeira ação:
def first():
form = FORM(INPUT(_name='visitor_name', requires=IS_NOT_EMPTY()),
INPUT(_type='submit'))
if form.process().accepted:
session.visitor_name = form.vars.visitor_name
redirect(URL('second'))
return dict(form=form)
onde estamos dizendo que a tag FORM contém duas tags INPUT. Os atributos das tags de entrada são especificados pelos argumentos nomeados começando com sublinhado. o requires
O argumento não é um atributo de tag (porque ele não inicia por sublinhado), mas define um validador para o valor de visitor_name.
Aqui está outra maneira melhor de criar o mesmo formulário:
def first():
form = SQLFORM.factory(Field('visitor_name',
label='what is your name?',
requires=IS_NOT_EMPTY()))
if form.process().accepted:
session.visitor_name = form.vars.visitor_name
redirect(URL('second'))
return dict(form=form)
o form
Objeto pode ser facilmente serializado em HTML, incorporando-o na exibição "default/first.html".
{{extend 'layout.html'}}
{{=form}}
o form.process()
O método aplica os validadores e retorna o próprio formulário. o form.accepted
variável é definida como Verdadeiro se o formulário foi processado e passou pela validação. Se o formulário enviado automaticamente passar pela validação, ele armazenará as variáveis na sessão e redirecionará como antes. Se o formulário não passar na validação, as mensagens de erro serão inseridas no formulário e mostradas ao usuário, conforme abaixo:
Na próxima seção, mostraremos como os formulários podem ser gerados automaticamente a partir de um modelo.
Em todos os nossos exemplos, usamos a sessão para passar o nome do usuário da primeira ação para a segunda. Poderíamos ter usado um mecanismo diferente e passado dados como parte de um URL de redirecionamento:
def first():
form = SQLFORM.factory(Field('visitor_name', requires=IS_NOT_EMPTY()))
if form.process().accepted:
name = form.vars.visitor_name
redirect(URL('second', vars=dict(name=name)))
return dict(form=form)
def second():
name = request.vars.name or redirect(URL('first'))
return dict(name=name)
Em seguida, modifique a visualização "default/second.html":
{{extend 'layout.html'}}
<h1>Hello {{=name}}</h1>
Lembre-se de que, em geral, não é uma boa idéia passar dados de uma ação para outra usando o URL. Isso torna mais difícil proteger o aplicativo. É mais seguro armazenar os dados em uma sessão.
Internacionalização
Seu código provavelmente incluirá strings codificadas, como "Qual é seu nome?". Você deve poder personalizar strings sem editar o código e, em particular, inserir traduções para essas strings em diferentes idiomas. Dessa forma, se um visitante tiver a preferência de idioma do navegador definida como "Italiano", o web2py usará a tradução italiana para as cadeias, se disponível. Esse recurso do web2py é chamado de "internacionalização" e é descrito com mais detalhes no próximo capítulo.
Aqui nós apenas observamos que para usar este recurso você deve marcar strings que precisam de tradução. Isso é feito envolvendo uma string entre aspas no código, como
"What is your name?"
com o T
operador:
T("What is your name?")
Você também pode marcar para cadeias de traduções codificadas em exibições. Por exemplo
<h1>What is your name?</h1>
torna-se
<h1>{{=T("What is your name?")}}</h1>
É uma boa prática fazer isso para cada string no código (rótulos de campo, mensagens flash, etc.), exceto para tabelas e nomes de campos.
Depois que as strings são identificadas e marcadas, o web2py cuida de quase todo o resto. A interface administrativa também fornece uma página onde você pode traduzir cada string nos idiomas que deseja suportar.
O web2py inclui um poderoso mecanismo de pluralização, descrito no próximo capítulo. Está integrado tanto com o mecanismo de internacionalização quanto com o renderizador markmin.
Um blog de imagens
Aqui, como outro exemplo, desejamos criar um aplicativo da Web que permita ao administrador postar imagens e dar a elas um nome, e permitir que os visitantes do site visualizem as imagens nomeadas e enviem comentários (postagens).
Como antes, do local página em admin, crie um novo aplicativo chamado images
e navegue até a página editar :
Começamos criando um modelo, uma representação dos dados persistentes no aplicativo (as imagens a serem carregadas, seus nomes e os comentários). Primeiro, você precisa criar/editar um arquivo de modelo que, por falta de imaginação, chamamos de "db.py". Assumimos que o código abaixo irá substituir qualquer código existente em "db.py". Modelos e controladores devem ter um .py
extensão desde que eles são código Python. Se a extensão não for fornecida, ela será anexada pelo web2py. As visualizações têm uma .html
extensão, pois eles contêm principalmente código HTML.
Remova o modelo "menu.py".
Edite o arquivo "db.py" clicando no botão "editar" correspondente:
e digite o seguinte:
db = DAL("sqlite://storage.sqlite")
db.define_table('image',
Field('title', unique=True),
Field('file', 'upload'),
format = '%(title)s')
db.define_table('post',
Field('image_id', 'reference image'),
Field('author'),
Field('email'),
Field('body', 'text'))
db.image.title.requires = IS_NOT_IN_DB(db, db.image.title)
db.post.image_id.requires = IS_IN_DB(db, db.image.id, '%(title)s')
db.post.author.requires = IS_NOT_EMPTY()
db.post.email.requires = IS_EMAIL()
db.post.body.requires = IS_NOT_EMPTY()
db.post.image_id.writable = db.post.image_id.readable = False
Vamos analisar isso linha por linha.
A linha 1 define uma variável global chamada db
que representa a conexão com o banco de dados. Neste caso, é uma conexão com um banco de dados SQLite armazenado no arquivo "applications/images/databases/storage.sqlite". Ao usar o SQLite, se o arquivo do banco de dados não existir, ele será criado. Você pode alterar o nome do arquivo, bem como o nome da variável global db
, mas é conveniente dar-lhes o mesmo nome, para facilitar o relembrar.
As linhas 3-6 definem uma tabela "imagem". define_table
é um método do db
objeto. O primeiro argumento, "imagem", é o nome da tabela que estamos definindo. Os outros argumentos são os campos pertencentes a essa tabela. Esta tabela tem um campo chamado "title", um campo chamado "file", e um campo chamado "id" que serve como a chave primária da tabela ("id" não é explicitamente declarado porque todas as tabelas possuem um campo id por padrão). O campo "title" é uma string, e o campo "file" é do tipo "upload". "upload" é um tipo especial de campo usado pelo web2py Data Abstraction Layer (DAL) para armazenar os nomes dos arquivos enviados. O web2py sabe como fazer upload de arquivos (via streaming, se forem grandes), renomeá-los com segurança e armazená-los.
Quando uma tabela é definida, o web2py realiza uma das várias ações possíveis:
- se a tabela não existir, a tabela é criada;
- se a tabela existe e não corresponde à definição, a tabela é alterada de acordo, e se um campo tem um tipo diferente, o web2py tenta converter seu conteúdo;
- se a tabela existe e corresponde à definição, web2py não faz nada.
Esse comportamento é chamado de "migração". No web2py as migrações são automáticas, mas podem ser desativadas para cada tabela passando migrate=False
como o último argumento de define_table
.
A linha 6 define uma string de formato para a tabela. Ele determina como um registro deve ser representado como uma string. Observe que o format
O argumento também pode ser uma função que recebe um registro e retorna uma string. Por exemplo:
format=lambda row: row.title
As linhas 8-12 definem outra tabela chamada "post". Uma postagem tem um "autor", um "e-mail" (pretendemos armazenar o endereço de e-mail do autor da postagem), um "corpo" do tipo "texto" (pretendemos usá-lo para armazenar o comentário real postado por o autor), e um campo "image_id" do tipo de referência que aponta para db.image
através do campo "id".
Na linha 14, db.image.title
representa o campo "título" da tabela "imagem". O atributo requires
permite que você defina requisitos/restrições que serão impostas pelos formulários web2py. Aqui, exigimos que o "título" seja único:
IS_NOT_IN_DB(db, db.image.title)
Observe que isso é opcional porque é definido automaticamente, Field('title', unique=True)
.
Os objetos que representam essas restrições são chamados de validadores. Vários validadores podem ser agrupados em uma lista. Os validadores são executados na ordem em que aparecem. IS_NOT_IN_DB(a, b)
é um validador especial que verifica se o valor de um campo b
para um novo recorde já não está em a
.
A linha 15 requer que o campo "image_id" da tabela "post" esteja em db.image.id
. No que diz respeito ao banco de dados, já havíamos declarado isso quando definimos a tabela "post". Agora estamos explicitamente informando ao modelo que essa condição também deve ser imposta pelo web2py, no nível de processamento do formulário, quando um novo comentário é postado, para que os valores inválidos não se propaguem dos formulários de entrada para o banco de dados. Nós também exigimos que o "image_id" seja representado pelo "título", '%(title)s'
, do registro correspondente.
A linha 20 indica que o campo "image_id" da tabela "post" não deve ser mostrado em formulários, writable=False
e nem mesmo em formulários somente leitura, readable=False
.
O significado dos validadores nas linhas 17-18 deve ser óbvio.
Observe que o validador
db.post.image_id.requires = IS_IN_DB(db, db.image.id, '%(title)s')
pode ser omitido (e seria automático) se especificarmos um formato para a tabela referenciada:
db.define_table('image', ..., format='%(title)s')
onde o formato pode ser uma string ou uma função que recebe um registro e retorna uma string.
Quando um modelo é definido, se não houver erros, o web2py cria uma interface de administração de aplicativos para gerenciar o banco de dados. Você o acessa através do link "administração do banco de dados" na página editar ou diretamente:
http://127.0.0.1:8000/images/appadmin
Aqui está uma captura de tela do appadmin interface:
Essa interface é codificada no controlador chamado "appadmin.py" e a visualização correspondente "appadmin.html". A partir de agora, vamos nos referir a essa interface simplesmente como appadmin. Ele permite que o administrador insira novos registros de banco de dados, edite e exclua registros existentes, navegue por tabelas e execute junções de banco de dados.
A primeira vez appadmin é acessado, o modelo é executado e as tabelas são criadas. O web2py DAL converte o código Python em instruções SQL específicas para o back-end do banco de dados selecionado (neste exemplo, SQLite). Você pode ver o SQL gerado na página editar clicando no link "sql.log" em "models". Observe que o link não está presente até que as tabelas tenham sido criadas.
Se você fosse editar o modelo e acessar appadmin novamente, o web2py geraria o SQL para alterar as tabelas existentes. O SQL gerado é registrado no "sql.log".
Agora volte para appadmin e tente inserir um novo registro de imagem:
web2py traduziu o db.image.file
campo "upload" em um formulário de upload para o arquivo. Quando o formulário é enviado e um arquivo de imagem é carregado, o arquivo é renomeado de forma segura, preservando a extensão, é salvo com o novo nome na pasta "uploads" do aplicativo e o novo nome é armazenado no arquivo. db.image.file
campo. Esse processo é projetado para evitar ataques de passagem de diretórios.
Observe que cada tipo de campo é processado por um widget . Widgets padrão podem ser substituídos.
Quando você clica no nome de uma tabela em appadmin, web2py realiza uma seleção de todos os registros na tabela atual, identificados pela consulta DAL
db.image.id > 0
e renderiza o resultado.
Você pode selecionar um conjunto diferente de registros editando a consulta DAL e pressionando [Submit].
Para editar ou excluir um único registro, clique no número de identificação do registro.
Por causa do IS_IN_DB
validador, o campo de referência "image_id" é processado por um menu suspenso. Os itens na lista suspensa são armazenados como chaves ( db.image.id
), mas são representados por seus db.image.title
, conforme especificado pelo validador.
Os validadores são objetos poderosos que sabem como representar campos, filtrar valores de campo, gerar erros e formatar valores extraídos do campo.
A figura a seguir mostra o que acontece quando você envia um formulário que não passa na validação:
Os mesmos formulários que são gerados automaticamente por appadmin também pode ser gerado programaticamente através do SQLFORM
auxiliar e incorporado em aplicativos do usuário. Esses formulários são compatíveis com CSS e podem ser personalizados.
Cada aplicativo tem seu próprio appadmin; assim sendo, appadmin pode ser modificado sem afetar outros aplicativos.
Até agora, o aplicativo sabe como armazenar dados e vimos como acessar o banco de dados via appadmin. Acesso a appadmin é restrito ao administrador e não é uma interface da Web de produção para o aplicativo; daí a próxima parte deste passo a passo. Especificamente, queremos criar:
- Uma página de "índice" que lista todas as imagens disponíveis classificadas por título e links para páginas de detalhes para as imagens.
- Uma página "show/[id]" que mostra ao visitante a imagem solicitada e permite ao visitante visualizar e postar comentários.
- Uma ação "download/[name]" para baixar as imagens enviadas.
Isto é representado esquematicamente aqui:
Volte para a página editar e edite o controlador "default.py", substituindo seu conteúdo pelo seguinte:
def index():
images = db().select(db.image.ALL, orderby=db.image.title)
return dict(images=images)
Esta ação retorna um dicionário. As chaves dos itens no dicionário são interpretadas como variáveis passadas para a visão associada à ação. Ao desenvolver, se não houver visualização, a ação é renderizada pela visualização "generic.html" que é fornecida com todos os aplicativos web2py.
A ação de índice executa uma seleção de todos os campos ( db.image.ALL
) da imagem da tabela, ordenada por db.image.title
. O resultado do select é um Rows
objeto contendo os registros. Atribuir a uma variável local chamada images
retornado pela ação para a exibição. images
é iterável e seus elementos são as linhas selecionadas. Para cada linha, as colunas podem ser acessadas como dicionários: images[0]['title']
ou equivalentemente como images[0].title
.
Se você não gravar uma exibição, o dicionário será renderizado por "views/generic.html" e uma chamada para a ação de índice será semelhante a:
Você ainda não criou uma visualização para essa ação, portanto, o web2py renderiza o conjunto de registros em formato de tabela simples.
Continue para criar uma exibição para a ação de índice. Volte a admin, edite "default/index.html" e substitua o conteúdo por:
{{extend 'layout.html'}}
<h1>Current Images</h1>
<ul>
{{for image in images:}}
{{=LI(A(image.title, _href=URL("show", args=image.id)))}}
{{pass}}
</ul>
A primeira coisa a notar é que uma exibição é HTML puro com tags {{...}} especiais. O código embutido em {{...}} é puro código Python com uma ressalva: a indentação é irrelevante. Blocos de código começam com linhas que terminam em dois pontos (:) e terminam em linhas que começam com a palavra-chave pass
. Em alguns casos, o fim de um bloco é óbvio a partir do contexto e do uso de pass
não é necessário.
As linhas 5-7 percorrem as linhas da imagem e exibem a imagem de cada linha:
LI(A(image.title, _href=URL('show', args=image.id))
Isto é um <li>...</li>
tag que contém um <a href="...">...</a>
tag que contém o image.title
. O valor da referência de hipertexto (atributo href) é:
URL('show', args=image.id)
isto é, a URL dentro do mesmo aplicativo e controlador que a solicitação atual que chama a função chamada "show", passando um único argumento para a função, args=image.id
. LI
, A
, etc. são ajudantes web2py que mapeiam para as tags HTML correspondentes. Seus argumentos sem nome são interpretados como objetos a serem serializados e inseridos no innerHTML da tag. Argumentos nomeados começando com um sublinhado (por exemplo _href
) são interpretados como atributos de tag, mas sem o sublinhado. Por exemplo _href
é o href
atributo, _class
é o class
atributo, etc.
Como exemplo, a seguinte declaração:
{{=LI(A('something', _href=URL('show', args=123))}}
é processado como:
<li><a href="/images/default/show/123">something</a></li>
Um punhado de ajudantes ( INPUT
, TEXTAREA
, OPTION
e SELECT
) também suporta alguns atributos nomeados especiais que não começam com sublinhado ( value
e requires
). Eles são importantes para criar formulários personalizados e serão discutidos posteriormente.
Volte para a página editar . Agora indica que "default.py expõe o índice". Ao clicar em "index", você pode visitar a página recém-criada:
http://127.0.0.1:8000/images/default/index
que se parece com:
Se você clicar no link do nome da imagem, você será direcionado para:
http://127.0.0.1:8000/images/default/show/1
e isso resulta em um erro, uma vez que você ainda não criou uma ação chamada "show" no controller "default.py".
Vamos editar o controlador "default.py" e substituir seu conteúdo por:
def index():
images = db().select(db.image.ALL, orderby=db.image.title)
return dict(images=images)
def show():
image = db.image(request.args(0, cast=int)) or redirect(URL('index'))
db.post.image_id.default = image.id
form = SQLFORM(db.post)
if form.process().accepted:
response.flash = 'your comment is posted'
comments = db(db.post.image_id == image.id).select(orderby=db.post.id)
return dict(image=image, comments=comments, form=form)
def download():
return response.download(request, db)
O controlador contém duas novas ações: "show" e "download". A ação "show" seleciona a imagem com o id
analisado a partir dos argumentos de solicitação e todos os comentários relacionados à imagem. "show" então passa tudo para a view "default/show.html".
O ID da imagem referenciado por:
URL('show', args=image.id)
em "default/index.html", pode ser acessado como:
request.args(0, cast=int)
da ação "show". o cast=int
O argumento é opcional, mas muito importante. Ele tenta converter o valor de sequência passado no PATH_INFO em um int. Na falha, ele gera uma exceção apropriada em vez de causar um ticket. Também é possível especificar um redirecionamento em caso de falha ao transmitir:
request.args(0, cast=int, otherwise=URL('error'))
Além disso db.image(...)
é um atalho para
db(db.image.id == ...).select().first()
A ação "download" espera um nome de arquivo request.args(0)
, cria um caminho para o local onde o arquivo deve estar e o envia de volta ao cliente. Se o arquivo é muito grande, ele transmite o arquivo sem incorrer em sobrecarga de memória.
Observe as seguintes declarações:
- A linha 7 define o valor para o campo de referência, que não faz parte do formulário de entrada devido à
db.post
modelo de tabela. - A linha 8 cria um formulário de inserção SQLFORM para o
db.post
mesa. - A linha 9 processa o formulário submetido (as variáveis do formulário submetidas estão em
request.vars
) dentro da sessão atual (a sessão é usada para evitar submissões duplas e para reforçar a navegação). Se as variáveis de formulário submetidas forem validadas, o novo comentário será inserido nodb.post
mesa; caso contrário, o formulário será modificado para incluir mensagens de erro (por exemplo, se o endereço de email do autor for inválido). Tudo isso é feito na linha 9 !. - A linha 10 só é executada se o formulário for aceito, após o registro ser inserido na tabela do banco de dados.
response.flash
é uma variável web2py que é exibida nas visualizações e usada para notificar o visitante de que algo aconteceu. - A linha 11 seleciona todos os comentários que fazem referência à imagem atual,
.select(orderby=db.post.id)
mantém o histórico de comentários classificado.
A ação "download" já está definida no controlador "default.py" do aplicativo scaffolding.
A ação "download" não retorna um dicionário, portanto, não precisa de uma visualização. A ação "show", no entanto, deve ter uma visão, então retorne para admin e crie uma nova visão chamada "default/show.html".
Edite este novo arquivo e substitua seu conteúdo pelo seguinte:
{{extend 'layout.html'}}
<h1>Image: {{=image.title}}</h1>
<div style="text-align:center">
<img width="200px"
src="{{=URL('download', args=image.file)}}" />
</div>
{{if len(comments):}}
<h2>Comments</h2><br /><p>
{{for post in comments:}}
<p>{{=post.author}} says <i>{{=post.body}}</i></p>
{{pass}}</p>
{{else:}}
<h2>No comments posted yet</h2>
{{pass}}
<h2>Post a comment</h2>
{{=form}}
Esta vista mostra o arquivo de imagem chamando a ação "download" dentro de um <img ... />
tag. Se houver comentários, ele faz um loop sobre eles e exibe cada um deles.
Aqui está como tudo aparecerá para um visitante.
Quando um visitante envia um comentário por meio dessa página, o comentário é armazenado no banco de dados e anexado na parte inferior da página.
Adicionando autenticação
A API web2py para Controle de Acesso Baseado em Função é bastante sofisticada, mas por enquanto nos limitaremos a restringir o acesso à ação de exibição para usuários autenticados, adiando uma discussão mais detalhada para o Capítulo 9.
Para limitar o acesso a usuários autenticados, precisamos concluir três etapas. Em um modelo, por exemplo "db.py", precisamos adicionar:
from gluon.tools import Auth
auth = Auth(db)
auth.define_tables(username=True)
Em nosso controlador, precisamos adicionar uma ação:
def user():
return dict(form=auth())
Isso é suficiente para ativar as páginas de login, registro, logout etc. O layout padrão também mostrará opções para as páginas correspondentes no canto superior direito.
Podemos agora decorar as funções que queremos restringir, por exemplo:
@auth.requires_login()
def show():
...
Qualquer tentativa de acesso
http://127.0.0.1:8000/images/default/show/[image_id]
exigirá login. Se o usuário não estiver logado, o usuário será redirecionado para
http://127.0.0.1:8000/images/default/user/login
o user
função também expõe, entre outras, as seguintes ações:
http://127.0.0.1:8000/images/default/user/logout
http://127.0.0.1:8000/images/default/user/register
http://127.0.0.1:8000/images/default/user/profile
http://127.0.0.1:8000/images/default/user/change_password
http://127.0.0.1:8000/images/default/user/request_reset_password
http://127.0.0.1:8000/images/default/user/retrieve_username
http://127.0.0.1:8000/images/default/user/retrieve_password
http://127.0.0.1:8000/images/default/user/verify_email
http://127.0.0.1:8000/images/default/user/impersonate
http://127.0.0.1:8000/images/default/user/not_authorized
Agora, um usuário iniciante precisa se registrar para poder fazer login e ler ou postar comentários.
Tanto o
auth
objeto e ouser
funções já estão definidas na aplicação de scaffold. oauth
O objeto é altamente personalizável e pode lidar com a verificação de e-mail, aprovações de registro, CAPTCHA e métodos alternativos de login por meio de plug-ins.
Adicionando grades
Podemos melhorar ainda mais usando o SQLFORM.grid
e SQLFORM.smartgrid
gadgets para criar uma interface de gerenciamento para nosso aplicativo:
@auth.requires_membership('manager')
def manage():
grid = SQLFORM.smartgrid(db.image, linked_tables=['post'])
return dict(grid=grid)
com "views/default/manage.html" associados
{{extend 'layout.html'}}
<h2>Management Interface</h2>
{{=grid}}
Usando appadmin, crie um grupo "manager" e faça alguns usuários membros do grupo. Eles poderão acessar
http://127.0.0.1:8000/images/default/manage
e navegue, pesquise:
criar, atualizar e excluir imagens e seus comentários:
Configurando o layout
Você pode configurar o layout padrão editando "views/layout.html", mas também pode configurá-lo sem editar o HTML. Na verdade, a folha de estilo "static/css/web2py.css" é bem documentada e descrita no Capítulo 5. Você pode alterar cores, colunas, tamanhos, bordas e plano de fundo sem editar o HTML. Se você quiser editar o menu, o título ou a legenda, pode fazê-lo em qualquer arquivo de modelo. O aplicativo scaffolding, define os valores padrão destes parâmetros no arquivo "models/menu.py":
response.title = request.application
response.subtitle = 'customize me!'
response.meta.author = 'you'
response.meta.description = 'describe your app'
response.meta.keywords = 'bla bla bla'
response.menu = [ [ 'Index', False, URL('index') ] ]
Um simples wiki
Nesta seção, construímos um wiki simples a partir do zero, usando apenas APIs de baixo nível (em oposição a usar os recursos de wiki integrados do web2py demonstrados na próxima seção). O visitante poderá criar páginas, pesquisá-las (por título) e editá-las. O visitante também poderá postar comentários (exatamente como nos aplicativos anteriores) e também postar documentos (como anexos nas páginas) e vinculá-los a partir das páginas. Como convenção, adotamos a sintaxe Markmin para a nossa sintaxe wiki. Também implementamos uma página de pesquisa com o Ajax, um feed RSS para as páginas e um manipulador para pesquisar as páginas via XML-RPC. [xmlrpc] . O diagrama a seguir lista as ações que precisamos implementar e os links que pretendemos construir entre eles.
Comece criando um novo aplicativo de scaffold, nomeando-o como "mywiki".
O modelo deve conter três tabelas: página, comentário e documento. Ambos comentam e documentam a página de referência porque pertencem à página. Um documento contém um campo de arquivo do tipo upload, como no aplicativo de imagens anterior.
Aqui está o modelo completo:
db = DAL('sqlite://storage.sqlite')
from gluon.tools import *
auth = Auth(db)
auth.define_tables()
crud = Crud(db)
db.define_table('page',
Field('title'),
Field('body', 'text'),
Field('created_on', 'datetime', default=request.now),
Field('created_by', 'reference auth_user', default=auth.user_id),
format='%(title)s')
db.define_table('post',
Field('page_id', 'reference page'),
Field('body', 'text'),
Field('created_on', 'datetime', default=request.now),
Field('created_by', 'reference auth_user', default=auth.user_id))
db.define_table('document',
Field('page_id', 'reference page'),
Field('name'),
Field('file', 'upload'),
Field('created_on', 'datetime', default=request.now),
Field('created_by', 'reference auth_user', default=auth.user_id),
format='%(name)s')
db.page.title.requires = IS_NOT_IN_DB(db, 'page.title')
db.page.body.requires = IS_NOT_EMPTY()
db.page.created_by.readable = db.page.created_by.writable = False
db.page.created_on.readable = db.page.created_on.writable = False
db.post.body.requires = IS_NOT_EMPTY()
db.post.page_id.readable = db.post.page_id.writable = False
db.post.created_by.readable = db.post.created_by.writable = False
db.post.created_on.readable = db.post.created_on.writable = False
db.document.name.requires = IS_NOT_IN_DB(db, 'document.name')
db.document.page_id.readable = db.document.page_id.writable = False
db.document.created_by.readable = db.document.created_by.writable = False
db.document.created_on.readable = db.document.created_on.writable = False
Edite o controlador "default.py" e crie as seguintes ações:
- index: lista todas as páginas wiki
- criar: adicionar uma nova página da wiki
- show: mostra uma página wiki e seus comentários e adiciona novos comentários
- edit: edite uma página existente
- documentos: gerencie os documentos anexados a uma página
- download: baixe um documento (como no exemplo das imagens)
- search: exibe uma caixa de pesquisa e, por meio de um callback do Ajax, retorna todos os títulos correspondentes conforme o tipo de visitante.
- callback: a função de retorno de chamada do Ajax. Ele retorna o HTML que é incorporado na página de pesquisa enquanto o visitante digita.
Aqui está o controlador "default.py":
def index():
""" this controller returns a dictionary rendered by the view
it lists all wiki pages
>>> index().has_key('pages')
True
"""
pages = db().select(db.page.id, db.page.title, orderby=db.page.title)
return dict(pages=pages)
@auth.requires_login()
def create():
"""creates a new empty wiki page"""
form = SQLFORM(db.page).process(next=URL('index'))
return dict(form=form)
def show():
"""shows a wiki page"""
this_page = db.page(request.args(0, cast=int)) or redirect(URL('index'))
db.post.page_id.default = this_page.id
form = SQLFORM(db.post).process() if auth.user else None
pagecomments = db(db.post.page_id == this_page.id).select(orderby=db.post.id)
return dict(page=this_page, comments=pagecomments, form=form)
@auth.requires_login()
def edit():
"""edit an existing wiki page"""
this_page = db.page(request.args(0, cast=int)) or redirect(URL('index'))
form = SQLFORM(db.page, this_page).process(
next = URL('show', args=request.args))
return dict(form=form)
@auth.requires_login()
def documents():
"""browser, edit all documents attached to a certain page"""
page = db.page(request.args(0, cast=int)) or redirect(URL('index'))
db.document.page_id.default = page.id
grid = SQLFORM.grid(db.document.page_id == page.id, args=[page.id])
return dict(page=page, grid=grid)
def user():
return dict(form=auth())
def download():
"""allows downloading of documents"""
return response.download(request, db)
def search():
"""an ajax wiki search page"""
return dict(form=FORM(INPUT(_id='keyword',
_name='keyword',
_onkeyup="ajax('callback', ['keyword'], 'target');")),
target_div=DIV(_id='target'))
def callback():
"""an ajax callback that returns a <ul> of links to wiki pages"""
query = db.page.title.contains(request.vars.keyword)
pages = db(query).select(orderby=db.page.title)
links = [A(p.title, _href=URL('show', args=p.id)) for p in pages]
return UL(*links)
As linhas 2-6 constituem um comentário para a ação do índice. Linhas 4-5 dentro do comentário são interpretadas pelo python como código de teste (doctest). Os testes podem ser executados através da interface administrativa. Nesse caso, os testes verificam se a ação do índice é executada sem erros.
As linhas 18, 27 e 35 tentam buscar page
gravar com o id em request.args(0)
.
As linhas 13, 20 definem e processam formulários de criação para uma nova página e um novo comentário e
A linha 28 define e processa um formulário de atualização para uma página wiki.
A linha 37 cria um grid
objeto que permite visualizar, adicionar e atualizar os comentários ligados a uma página.
Alguma mágica acontece na linha 51. onkeyup
atributo da tag INPUT "keyword" está definido. Toda vez que o visitante libera uma chave, o código JavaScript dentro do onkeyup
atributo é executado, do lado do cliente. Aqui está o código JavaScript:
ajax('callback', ['keyword'], 'target');
ajax
é uma função JavaScript definida no arquivo "web2py.js", incluído no padrão "layout.html". São necessários três parâmetros: a URL da ação que executa o retorno de chamada síncrono, uma lista dos IDs das variáveis a serem enviadas ao retorno de chamada (["keyword"]) e o ID no qual a resposta deve ser inserida ("destino ").Assim que você digita algo na caixa de pesquisa e libera uma chave, o cliente chama o servidor e envia o conteúdo do campo "palavra-chave" e, quando o servidor responde, a resposta é incorporada na própria página como innerHTML de a tag "alvo".
A tag 'target' é um DIV definido na linha 52. Ela também poderia ter sido definida na exibição.
Aqui está o código para a visualização "default/create.html":
{{extend 'layout.html'}}
<h1>Create new wiki page</h1>
{{=form}}
Supondo que você está registrado e logado, se você visitar o crio página, você vê o seguinte:
Aqui está o código para a visualização "default/index.html":
{{extend 'layout.html'}}
<h1>Available wiki pages</h1>
[ {{=A('search', _href=URL('search'))}} ]<br />
<ul>{{for page in pages:}}
{{=LI(A(page.title, _href=URL('show', args=page.id)))}}
{{pass}}</ul>
[ {{=A('create page', _href=URL('create'))}} ]
Ele gera a seguinte página:
Aqui está o código para a visualização "default/show.html":
{{extend 'layout.html'}}
<h1>{{=page.title}}</h1>
[ {{=A('edit', _href=URL('edit', args=request.args))}} ]<br />
{{=MARKMIN(page.body)}}
<h2>Comments</h2>
{{for post in comments:}}
<p>{{=db.auth_user[post.created_by].first_name}} on {{=post.created_on}}
says <i>{{=post.body}}</i></p>
{{pass}}
<h2>Post a comment</h2>
{{=form}}
Se você deseja usar a sintaxe de markdown em vez da sintaxe markmin:
from gluon.contrib.markdown import WIKI as MARKDOWN
E use MARKDOWN
ao invés de MARKMIN
ajudante. Como alternativa, você pode optar por aceitar HTML bruto em vez da sintaxe markmin. Neste caso você substituiria:
{{=MARKMIN(page.body)}}
com:
{{=XML(page.body)}}
(para que o XML não escape, o que o web2py normalmente faz por padrão por razões de segurança).
Isso pode ser feito melhor com:
{{=XML(page.body, sanitize=True)}}
Definindo sanitize=True
, você diz ao web2py para escapar de tags XML inseguras, como "<script>", e assim evitar vulnerabilidades XSS.
Agora, se você clicar na página de índice e clicar no título de uma página, poderá ver a página que criou:
Aqui está o código para a visualização "default/edit.html":
{{extend 'layout.html'}}
<h1>Edit wiki page</h1>
[ {{=A('show', _href=URL('show', args=request.args))}}
| {{=A('documents', _href=URL('documents', args=request.args))}} ]<br />
{{=form}}
Ele gera uma página que parece quase idêntica à página de criação.
Aqui está o código para a visualização "default/documents.html":
{{extend 'layout.html'}}
<h1>Documents for page: {{=page.title}}</h1>
[ {{=A('show', _href=URL('show', args=request.args))}} ]<br />
<h2>Documents</h2>
{{=grid}}
Se, na página "mostrar", você clicar em documentos, poderá gerenciar os documentos anexados à página.
Finalmente aqui está o código para a visão "default/search.html":
{{extend 'layout.html'}}
<h1>Search wiki pages</h1>
[ {{=A('listall', _href=URL('index'))}}]<br />
{{=form}}<br />{{=target_div}}
que gera o seguinte formulário de pesquisa do Ajax:
Você também pode tentar chamar a ação de retorno diretamente visitando, por exemplo, o seguinte URL:
http://127.0.0.1:8000/mywiki/default/callback?keyword=main
Se você observar a origem da página, verá o HTML retornado pelo retorno de chamada:
<ul><li><a href="/mywiki/default/show/4">My Main Page</a></li></ul>
Gerar um feed RSS de suas páginas wiki usando o web2py é fácil porque o web2py inclui gluon.contrib.rss2
. Basta anexar a seguinte ação ao controlador padrão:
def news():
"""generates rss feed from the wiki pages"""
response.generic_patterns = ['.rss']
pages = db().select(db.page.ALL, orderby=db.page.title)
return dict(title='mywiki rss feed',
link='http://127.0.0.1:8000/mywiki/default/index',
description='mywiki news',
created_on=request.now,
items=[dict(title=row.title,
link=URL('show', args=row.id, scheme=True, host=True, extension=False),
description=MARKMIN(row.body).xml(),
created_on=row.created_on) for row in pages])
e quando você visita a página
http://127.0.0.1:8000/mywiki/default/news.rss
você vê o feed (a saída exata depende do leitor de feed). Observe que o dict é convertido automaticamente em RSS, graças à extensão .rss no URL.
O web2py também inclui o feedparser para ler feeds de terceiros.
Observe que a linha:
response.generic_patterns = ['.rss']
instrui o web2py a usar views genéricas (no nosso caso "views/generic.rss") quando o URL terminar no padrão glob ".rss". Por padrão, visualizações genéricas só são permitidas a partir do host local para fins de desenvolvimento.
Finalmente, vamos adicionar um manipulador XML-RPC que permite pesquisar o wiki de forma programática:
service = Service()
@service.xmlrpc
def find_by(keyword):
"""finds pages that contain keyword for XML-RPC"""
return db(db.page.title.contains(keyword)).select().as_list()
def call():
"""exposes all registered services, including XML-RPC"""
return service()
Aqui, a ação do manipulador simplesmente publica (via XML-RPC), as funções decoradas por @ervice.xmlrpc
. Nesse caso, find_by
. find_by
não é uma ação (porque leva um argumento). Consulta o banco de dados com .select()
e, em seguida, extrai os registros como uma lista com .as_list()
e retorna a lista.
Aqui está um exemplo de como acessar o manipulador XML-RPC a partir de um Programa em Python.
>>> import xmlrpclib
>>> server = xmlrpclib.ServerProxy('http://127.0.0.1:8000/mywiki/default/call/xmlrpc')
>>> for item in server.find_by('main'):
print item['created_on'], item['title']
O manipulador pode ser acessado de muitas outras linguagens de programação que entendem o XML-RPC, incluindo C, C ++, C # e Java.
Em date
, datetime
e time
formato
Existem três representações diferentes para cada um dos tipos de campo date
, datetime
e time
:
- a representação do banco de dados
- a representação interna da web2py
- a representação de string em formulários e tabelas
A representação do banco de dados é um problema interno e não afeta o código. Internamente, no nível web2py, eles são armazenados como datetime.date
, datetime.datetime
e datetime.time
objeto respectivamente e eles podem ser manipulados como tal:
for page in db(db.page).select():
print page.title, page.created_on.day, page.created_on.month, page.created_on.year
Quando as datas são convertidas em strings em formulários, elas são convertidas usando a representação ISO
%Y-%m-%d %H:%M:%S
no entanto, essa representação é internacionalizada e você pode usar a página de tradução administrativa para alterar o formato para um alternativo. Por exemplo:
%m/%d/%Y %H:%M:%S
Lembre-se que, por padrão, o inglês não é traduzido porque o web2py assume que os aplicativos estão escritos em inglês. Se você quer que a internacionalização funcione para o inglês, você precisa criar o arquivo de tradução (usando admin) e você precisa declarar que o idioma atual do aplicativo é diferente do inglês, por exemplo:
T.current_languages = ['null']
O wiki web2py embutido
Agora você pode esquecer o código que construímos na seção anterior (não o que você aprendeu sobre APIs web2py, apenas o código do exemplo específico), pois vamos fornecer um exemplo do wiki web2py embutido.
Na verdade, o web2py vem com recursos de wiki, incluindo anexos de mídia, tags, nuvem de tags, permissões de página e suporte para oembed [oembed] e componentes (capítulo 14). Este wiki pode ser usado com qualquer aplicativo web2py.
Observe que a API do wiki integrado ainda é considerada experimental e pequenas alterações ainda são possíveis.
Aqui nós assumimos que estamos começando do zero a partir de um simples clone do aplicativo "welcome" chamado "wikidemo". Se não, certifique-se de que db.py
as migrações são ativadas, caso contrário, as novas tabelas wiki necessárias não serão criadas automaticamente.
Edite o controlador e substitua a ação "index" por.
def index(): return auth.wiki()
Feito! Você tem um wiki totalmente funcional. Por favor, note que o wiki precisa de algumas tabelas para serem definidas, e elas só serão definidas ao acessar o controlador. Se você quiser que eles estejam prontamente disponíveis, use auth.wiki(resolve=False)
e verifique se as migrações de tabela estão ativadas: mais sobre isso no Estendendo-o-auth-wiki-feature seção seguinte.
Neste ponto nenhuma página foi criada e para criar páginas você deve estar logado e você deve ser membro de um grupo chamado "wiki_editor" ou "wiki_author". Se você está logado como administrador, o grupo "wiki_editor" é criado automaticamente e você se torna um membro. A diferença entre editores e autores é que os editores podem criar páginas, editar e excluir qualquer página, enquanto os autores podem criar páginas (com algumas restrições opcionais) e só podem editar/excluir as páginas que criaram.
o auth.wiki()
função retorna em um dicionário com uma chave content
que é entendido pelo scaffolding "views/default/index.html". Você pode criar sua própria opinião para esta ação:
{{extend 'layout.html'}}
{{=content}}
e adicione HTML ou código extra, conforme necessário. Você não precisa usar a ação "index" para expor o wiki. Você pode usar uma ação com um nome diferente.
Para experimentar o wiki, basta acessar o administrador, visitar a página
http://127.0.0.1:8000/wikidemo/default/index
Em seguida, escolha um slug (no ramo de publicação, um slug é um nome curto dado a um artigo que está em produção) e você será redirecionado para uma página vazia, onde poderá editar o conteúdo usando a sintaxe wiki do MARKMIN. Um novo item de menu chamado "[wiki]" permitirá que você crie, pesquise e edite páginas. Páginas Wiki têm URLs como:
http://127.0.0.1:8000/wikidemo/default/index/[slug]
As páginas de serviço têm nomes que começam por sublinhado:
http://127.0.0.1:8000/wikidemo/default/index/_create
http://127.0.0.1:8000/wikidemo/default/index/_search
http://127.0.0.1:8000/wikidemo/default/index/_could
http://127.0.0.1:8000/wikidemo/default/index/_recent
http://127.0.0.1:8000/wikidemo/default/index/_edit/...
http://127.0.0.1:8000/wikidemo/default/index/_editmedia/...
http://127.0.0.1:8000/wikidemo/default/index/_preview/...
Tente criar mais páginas como "index", "aboutus" e "contactus". Tente editá-los.
o wiki
método tem a seguinte assinatura:
def wiki(self, slug=None, env=None, render='markmin',
manage_permissions=False, force_prefix='',
restrict_search=False, resolve=True,
extra=None, menu_groups=None)
Leva os seguintes argumentos:
render
qual padrão é'markmin'
mas pode ser igual a'html'
. Ele determina a sintaxe do wiki. Nós discutiremos a marcação do markmin wiki mais tarde. Se você alterá-lo para HTML, você pode usar um editor de javascript wysiwyg, como TinyMCE ou NicEdit.manage_permissions
. Isso está definido paraFalse
por padrão e só reconhece permissões para "wiki_editor" e "wiki_author". Se você mudar paraTrue
a página de criação/edição dará a opção de especificar por nome o (s) grupo (s) cujos membros têm permissão para ler e editar a página. Existe um grupo "todo mundo", que inclui todos os usuários.force_prefix
. Se definido para algo como'%(id)s-'
ele restringirá os autores (não os editores) a criar páginas com um prefixo como "[user id] - [page name]". O prefixo pode conter o id ("% (id) s") ou o nome de usuário ("% (nome de usuário) s") ou qualquer outro campo da tabela auth_user, contanto que a coluna correspondente contenha uma string válida que passe a URL validação.restrict_search
. Este padrão éFalse
e qualquer usuário logado pode pesquisar todas as páginas wiki (mas não é necessário ler ou editá-las). Se definido paraTrue
, os autores podem pesquisar apenas suas próprias páginas, editores podem pesquisar tudo, outros usuários não podem pesquisar nada.menu_groups
. Este padrão éNone
e indica que o menu de gerenciamento do wiki (pesquisa, criação, edição, etc.) é sempre exibido. Você pode configurá-lo para uma lista de nomes de grupos cujos membros só podem ver este menu, por exemplo['wiki_editor','wiki_author']
. Observe que, mesmo que o menu seja exposto a todos, isso não significa que todos podem realizar ações listadas no menu, uma vez que são reguladas pelo sistema de controle de acesso.
o wiki
método tem alguns parâmetros adicionais que serão explicados mais tarde: slug
, env
e extra
.
Noções básicas de MARKMIN
A sintaxe MARKMIN permite que você marque negrito texto usando **bold**
texto "itálico" com ''italic''
e code
deve ser delimitado por aspas duplas invertidas. Use um sinal de menos (-) para prefixar um item não ordenado e mais (+) para prefixar um item ordenado. URLs são automaticamente convertidos em links. Aqui está um exemplo de texto markmin:
# This is a title
## this is a section title
### this is a subsection title
Text can be **bold**, ''italic'', ``code`` etc.
Learn more at:
http://web2py.com
Você pode usar o parâmetro extra
de auth.wiki
para passar regras extras de renderização para o ajudante MARKMIN.
Você pode encontrar mais informações sobre a sintaxe do MARKMIN no capítulo 5.
auth.wiki
é mais poderoso que os ajudantes MARKMIN barebones, suportando componentes e oembed.
Você pode usar o env
parâmetro de auth.wiki
expor funções ao seu wiki. Por exemplo:
auth.wiki(env=dict(join=lambda a:"-".join(a.split(","))))
permite que você use a sintaxe de marcação:
@{join:1,2,3}
Isso chama a função de junção passada para env com argumento "1,2,3"
e será processado como 1-2-3
.
Protocolo Oembed
Você pode digitar (ou recortar e colar) qualquer URL em uma página da wiki e ele será renderizado como um link para a URL. Existem exceções:
- Se o URL tiver uma extensão de imagem, o link será incorporado como uma imagem.
<img/>
. - Se o URL tiver uma extensão de áudio, o link será incorporado como áudio HTML5
<audio/>
. - Se o URL tiver uma extensão de vídeo, o link será incorporado como vídeo HTML5
<video/>
. - Se o URL tiver uma extensão do MS Office ou PDF, o Visualizador do Google Doc está incorporado, mostrando o conteúdo do documento (funciona apenas para documentos públicos).
- Se o URL apontar para uma página do YouTube, uma página do Vimeo ou uma página do Flickr, a web2py entrará em contato com o serviço da Web correspondente e consultará sobre a maneira correta de incorporar o conteúdo. Isso é feito usando o protocolo
oembed
.
Aqui está uma lista completa de formatos suportados:
Image (.PNG, .GIF, .JPG, .JPEG)
Audio (.WAV, .OGG, .MP3)
Video (.MOV, .MPE, .MP4, .MPG, .MPG2, .MPEG, .MPEG4, .MOVIE)
Suportado pelo visualizador do Google Docs:
Microsoft Excel (.XLS and .XLSX)
Microsoft PowerPoint 2007 / 2010 (.PPTX)
Apple Pages (.PAGES)
Adobe PDF (.PDF)
Adobe Illustrator (.AI)
Adobe Photoshop (.PSD)
Autodesk AutoCad (.DXF)
Scalable Vector Graphics (.SVG)
PostScript (.EPS, .PS)
TrueType (.TTF)
xml Paper Specification (.XPS)
Apoiado por oembed:
flickr.com
youtube.com
hulu.com
vimeo.com
slideshare.net
qik.com
polleverywhere.com
wordpress.com
revision3.com
viddler.com
Isso é implementado no arquivo web2py gluon.contrib.autolinks
e especificamente na função expand_one
. Você pode estender o suporte integrado registrando mais serviços. Isso é feito anexando uma entrada ao EMBED_MAPS
Lista:
from gluon.contrib.autolinks import EMBED_MAPS
EMBED_MAPS.append((re.compile('http://vimeo.com/\S*'),
'http://vimeo.com/api/oembed.json'))
Referenciando o conteúdo do wiki
Se você criar uma página wiki com slug "contactus", você pode se referir a esta página como
@////contactus
Aqui @ //// apoia
@/app/controller/function/
mas "app", "controller" e "function" são omitidos, assumindo assim o padrão.
Da mesma forma, você pode usar o menu wiki para fazer upload de um arquivo de mídia (por exemplo, uma imagem) vinculado à página. A página "gerenciar mídia" mostrará todos os arquivos que você enviou e também mostrará a expressão adequada para vincular o arquivo de mídia. Se, por exemplo, você fizer o upload de um arquivo chamado "test.jpg" com o título "beach", a expressão do link será algo como:
@////15/beach.jpg
@////
é o mesmo prefixo descrito anteriormente. 15
é o id do registro que armazena o arquivo de mídia. beach
é o título. .jpg
é a extensão do arquivo original.
Se você cortar e colar @////15/beach.jpg
nas páginas wiki você incorpora a imagem.
Lembre-se de que os arquivos de mídia estão vinculados a páginas e herdam a permissão de acesso das páginas.
Menus Wiki
Se você criar uma página com slug "wiki-menu", ela será interpretada como uma descrição do menu. Aqui está um exemplo:
- Home > @////index
- Info > @////info
- web2py > http://www.web2py.com
- - About us > @////aboutus
- - Contact us > @////contactus
Cada linha um item de menu. Usamos o traço duplo para itens de menu aninhados. o >
símbolos separa o título do item de menu do link do item de menu.
Lembre-se de que o menu é anexado a response.menu
. Não substitui isso. o [wiki]
item de menu com funções de serviço é adicionado automaticamente.
Funções de serviço
Se, por exemplo, você quiser usar o wiki para criar uma barra lateral editável, você pode criar uma página com slug="sidebar"
e depois incorporá-lo no seu layout.html com
{{=auth.wiki(slug='sidebar')}}
Observe que não há nada especial com a palavra "sidebar". Qualquer página wiki pode ser recuperada e incorporada em qualquer ponto do seu código. Isso permite misturar e combinar funcionalidades do wiki com funcionalidades regulares do web2py.
Observe também que
é o mesmo queauth.wiki('sidebar'), já que o slug kwarg é o primeiro na assinatura do método. O primeiro dá uma sintaxe ligeiramente mais simples.auth.wiki(slug='sidebar')
Você também pode incorporar funções especiais do wiki, como a pesquisa por tags:
{{=auth.wiki('_search')}}
ou a nuvem de tags:
{{=auth.wiki('_cloud')}}
Estendendo o recurso auth.wiki
Quando seu aplicativo habilitado para wiki ficar mais complicado, talvez seja necessário personalizar os registros db do wiki gerenciados pela interface Auth ou expor formulários personalizados para tarefas CRUD do wiki. Por exemplo, você pode querer personalizar uma representação de registro de tabela wiki ou adicionar um novo validador de campo. Isto não é permitido por padrão, já que o modelo wiki é definido somente após a interface wiki ser solicitada com o método auth.wiki (). Para permitir o acesso à configuração do banco de dados específica do wiki dentro do modelo do seu aplicativo, você deve adicionar a seguinte frase ao seu arquivo de modelo (por exemplo, db.py)
# Make sure this is called after the auth instance is created
# and before any change to the wiki tables
auth.wiki(resolve=False)
Usando a linha acima em seu modelo, as tabelas wiki estarão acessíveis (por exemplo, wiki_page
) para CRUD personalizado ou outras tarefas do banco de dados.
Note que você ainda tem que chamar auth.wiki () no controller ou view para expor a interface wiki, já que o parâmetro
resolve=False
instrui o objeto auth a apenas construir o modelo wiki sem qualquer outra configuração de interface.
Além disso, definindo a resolução para False
na chamada do método, as tabelas wiki estarão agora acessíveis através da interface padrão do aplicativo no <app>/appadmin
para gerenciar registros wiki.
Outra customização possível é adicionar campos extras às tabelas wiki padrão (da mesma forma que com o auth_user
tabela, conforme descrito no Capítulo 9). Aqui está como:
# Place this after auth object initialization
auth.settings.extra_fields["wiki_page"] = [Field("ablob", "blob"), ]
A linha acima adiciona um blob
campo para o wiki_page
mesa. Não há necessidade de ligar
auth.wiki(resolve=False)
Componentes
Uma das funções mais poderosas do novo web2py consiste na capacidade de incorporar uma ação dentro de outra ação. Nós chamamos isso de um componente.
Considere o seguinte modelo:
db.define_table('thing', Field('name', requires=IS_NOT_EMPTY()))
e a seguinte ação:
@auth.requires_login()
def manage_things():
return SQLFORM.grid(db.thing)
Esta ação é especial porque retorna um widget/helper e não um dict de objetos. Agora podemos incorporar isso manage_things
ação em qualquer ponto de vista, com
{{=LOAD('default', 'manage_things', ajax=True)}}
Isso permite que o visitante interaja com o componente via Ajax sem recarregar a página do host que incorpora o widget. A ação é chamada via Ajax, herda o estilo da página do host e captura todos os envios de formulários e mensagens flash para que sejam manipulados na página atual. Além disso, o SQLFORM.grid
widget usa URLs assinadas digitalmente para restringir o acesso. Mais informações sobre componentes podem ser encontradas no capítulo 13.
Componentes como o acima podem ser incorporados em páginas wiki usando a sintaxe MARKMIN:
@{component:default/manage_things}
Isso simplesmente diz ao web2py que queremos incluir a ação "manage_things" definida no controlador "default" como um "componente" Ajax.
A maioria dos usuários poderá construir aplicativos relativamente complexos simplesmente usando
auth.wiki
para criar páginas e menus e incorporar componentes personalizados em páginas wiki. Os wikis podem ser considerados como um mecanismo para permitir que os membros do grupo criem páginas, mas também podem ser pensados como uma maneira de desenvolver aplicativos de maneira modular.
Mais sobre admin
A interface administrativa fornece funcionalidades adicionais que são brevemente revisadas aqui.
Local
Esta página é a principal interface administrativa do web2py. Ele lista todos os aplicativos instalados à esquerda, enquanto no lado direito há alguns formulários de ação especiais.
O primeiro deles mostra a versão web2py e propõe a atualização se novas versões estiverem disponíveis. Claro, antes de atualizar certifique-se de ter um backup completo de trabalho! Em seguida, há dois outros formulários que permitem a criação de um novo aplicativo (simples ou usando um assistente on-line), especificando seu nome.
https://github.com/rochacbruno/Movuca
ou Instant Press CMS criado por Martin Mulone:
http://code.google.com/p/instant-press/
ou um dos muitos exemplos de aplicativos disponíveis em:
http://web2py.com/appliances
Arquivos Web2py são pacotes como
.w2p
arquivos. Estes são arquivos compactados com tar. Web2py usa o.w2p
extensão em vez do.tgz
extensão para impedir que o navegador descompacte o download. Eles podem ser descomprimidos manualmente comtar xzvf [filename]
embora isso nunca seja necessário.
Após o upload bem-sucedido, o web2py exibe a soma de verificação MD5 do arquivo enviado. Você pode usá-lo para verificar se o arquivo não foi corrompido durante o upload. O nome do aplicativo aparecerá na lista de aplicativos instalados.
Se você executar o web2py a partir do código-fonte e tiver gitpython
instalado (se necessário, configure-o com 'easy_install gitpython'), você pode instalar aplicativos diretamente de repositórios git usando o .git
URL no formulário de upload. Nesse caso, você também estará habilitado para usar a interface administrativa para enviar as alterações de volta ao repositório, mas esse é um recurso experimental.
Por exemplo, você pode instalar localmente o aplicativo que mostra este livro no site web2py com o URL:
https://github.com/mdipierro/web2py-book.git
Esse repositório hospeda a versão atual e atualizada deste livro (que pode ser diferente da versão estável que você pode ver no site). Você está calorosamente convidado a usá-lo para enviar melhorias, correções e correções na forma de solicitações pull.
Para cada aplicativo instalado, você pode usar a página site para:
- Ir diretamente para o aplicativo, clicando em seu nome.
- Desinstale o aplicativo.
- Salte para a página about (leia abaixo).
- Ir para a página editar (leia abaixo).
- Ir para a página erros (leia abaixo).
- Limpe arquivos temporários (sessões, erros e arquivos cache.disk).
- Empacote tudo. Isso retorna um arquivo tar contendo uma cópia completa do aplicativo. Sugerimos que você limpe arquivos temporários antes de empacotar um aplicativo.
- Compile o aplicativo. Se não houver erros, esta opção irá compilar todos os modelos, controladores e visualizações. Como as exibições podem estender e incluir outras exibições em uma árvore, antes da compilação de bytecode, a árvore de exibições de cada controlador é recolhida em um único arquivo. O efeito final é que um aplicativo compilado por bytecode é mais rápido, porque não há mais análise de modelos ou substituições de string que ocorrem no tempo de execução.
- Pacote compilado. Esta opção está presente apenas para aplicativos compilados por código de bytes. Permite empacotar a aplicação sem código fonte para distribuição como fonte fechada. Note que o Python (como qualquer outra linguagem de programação) pode tecnicamente ser descompilado; portanto, a compilação não fornece proteção completa do código-fonte. No entanto, a descompilação pode ser difícil e pode ser ilegal.
- Remova compilado. Ele simplesmente remove os modelos, visualizações e controladores compilados por código de bytes do aplicativo. Se o aplicativo foi empacotado com código-fonte ou editado localmente, não há nenhum dano em remover os arquivos compilados por bytecode, e o aplicativo continuará a funcionar. Se o aplicativo foi instalado de um arquivo compilado compactado, isso não é seguro, porque não há código-fonte para o qual reverter e o aplicativo não funcionará mais.
Toda a funcionalidade disponível na página do site de administração web2py também pode ser acessada programaticamente por meio da API definida no módulo
gluon/admin.py
. Simplesmente abra um shell python e importe este módulo.
Se o SDK do Google App Engine estiver instalado, a página "site" do administrador mostrará um botão para enviar seus aplicativos ao GAE. E se python-git
está instalado, há também um botão para enviar seu aplicativo para o Open Shift. Para instalar aplicativos em Heroku
ou outro sistema de hospedagem, você deve procurar na pasta "scripts" do script apropriado.
Sobre about license
A guia about permite editar a descrição do aplicativo e sua licença. Estes são escritos respectivamente nos arquivos ABOUT e LICENSE na pasta do aplicativo.
Você pode usar MARKMIN
ou gluon.contrib.markdown.WIKI
sintaxe para esses arquivos, conforme descrito na ref. [markdown2] .
Design
Você usou a página editar já neste capítulo. Aqui queremos destacar mais algumas funcionalidades da página editar .
- Se você clicar em qualquer nome de arquivo, poderá ver o conteúdo do arquivo com realce de sintaxe.
- Se você clicar em editar, você pode editar o arquivo através de uma interface web.
- Se você clicar em excluir, você pode excluir o arquivo (permanentemente).
- Se você clicar em teste, o web2py executará testes. Testes são escritos pelo desenvolvedor usando doctests em Python, e cada função deve ter seus próprios testes.
- Você pode adicionar arquivos de idioma, digitalizar o aplicativo para descobrir todas as seqüências de caracteres e editar traduções de string através da interface web.
- Se os arquivos estáticos estiverem organizados em pastas e subpastas, a hierarquia de pastas poderá ser alternada clicando no nome de uma pasta.
A imagem abaixo mostra a saída da página de teste para o aplicativo de boas-vindas.
A imagem abaixo mostra a guia de idiomas do aplicativo de boas-vindas.
A imagem abaixo mostra como editar um arquivo de idioma, neste caso, o idioma "it" (italiano) para o aplicativo de boas-vindas.
Depurador integrado baseado na web
(requer o Python 2.7 ou posterior)
O administrador do web2py inclui um depurador baseado na web.
Usando o editor baseado na web fornecido, você pode adicionar pontos de interrupção ao código Python e, a partir do console do depurador associado, é possível inspecionar as variáveis do sistema nesses pontos de interrupção e retomar a execução. Isso é ilustrado na seguinte captura de tela: O console interativo também serve como um rascunho de python.
Essa funcionalidade é baseada no depurador Qdb criado por Mariano Reingart. Ele usa multiprocessamento.conexão para se comunicar entre o back-end e frontend, com um protocolo de fluxo semelhante a JSON-RPC. [qdb]
Definindo pontos de interrupção por meio de código
Inclua isto:
from gluon.debug import dbg
e para colocar no depurador, coloque isso no local desejado:
dbg.set_trace()
O aplicativo depurador tem um gerenciador de ponto de interrupção.
Notas: web2py não sabe se você realmente tem uma janela de depuração aberta no seu navegador; execução suspende independentemente. Os IDEs geralmente têm seu próprio depurador entre processos, por exemplo PyCharm ou PyDev. Eles podem reclamar se você incluir a biblioteca de glúons.
Shell Python baseado na Web
Se você clicar no link "shell" sob a aba controllers em edit , o web2py irá abrir um shell Python baseado na web e irá executar os modelos para o aplicativo atual. Isso permite que você fale interativamente com seu aplicativo.
Tenha cuidado ao usar o shell baseado na web - porque diferentes solicitações de shell serão executadas em diferentes threads. Isso facilmente dá erros, especialmente se você joga com bancos de dados criação e conexões. Para atividades como essas (ou seja, se você precisar de persistência) é muito melhor usar a linha de comando do python.
Crontab
Também sob a aba controllers em edit existe um link "crontab". Ao clicar neste link, você poderá editar o arquivo crontab web2py. Isso segue a mesma sintaxe do crontab do Unix, mas não depende do Unix. Na verdade, requer apenas web2py e funciona no Windows. Ele permite registrar ações que precisam ser executadas em segundo plano em horários programados. Para mais informações sobre isso, veja o próximo capítulo.
Erros
Ao programar o web2py, você inevitavelmente cometerá erros e apresentará bugs. O web2py ajuda de duas maneiras: 1) permite criar testes para todas as funções que podem ser executadas no navegador a partir da página editar ; e 2) quando um erro se manifesta, um ticket é emitido para o visitante e o erro é registrado.
Intencionalmente introduza um erro no aplicativo de imagens como mostrado abaixo:
def index():
images = db().select(db.image.ALL, orderby=db.image.title)
1/0
return dict(images=images)
Quando você acessa a ação de índice, recebe o seguinte ticket:
Apenas o administrador pode acessar o ticket:
O ticket mostra o traceback e o conteúdo do arquivo que causou o problema e o estado completo do sistema (variáveis, solicitação, sessão etc.). Se o erro ocorrer em uma exibição, o web2py mostrará a exibição convertida de HTML em Python código. Isso permite identificar facilmente a estrutura lógica do arquivo.
Por padrão, os tickets são armazenados no sistema de arquivos e exibidos agrupados por traceback. A interface administrativa fornece uma visão agregada (tipo de traceback e número de ocorrência) e uma visão detalhada (todos os tickets são listados pelo id do ticket). O administrador pode alternar entre as duas visualizações.
Observe que em todos os lugares admin mostra código destacado de sintaxe (por exemplo, em relatórios de erros, as palavras-chave web2py são mostradas em laranja). Se você clicar em uma palavra-chave web2py, será redirecionado para uma página de documentação sobre a palavra-chave.
Se você corrigir o erro de divisão por zero na ação de índice e introduzir um na exibição de índice:
{{extend 'layout.html'}}
<h1>Current Images</h1>
<ul>
{{for image in images:}}
{{1/0}}
{{=LI(A(image.title, _href=URL("show", args=image.id)))}}
{{pass}}
</ul>
você recebe o seguinte bilhete:
Observe que o web2py converteu a visualização do HTML em um arquivo Python, e o erro descrito no ticket se refere ao código Python gerado e NÃO ao arquivo de exibição original:
Isso pode parecer confuso no início, mas, na prática, torna a depuração mais fácil, porque o recuo do Python destaca a estrutura lógica do código que você embutiu nas visualizações.
O código é mostrado na parte inferior da mesma página.
Todos os tickets estão listados sob admin na página erros de cada aplicativo:
Mercurial
Se você estiver executando a partir do código-fonte, a interface administrativa mostrará mais um item de menu chamado "Versionamento".
Inserir um comentário e pressionar o botão "confirmar" na página resultante confirmará o aplicativo atual. Com o primeiro commit, um repositório local do Mercurial para o específico aplicativo será criado. Sob o capô, o Mercurial armazena informações sobre as alterações feitas em seu código em uma pasta oculta ".hg" na subpasta do aplicativo. Cada aplicativo tem sua própria pasta ".hg" e seu próprio arquivo ".hgignore" (informa ao Mercurial quais arquivos ignorar). Para usar este recurso, você deve ter as bibliotecas de controle de versão do Mercurial instaladas (pelo menos a versão 1.9):
pip install mercurial
A interface web do Mercurial permite que você navegue pelos arquivos anteriores de commit e diff, mas nós recomendamos que você use o Mercurial diretamente do shell ou de um dos muitos clientes do Mercurial baseados em GUI, já que eles são mais poderosos. Por exemplo, eles permitirão que você sincronize seu aplicativo com um repositório de origem remoto.
Você pode ler mais sobre o Mercurial aqui:
http://mercurial.selenic.com/
Integração Git
O aplicativo administrativo também inclui integração com o git. São necessárias bibliotecas git Python, por ex.
pip install gitpython
e, em seguida, por aplicativo, você deve clonar ou configurar um repositório git.
Após essas etapas, o menu Gerenciar para cada aplicativo gerenciado pelo git mostrará git push e git pull. Aplicativos que não são gerenciados pelo git são ignorados. Você pode extrair e enviar aplicativos do repositório remoto padrão.
Assistente de Aplicação (experimental)
o admin A interface inclui um Assistente que pode ajudá-lo a criar novos aplicativos. Você pode acessar o assistente na página "site", como mostra a imagem abaixo.
O assistente irá guiá-lo através de uma série de etapas envolvidas na criação de um novo aplicativo:
- Escolha um nome para o aplicativo
- Configure o aplicativo e escolha os plugins necessários
- Construir modelos necessários (criará páginas CRUD para cada modelo)
- Permite que você edite as visualizações dessas páginas usando a sintaxe MARKMIN
A imagem abaixo mostra o segundo passo do processo.
Você pode ver uma lista suspensa para selecionar um plugin de layout (de web2py.com/layouts
), um menu suspenso de múltipla escolha para verificar outros plugins (de web2py.com/plugins
) e um campo "login config" onde colocar o Janrain "domain: key".
Os outros passos são bastante auto-explicativos.
O Assistente funciona bem para o que faz, mas é considerado um "recurso experimental" por dois motivos:
- Aplicativos criados com o assistente e editados manualmente, não podem ser modificados posteriormente pelo assistente.
- A interface do assistente mudará com o tempo para incluir suporte para mais recursos e desenvolvimento visual mais fácil.
Em qualquer caso, o assistente é uma ferramenta útil para prototipagem rápida e pode ser usado para inicializar um novo aplicativo com um layout alternativo e plugins opcionais.
Configurando admin
Normalmente não há necessidade de realizar qualquer configuração de admin mas algumas personalizações são possíveis. Depois de fazer login no administrador, você pode editar o arquivo de configuração do administrador por meio do URL:
http://127.0.0.1:8000/admin/default/edit/admin/models/0.py
Notar que admin pode ser usado para editar a si mesmo. de fato admin é um aplicativo como qualquer outro.
O arquivo "0.py" é mais ou menos auto documentado, enfim, aqui estão algumas das personalizações possíveis mais importantes:
GAE_APPCFG = os.path.abspath(os.path.join('/usr/local/bin/appcfg.py'))
Isso deve apontar para o local do arquivo "appcfg.py" que acompanha o SDK do Google App Engine. Se você tiver o SDK, poderá alterar esses parâmetros de configuração para o valor correto. Ele permitirá que você implante no GAE a partir da interface administrativa.
Você também pode configurar o admin do web2py no modo de demonstração:
DEMO_MODE = True
FILTER_APPS = ['welcome']
E apenas os aplicativos listados em FILTER_APPS estarão acessíveis e só estarão acessíveis no modo somente leitura.
Se você é um professor e quer expor a interface administrativa aos alunos para que os alunos possam compartilhar uma interface administrativa para seus projetos (pense em um laboratório virtual), pode fazê-lo configurando:
MULTI_USER_MODE = True
Desta forma, os alunos serão obrigados a fazer o login e só poderão acessar seus próprios aplicativos via admin. Você, como primeiro usuário/professor, poderá acessar todos eles.
No modo multiusuário, você pode registrar alunos usando o link "registro em massa" em admin e gerenciá-los usando o link "gerenciar alunos". O sistema também rastreia quando os alunos fazem login e quantas linhas de código eles adicionam/removem de/para seu código. Esses dados são apresentados ao administrador como gráficos na página "about" do aplicativo.
Lembre-se de que esse mecanismo ainda pressupõe que todos os usuários são confiáveis. Todos os aplicativos criados sob o administrador são executados sob as mesmas credenciais no mesmo sistema de arquivos. É possível que um aplicativo criado por um aluno acesse os dados e a origem de um aplicativo criado por outro aluno. Também é possível que um aluno crie um aplicativo que bloqueie o servidor.
Móvel admin
Observe que o aplicativo admin inclui o "plugin_jqmobile", que contém os pacotes do jQuery Mobile. Quando o administrador é acessado de um dispositivo móvel, isso é detectado pelo web2py e a interface é exibida usando um layout otimizado para dispositivos móveis:
Mais sobre appadmin
appadmin não se destina a ser exposto ao público. Ele é projetado para ajudá-lo, fornecendo um acesso fácil ao banco de dados. Ele consiste em apenas dois arquivos: um controlador "appadmin.py" e uma visualização "appadmin.html", que são usados por todas as ações no controlador.
o appadmin controlador é relativamente pequeno e legível; Ele fornece um exemplo de design de uma interface de banco de dados.
appadmin mostra quais bancos de dados estão disponíveis e quais tabelas existem em cada banco de dados. Você pode inserir registros e listar todos os registros para cada tabela individualmente. appadmin pagina 100 registros de saída de cada vez.
Depois que um conjunto de registros é selecionado, o cabeçalho das páginas é alterado, permitindo que você atualize ou exclua os registros selecionados.
Para atualizar os registros, insira uma atribuição de SQL no campo Sequência de consulta:
title = 'test'
onde valores de cadeia devem ser colocados entre aspas simples. Vários campos podem ser separados por vírgulas.
Para excluir um registro, clique na caixa de seleção correspondente para confirmar que você tem certeza.
appadmin Também pode executar associações se a consulta contiver uma condição SQL que envolva duas ou mais tabelas. Por exemplo, tente:
db.image.id == db.post.image_id
O web2py passa isso para o DAL e entende que a consulta vincula duas tabelas; portanto, ambas as tabelas são selecionadas com um INNER JOIN. Aqui está a saída:
Se você clicar no número de um campo de ID, você receberá uma página de edição para o registro com o ID correspondente.
Se você clicar no número de um campo de referência, você receberá uma página de edição para o registro referenciado.
Você não pode atualizar ou excluir linhas selecionadas por uma junção, pois elas envolvem registros de várias tabelas e isso seria ambíguo.
Além de suas capacidades de administração de banco de dados, appadmin também permite visualizar detalhes sobre o conteúdo do aplicativo cache
(em /yourapp/appadmin/cache
), bem como o conteúdo da corrente request
, response
e session
objetos (em /yourapp/appadmin/state
).
appadmin substitui response.menu
com seu próprio menu, que fornece links para o aplicativo editar página em admin, a db (administração de banco de dados), o Estado página e esconderijo página. Se o layout do seu aplicativo não gerar um menu usando response.menu
, então você não verá o appadmin cardápio. Nesse caso, você pode modificar o arquivo appadmin.html e adicionar {{=MENU(response.menu)}}
para exibir o menu.