Chapter 4: O núcleo
O nucleo
Opções de linha de comando
É possível pular a GUI e iniciar o web2py diretamente da linha de comando, digitando algo como:
python web2py.py -a 'your password' -i 127.0.0.1 -p 8000
Quando o web2py é iniciado, ele cria um arquivo chamado "parameters_8000.py", onde armazena a senha com hash. Se você usar "<ask>" como senha, o web2py solicitará a senha.
Para segurança adicional, você pode iniciar o web2py com:
python web2py.py -a '<recycle>' -i 127.0.0.1 -p 8000
Nesse caso, o web2py reutiliza a senha com hash armazenada anteriormente. Se nenhuma senha for fornecida ou se o arquivo "parameters_8000.py" for excluído, a interface administrativa baseada na Web será desativada.
Em alguns sistemas Unix/Linux, se a senha for
<pam_user:some_user>
O web2py usa a senha do PAM da conta do sistema operacional do some_user
para autenticar o administrador, a menos que seja bloqueado pela configuração do PAM.
O web2py normalmente roda com CPython (a implementação C do interpretador Python criado por Guido van Rossum), mas também pode ser executado com PyPy e Jython. A última possibilidade permite o uso de web2py no contexto de uma infraestrutura Java EE. Para usar o Jython, simplesmente substitua "python web2py.py ..." por "jython web2py.py". Detalhes sobre a instalação do Jython, os módulos zxJDBC necessários para acessar os bancos de dados podem ser encontrados no Capítulo 14.
O script "web2py.py" pode ter muitos argumentos de linha de comando especificando o número máximo de threads, a ativação do SSL, etc. Para um tipo de lista completo:
>>> python web2py.py -h
Usage: python web2py.py
web2py Web Framework startup script. ATTENTION: unless a password
is specified (-a 'passwd'), web2py will attempt to run a GUI.
In this case command line options are ignored.
Options:
--version show program's version number and exit
-h, --help show this help message and exit
-i IP, --ip=IP IP address of the server (e.g., 127.0.0.1 or ::1);
Note: This value is ignored when using the
'interfaces' option.
-p PORT, --port=PORT port of server (8000)
-a PASSWORD, --password=PASSWORD
password to be used for administration (use -a
"<recycle>" to reuse the last password))
-c SSL_CERTIFICATE, --ssl_certificate=SSL_CERTIFICATE
file that contains ssl certificate
-k SSL_PRIVATE_KEY, --ssl_private_key=SSL_PRIVATE_KEY
file that contains ssl private key
--ca-cert=SSL_CA_CERTIFICATE
Use this file containing the CA certificate to
validate X509 certificates from clients
-d PID_FILENAME, --pid_filename=PID_FILENAME
file to store the pid of the server
-l LOG_FILENAME, --log_filename=LOG_FILENAME
file to log connections
-n NUMTHREADS, --numthreads=NUMTHREADS
number of threads (deprecated)
--minthreads=MINTHREADS
minimum number of server threads
--maxthreads=MAXTHREADS
maximum number of server threads
-s SERVER_NAME, --server_name=SERVER_NAME
server name for the web server
-q REQUEST_QUEUE_SIZE, --request_queue_size=REQUEST_QUEUE_SIZE
max number of queued requests when server unavailable
-o TIMEOUT, --timeout=TIMEOUT
timeout for individual request (10 seconds)
-z SHUTDOWN_TIMEOUT, --shutdown_timeout=SHUTDOWN_TIMEOUT
timeout on shutdown of server (5 seconds)
--socket-timeout=SOCKET_TIMEOUT
timeout for socket (5 second)
-f FOLDER, --folder=FOLDER
location of the applications folder (also known as directory)
-v, --verbose increase --test verbosity
-Q, --quiet disable all output
-D DEBUGLEVEL, --debug=DEBUGLEVEL
set debug output level (0-100, 0 means all, 100 means
none; default is 30)
-S APPNAME, --shell=APPNAME
run web2py in interactive shell or IPython (if
installed) with specified appname (if app does not
exist it will be created). APPNAME like a/c/f (c,f
optional)
-B, --bpython run web2py in interactive shell or bpython (if
installed) with specified appname (if app does not
exist it will be created). Use combined with --shell
-P, --plain only use plain python shell; should be used with
--shell option
-M, --import_models auto import model files; default is False; should be
used with --shell option
-R PYTHON_FILE, --run=PYTHON_FILE
run PYTHON_FILE in web2py environment; should be used
with --shell option
-K SCHEDULER, --scheduler=SCHEDULER
run scheduled tasks for the specified apps: expects a
list of app names as -K app1,app2,app3 or a list of
app:groups as -K app1:group1:group2,app2:group1 to
override specific group_names. (only strings, no
spaces allowed. Requires a scheduler defined in the
models
-X, --with-scheduler run schedulers alongside webserver
-T TEST_PATH, --test=TEST_PATH
run doctests in web2py environment; TEST_PATH like
a/c/f (c,f optional)
-C, --cron trigger a cron run manually; usually invoked from a
system crontab
--softcron triggers the use of softcron
-Y, --run-cron start the background cron process
-J, --cronjob identify cron-initiated command
-L CONFIG, --config=CONFIG
config file
-F PROFILER_DIR, --profiler=PROFILER_DIR
profiler dir
-t, --taskbar use web2py gui and run in taskbar (system tray)
--nogui text-only, no GUI
-A ARGS, --args=ARGS should be followed by a list of arguments to be passed
to script, to be used with -S, -A must be the last
option
--no-banner Do not print header banner
--interfaces=INTERFACES
listen on multiple addresses: "ip1:port1:key1:cert1:ca
_cert1;ip2:port2:key2:cert2:ca_cert2;..."
(:key:cert:ca_cert optional; no spaces; IPv6 addresses
must be in square [] brackets)
--run_system_tests runs web2py tests
Observe:
- A opção
-W
, usado para instalar um serviço do Windows, foi removido. Por favor, veja nssm in the Deployment Recipes chapter- A saída do perfilador pode ser analisada usando
runsnakerun
ferramenta
As opções de minúsculas são usadas para configurar o servidor da web. o -L
opção diz ao web2py para ler as opções de configuração de um arquivo, enquanto -S
, -P
e -M
opções iniciam um shell interativo do Python. o -T
A opção localiza e executa doctests de controlador em um ambiente de execução web2py. Por exemplo, o exemplo a seguir executa doctests de todos os controladores no aplicativo "welcome":
python web2py.py -vT welcome
Fluxo de trabalho
O fluxo de trabalho web2py é o seguinte:
- Uma requisição HTTP chega ao servidor web (o servidor Rocket embutido ou um servidor diferente conectado ao web2py via WSGI ou outro adaptador). O servidor da web manipula cada solicitação em seu próprio encadeamento, em paralelo.
- O cabeçalho da solicitação HTTP é analisado e passado para o dispatcher (explicado mais adiante neste capítulo).
- O dispatcher decide qual aplicativo instalado manipulará a solicitação e mapeará o PATH_INFO na URL em uma chamada de função. Cada URL corresponde a uma chamada de função.
- As solicitações de arquivos na pasta estática são tratadas diretamente e arquivos grandes são automaticamente transmitidos para o cliente.
- Pedidos para qualquer coisa, exceto um arquivo estático, são mapeados em uma ação (ou seja, uma função em um arquivo de controle, no aplicativo solicitado).
- Antes de chamar a ação, algumas coisas acontecem: se o cabeçalho da solicitação contiver um cookie de sessão para o aplicativo, o objeto da sessão será recuperado; se não, um id de sessão é criado (mas o arquivo de sessão não é salvo até mais tarde); um ambiente de execução para o pedido é criado; modelos são executados neste ambiente.
- Finalmente, a ação do controlador é executada no ambiente pré-construído.
- Se a ação retornar uma string, ela será retornada ao cliente (ou se a ação retornar um objeto auxiliar web2py HTML, ela será serializada e retornada ao cliente).
- Se a ação retornar um iterável, isso é usado para fazer um loop e transmitir os dados para o cliente.
- Se a ação retornar um dicionário, o web2py tentará localizar uma visualização para renderizar o dicionário. A exibição deve ter o mesmo nome da ação (a menos que seja especificado de outra forma) e a mesma extensão da página solicitada (o padrão é .html); em caso de falha, o web2py pode pegar uma visão genérica (se disponível e ativada). A visão vê todas as variáveis definidas nos modelos, bem como aquelas no dicionário retornado pela ação, mas não vê variáveis globais definidas no controlador.
- O código de usuário inteiro é executado em uma única transação de banco de dados, a menos que seja especificado de outra forma.
- Se o código do usuário for bem-sucedido, a transação será confirmada.
- Se o código do usuário falhar, o traceback será armazenado em um ticket e um ID de ticket será emitido para o cliente. Apenas o administrador do sistema pode pesquisar e ler os tracebacks em tickets.
Há algumas ressalvas a serem lembradas:
- Modelos na mesma pasta/subpasta são executados em ordem alfabética.
- Qualquer variável definida em um modelo será visível para outros modelos seguindo em ordem alfabética, para os controladores e para as visualizações. models_to_runconditional models
Modelos condicionais
- Modelos em subpastas são executados condicionalmente com base no controlador em uso. Isso evita o processamento de todas as definições de tabela em cada solicitação. Por exemplo, se o usuário solicitou "/ a/c/f" onde "a" é o aplicativo, "c" é o controlador e "f" é a função (ação), então os seguintes modelos são executados:
applications/a/models/*.py
applications/a/models/c/*.py
applications/a/models/c/f/*.py
Esse comportamento é imposto por padrão. Alterando o response.models_to_run
lista regex, você pode forçar o comportamento desejado. Olhe para response para detalhes adicionais
- O controlador solicitado é executado e a função solicitada é chamada. Isso significa que todo o código de nível superior no controlador também é executado em todas as solicitações desse controlador.
- A visualização só é chamada se a ação retornar um dicionário.
- Se uma visão não for encontrada, o web2py tentará usar uma visão genérica. Por padrão, as visualizações genéricas são desativadas, embora o aplicativo 'welcome' inclua uma linha em /models/db.py para ativá-las apenas no host local. Eles podem ser ativados por tipo de extensão e por ação (usando
response.generic_patterns
). Em geral, as visualizações genéricas são uma ferramenta de desenvolvimento e normalmente não devem ser usadas na produção. Se você quiser que algumas ações usem uma visão genérica, liste essas ações emresponse.generic_patterns
(discutido com mais detalhes no capítulo sobre Serviços).
Os possíveis comportamentos de uma ação são os seguintes:
Retornar uma string
def index():
return 'data'
Retornar um dicionário para uma visão:
def index():
return dict(key='value')
Retornar todas as variáveis locais:
def index():
return locals()
Redirecionar o usuário para outra página:
def index():
redirect(URL('other_action'))
Retornar uma página HTTP diferente de "200 OK":
def index():
raise HTTP(404)
Devolva um ajudante (por exemplo, um FORMULÁRIO):
def index():
return FORM(INPUT(_name='test'))
(isto é usado principalmente para callbacks e componentes do Ajax, veja o capítulo 12)
Quando uma ação retorna um dicionário, ela pode conter código gerado por ajudantes, incluindo formulários baseados em tabelas de banco de dados ou formulários de uma fábrica, por exemplo:
def index():
return dict(form=SQLFORM.factory(Field('name')).process())
(todos os formulários gerados pelo web2py usam postbacks, veja o capítulo 3)
Despachando url mapping dispatching
web2py mapeia uma URL do formulário:
http://127.0.0.1:8000/a/c/f.html
para a função f()
no controlador "c.py" no aplicativo "a". E se f
não está presente, web2py é padronizado para o index
função do controlador. E se c
não está presente, o web2py é padronizado para o controlador "default.py", e se a
não está presente, web2py é padronizado para o init
aplicação. Se não há init
aplicação, web2py tenta executar o welcome
aplicação. Isso é mostrado esquematicamente na imagem abaixo:
Por padrão, qualquer nova solicitação também cria uma nova sessão. Além disso, um cookie de sessão é retornado ao navegador do cliente para acompanhar a sessão.
A extensão .html
é opcional; .html
é assumido como padrão. A extensão determina a extensão da visualização que processa a saída da função do controlador f()
. Ele permite que o mesmo conteúdo seja exibido em vários formatos (html, xml, json, rss, etc.).
As funções que recebem argumentos ou começam com um sublinhado duplo não são expostas publicamente e só podem ser chamadas por outras funções.
Há uma exceção feita para URLs do formulário:
http://127.0.0.1:8000/a/static/filename
Não há controlador chamado "estático". O web2py interpreta isso como um pedido para o arquivo chamado "filename" na subpasta "static" do aplicativo "a".
O web2py também suporta o protocolo IF_MODIFIED_SINCE e não envia o arquivo se já estiver armazenado no cache do navegador e se o arquivo não foi alterado desde a versão.
Ao vincular a um arquivo de áudio ou vídeo na pasta estática, se você quiser forçar o navegador a baixar o arquivo em vez de transmitir o áudio/vídeo por meio de um media player, adicione ?attachment
para o URL. Isso diz ao web2py para definir Content-Disposition
cabeçalho da resposta HTTP para "anexo". Por exemplo:
<a href="/app/static/my_audio_file.mp3?attachment">Download</a>
Quando o link acima é clicado, o navegador solicitará que o usuário baixe o arquivo MP3 em vez de imediatamente transmitir o áudio. (Como discutido below , você também pode definir cabeçalhos de resposta HTTP diretamente atribuindo um dict
de nomes de cabeçalho e seus valores para response.headers
.)
http://127.0.0.1:8000/a/c/f.html/x/y/z?p=1&q=2
funcionar f
no controlador "c.py" na aplicação a
e armazena os parâmetros de URL no request
variável da seguinte forma:
request.args = ['x', 'y', 'z']
e:
request.vars = {'p': 1, 'q': 2}
e:
request.application = 'a'
request.controller = 'c'
request.function = 'f'
No exemplo acima, ambos request.args[i]
e request.args(i)
pode ser usado para recuperar o i-ésimo elemento do request.args
, mas enquanto o primeiro gera uma exceção se a lista não tiver esse índice, o último retorna Nenhum neste caso.
request.url
armazena o URL completo da solicitação atual (não incluindo variáveis GET).
request.ajax
O padrão é False, mas é True se web2py determinar que a ação foi chamada por uma solicitação Ajax.
Se a solicitação for uma solicitação Ajax e for iniciada por um componente web2py, o nome do componente poderá ser encontrado em:
request.cid
Os componentes são discutidos em mais detalhes no Capítulo 12.
request.env.request_method
está definido para "GET"; se for um POST, request.env.request_method
está definido para "POST". Variáveis de consulta de URL são armazenadas em request.get_vars
. request.post_vars
contém todos os parâmetros passados para o corpo de uma solicitação (geralmente um POST, PUT ou DELETE). o request.vars
Dicionário de armazenamento contém ambos ( get_vars
e post_vars
se fundir)web2py armazena variáveis de ambiente WSGI e web2py em request.env
, por exemplo:
request.env.path_info = 'a/c/f'
e cabeçalhos HTTP em variáveis de ambiente, por exemplo:
request.env.http_host = '127.0.0.1:8000'
Observe que o web2py valida todas as URLs para impedir ataques de passagem de diretórios.
As URLs só podem conter caracteres alfanuméricos, sublinhados e barras. a args
pode conter pontos não consecutivos. Espaços são substituídos por sublinhados antes da validação. Se a sintaxe da URL for inválida, web2py retornará uma mensagem de erro HTTP 400 [http-w] [http-o] .
Se o URL corresponder a um pedido de um arquivo estático, o web2py simplesmente lê e retorna (transmite) o arquivo solicitado.
Se o URL não solicitar um arquivo estático, o web2py processará o pedido na seguinte ordem:
- Analisa os cookies.
- Cria um ambiente no qual executar a função.
- inicializa
request
,response
,cache
. - Abre o existente
session
ou cria um novo. - Executa os modelos pertencentes ao aplicativo solicitado.
- Executa a função de ação do controlador solicitada.
- Se a função retornar um dicionário, executa a visão associada.
- No sucesso, compromete todas as transações abertas.
- Salva a sessão.
- Retorna uma resposta HTTP.
Observe que o controlador e a visão são executados em cópias diferentes do mesmo ambiente; portanto, a visualização não vê o controlador, mas vê os modelos e vê as variáveis retornadas pela função de ação do controlador.
Se uma exceção (diferente de HTTP) for gerada, o web2py faz o seguinte:
- Armazena o traceback em um arquivo de erro e atribui um número de ticket a ele.
- Retrocede todas as transações abertas do banco de dados.
- Retorna uma página de erro informando o número do ticket.
Se a exceção é um HTTP
exceção, este é assumido como sendo o comportamento pretendido (por exemplo, um HTTP
redirecionar), e todas as transações abertas do banco de dados são confirmadas. O comportamento depois disso é especificado pelo HTTP
exceção em si. o HTTP
A classe de exceção não é uma exceção padrão do Python; é definido pelo web2py.
Bibliotecas
As bibliotecas web2py são expostas aos aplicativos do usuário como objetos globais. Por exemplo ( request
, response
, session
, cache
), classes (ajudantes, validadores, DAL API) e funções ( T
e redirect
).
Esses objetos são definidos nos seguintes arquivos principais:
web2py.py
gluon/__init__.py gluon/highlight.py gluon/restricted.py gluon/streamer.py
gluon/admin.py gluon/html.py gluon/rewrite.py gluon/template.py
gluon/cache.py gluon/http.py gluon/rocket.py gluon/storage.py
gluon/cfs.py gluon/import_all.py gluon/sanitizer.py gluon/tools.py
gluon/compileapp.py gluon/languages.py gluon/serializers.py gluon/utils.py
gluon/contenttype.py gluon/main.py gluon/settings.py gluon/validators.py
gluon/dal.py gluon/myregex.py gluon/shell.py gluon/widget.py
gluon/decoder.py gluon/newcron.py gluon/sql.py gluon/winservice.py
gluon/fileutils.py gluon/portalocker.py gluon/sqlhtml.py gluon/xmlrpc.py
gluon/globals.py gluon/reserved_sql_keywords.py
Observe que muitos desses módulos, especificamente
dal
(a camada de abstração de banco de dados),template
(o idioma do modelo),rocket
(o servidor da web) ehtml
(os ajudantes) não têm dependências e podem ser usados fora do web2py.
O aplicativo de andaimes gzip que vem com web2py é
welcome.w2p
Isso é criado na instalação e sobregravado na atualização.
Na primeira vez que você inicia o web2py, duas novas pastas são criadas: depósito e aplicativos. A pasta de depósito é usada como armazenamento temporário para instalar e desinstalar aplicativos.
A primeira vez que você inicia o web2py e depois de uma atualização, o aplicativo "welcome" é compactado em um arquivo "welcome.w2p" para ser usado como um aplicativo de andaimes.
Quando o web2py é atualizado, ele vem com um arquivo chamado "NEWINSTALL". Se o web2py encontrar este arquivo, ele entende que uma atualização foi executada, portanto, ele remove o arquivo e cria um novo "welcome.w2p".
A versão atual do web2py é armazenada no campo "VERSION" e segue a notação padrão de versão semântica, onde o ID de construção é o registro de data e hora da compilação.
testes unitários web2py estão em
gluon/tests/
Existem manipuladores para conexão com vários servidores da web:
cgihandler.py # discouraged
gaehandler.py # for Google App Engine
fcgihandler.py # for FastCGI
wsgihandler.py # for WSGI
isapiwsgihandler.py # for IIS
modpythonhandler.py # deprecated
("fcgihandler" chama de "gluon/contrib/gateways/fcgi.py" desenvolvido por Allan Saddi) e
anyserver.py
que é um script para fazer interface com diversos servidores da Web, descritos no Capítulo 13.
Existem três arquivos de exemplo no diretório "exemplos":
options_std.py
routes.parametric.example.py
routes.patterns.example.py
Eles são todos feitos para serem copiados para o diretório raiz (onde web2py.py ou web2py.exe é) e editados de acordo com suas próprias preferências. O primeiro é um arquivo de configuração opcional que pode ser passado para web2py.py com o -L
opção. O segundo é um exemplo de um arquivo de mapeamento de URL. É carregado automaticamente quando renomeado "routes.py". O terceiro é uma sintaxe alternativa para o mapeamento de URL e também pode ser renomeado (ou copiado para) "routes.py".
Os arquivos
app.example.yaml
queue.example.yaml
são arquivos de configuração de exemplo usados para implantação no Google App Engine. Você pode ler mais sobre eles no capítulo Receitas de implantação e nas páginas de documentação do Google.
Existem também bibliotecas adicionais, algumas desenvolvidas por terceiros:
feedparser [feedparser] por Mark Pilgrim para ler feeds RSS e Atom:
gluon/contrib/__init__.py
gluon/contrib/feedparser.py
markdown2 [markdown2] por Trent Mick para marcação wiki:
gluon/contrib/markdown/__init__.py
gluon/contrib/markdown/markdown2.py
markmin marcação:
gluon/contrib/markmin
(Vejo MARKMIN syntax para mais)
fpdf criei o meu Mariano Reingart para gerar documentos PDF:
gluon/contrib/fpdf
Isso não está documentado neste livro, mas está hospedado e documentado aqui:
http://code.google.com/p/pyfpdf/
pysimplesoap é uma implementação de servidor SOAP leve criada por Mariano Reingart:
gluon/contrib/pysimplesoap/
simplejsonrpc é um cliente JSON-RPC leve também criado por Mariano Reingart:
gluon/contrib/simplejsonrpc.py
memcache [memcache] API do Python por Evan Martin:
gluon/contrib/memcache/__init__.py
gluon/contrib/memcache/memcache.py
redis_cache
gluon/contrib/redis_cache.py
gql, uma porta do DAL para o Google App Engine:
gluon/contrib/gql.py
memdb, uma porta do DAL em cima do memcache:
gluon/contrib/memdb.py
gae_memcache é uma API para usar o memcache no Google App Engine:
gluon/contrib/gae_memcache.py
pirtf [pyrtf] para gerar documentos Rich Text Format (RTF), desenvolvidos por Simon Cusack e revisados por Grant Edwards:
gluon/contrib/pyrtf/
PyRSS2Gen [pyrss2gen] desenvolvido pela Dalke Scientific Software, para gerar feeds RSS:
gluon/contrib/rss2.py
simplejson [simplejson] por Bob Ippolito, a biblioteca padrão para analisar e escrever objetos JSON:
gluon/contrib/simplejson/
Google Wallet [googlewallet] fornece botões "pague agora" que vinculam o Google como processador de pagamento:
gluon/contrib/google_wallet.py
Stripe.com [stripe] fornece uma API simples para aceitar pagamentos com cartão de crédito:
gluon/contrib/stripe.py
AutorizeNet [authorizenet] fornece API para aceitar pagamentos com cartão de crédito via rede Authorize.net
gluon/contrib/AuthorizeNet.py
Dowcommerce [dowcommerce] API de processamento de cartão de crédito:
gluon/contrib/DowCommerce.py
PaymentTech API de processamento de cartão de crédito:
gluon/contrib/paymentech.py
PAM [PAM] API de autenticação criada por Chris AtLee:
gluon/contrib/pam.py
Um classificador Bayesiano para preencher o banco de dados com dados fictícios para fins de teste:
gluon/contrib/populate.py
Um arquivo com API para execução no Heroku.com:
gluon/contrib/heroku.py
Um arquivo que permite interação com a barra de tarefas nas janelas, quando o web2py está sendo executado como um serviço:
gluon/contrib/taskbar_widget.py
Opcional login_methods e login_forms a ser usado para autenticação:
gluon/contrib/login_methods/__init__.py
gluon/contrib/login_methods/basic_auth.py
gluon/contrib/login_methods/browserid_account.py
gluon/contrib/login_methods/cas_auth.py
gluon/contrib/login_methods/dropbox_account.py
gluon/contrib/login_methods/email_auth.py
gluon/contrib/login_methods/extended_login_form.py
gluon/contrib/login_methods/gae_google_account.py
gluon/contrib/login_methods/ldap_auth.py
gluon/contrib/login_methods/linkedin_account.py
gluon/contrib/login_methods/loginza.py
gluon/contrib/login_methods/oauth10a_account.py
gluon/contrib/login_methods/oauth20_account.py
gluon/contrib/login_methods/oneall_account.py
gluon/contrib/login_methods/openid_auth.py
gluon/contrib/login_methods/pam_auth.py
gluon/contrib/login_methods/rpx_account.py
gluon/contrib/login_methods/x509_auth.py
web2py também contém uma pasta com scripts úteis, incluindo
scripts/setup-web2py-fedora.sh
scripts/setup-web2py-ubuntu.sh
scripts/setup-web2py-nginx-uwsgi-ubuntu.sh
scripts/setup-web2py-heroku.sh
scripts/update-web2py.sh
scripts/make_min_web2py.py
...
scripts/sessions2trash.py
scripts/sync_languages.py
scripts/tickets2db.py
scripts/tickets2email.py
...
scripts/extract_mysql_models.py
scripts/extract_pgsql_models.py
...
scripts/access.wsgi
scripts/cpdb.py
o setup-web2py-*
são particularmente úteis porque tentam uma instalação e configuração completas de um ambiente de produção web2py do zero. Algumas delas são discutidas no Capítulo 14, mas todas elas contêm uma string de documentação que explica seu propósito e uso.
Finalmente o web2py inclui esses arquivos necessários para construir as distribuições binárias.
Makefile
setup_exe.py
setup_app.py
Estes são scripts de configuração para py2exe e py2app, respectivamente, e eles são apenas obrigados a construir as distribuições binárias do web2py. VOCÊ NUNCA PRECISARÁ CORRES.
Os aplicativos web2py contêm arquivos adicionais, especialmente bibliotecas JavaScript de terceiros, como jQuery, calendário e Codemirror. Seus autores são reconhecidos nos próprios arquivos.
Aplicativos
Os aplicativos desenvolvidos no web2py são compostas das seguintes partes:
models/
modelos Descrevem uma representação dos dados como tabelas de banco de dados e relações entre tabelas.controllers/
controladores Descrevem a lógica e o fluxo de trabalho da aplicação.views/
visualizações Descrevem como os dados devem ser apresentados ao usuário usando HTML e JavaScript.languages/
línguas Descrevem como traduzir strings no aplicativo em vários idiomas suportados.static/
arquivos estáticos não requer processamento (por exemplo, imagens, folhas de estilo CSS, etc).- os arquivos
ABOUT
(SOBRE) eREADME
(LEIA ME) são autoexplicativos. errors/
` erros armazena relatórios de erros gerados pelo aplicativo.sessions/
sessões armazena informações relacionadas a cada usuário em particular.databases/
bancos de dados armazena bancos de dados SQLite e informações adicionais sobre tabelas.cache/
armazena itens de aplicativo em cache.modules/
módulos são outros módulos opcionais do Python.private/
privado os arquivos são acessados pelos controllers, mas não diretamente pelo desenvolvedor.uploads/
Os arquivos são acessados pelos models, mas não diretamente pelo desenvolvedor (por exemplo, arquivos enviados por usuários do aplicativo).tests/
Testes é um diretório para armazenar scripts de teste, fixtures e simulações.
Modelos, visualizações, controladores, idiomas e arquivos estáticos são acessíveis através da interface de administração da Web [design]. SOBRE, README e erros também são acessíveis através da interface de administração através dos itens de menu correspondentes. Sessões, cache, módulos e arquivos privados podem ser acessados pelos aplicativos, mas não pela interface de administração.
Tudo é ordenadamente organizado em uma estrutura de diretório clara que é replicada para cada aplicativo web2py instalado, embora o usuário nunca precise acessar o sistema de arquivos diretamente:
__init__.py ABOUT LICENSE models views
controllers modules private tests cron
cache errors upload sessions static
"__init__.py" é um arquivo vazio que é necessário para permitir que o Python (e web2py) importe os módulos no modules
diretório.
Observe que o aplicativo admin
simplesmente fornece uma interface da Web para aplicativos web2py no sistema de arquivos do servidor. Aplicativos web2py também podem ser criados e desenvolvidos a partir da linha de comando ou do seu editor de texto/IDE preferencial; você não precisa usar a interface admin no navegador. Um novo aplicativo pode ser criado manualmente replicando-se a estrutura de diretórios acima, por exemplo, "applications/newapp /" (ou simplesmente descompactar welcome.w2p
arquivo em seu novo diretório de aplicativos). Arquivos de aplicativo também podem ser criados e editados a partir da linha de comando sem ter que usar a interface web admin.
API
Modelos, controladores e visualizações são executados em um ambiente onde os seguintes objetos já são importados para nós:
Objetos Globais:
request, response, session, cache
Internacionalização:
T
Navegação:
redirect, HTTP
Assistentes:
XML, URL, BEAUTIFY
A, B, BODY, BR, CENTER, CODE, COL, COLGROUP,
DIV, EM, EMBED, FIELDSET, FORM, H1, H2, H3, H4, H5, H6,
HEAD, HR, HTML, I, IFRAME, IMG, INPUT, LABEL, LEGEND,
LI, LINK, OL, UL, META, OBJECT, OPTION, P, PRE,
SCRIPT, OPTGROUP, SELECT, SPAN, STYLE,
TABLE, TAG, TD, TEXTAREA, TH, THEAD, TBODY, TFOOT,
TITLE, TR, TT, URL, XHTML, xmlescape, embed64
CAT, MARKMIN, MENU, ON
Formulários e tabelas
SQLFORM (SQLFORM.factory, SQLFORM.grid, SQLFORM.smartgrid)
Validadores:
CLEANUP, CRYPT, IS_ALPHANUMERIC, IS_DATE_IN_RANGE, IS_DATE,
IS_DATETIME_IN_RANGE, IS_DATETIME, IS_DECIMAL_IN_RANGE,
IS_EMAIL, IS_EMPTY_OR, IS_EXPR, IS_FLOAT_IN_RANGE, IS_IMAGE,
IS_IN_DB, IS_IN_SET, IS_INT_IN_RANGE, IS_IPV4, IS_LENGTH,
IS_LIST_OF, IS_LOWER, IS_MATCH, IS_EQUAL_TO, IS_NOT_EMPTY,
IS_NOT_IN_DB, IS_NULL_OR, IS_SLUG, IS_STRONG, IS_TIME,
IS_UPLOAD_FILENAME, IS_UPPER, IS_URL
Base de dados:
DAL, Field
Para compatibilidade com versões anteriores SQLDB=DAL
e SQLField=Field
. Nós encorajamos você a usar a nova sintaxe DAL
e Field
, em vez da antiga sintaxe.
Outros objetos e módulos são definidos nas bibliotecas, mas não são importados automaticamente, pois não são usados com frequência.
As principais entidades da API no ambiente de execução web2py são request
, response
, session
, cache
, URL
, HTTP
, redirect
e T
e são discutidos abaixo.
Alguns objetos e funções, incluindo Auth, Crud e Serviço, estão definidos em "gluon/tools.py" e precisam ser importados conforme necessário:
from gluon.tools import Auth, Crud, Service
Eles são importados em db.py
na aplicação de andaimes.
Acessando a API dos módulos do Python
Seus modelos ou controladores podem importar módulos python. Estes são geralmente arquivos python que você armazena no diretório de módulos do seu aplicativo. Eles podem precisar usar algumas das APIs web2py. A maneira de fazer isso é importando-os:
from gluon import *
De fato, qualquer módulo Python, mesmo que não seja importado por um aplicativo web2py, pode importar a API web2py contanto que o web2py esteja na sys.path
.
Compartilhando o escopo global com módulos usando o objeto atual
Há uma ressalva, no entanto. O Web2py define alguns objetos globais (solicitação, resposta, sessão, cache, T) que só podem existir quando uma solicitação HTTP está presente (ou é falsificada). Portanto, os módulos podem acessá-los somente se forem chamados de um aplicativo. Por esta razão, eles são colocados em um recipiente chamado current
, que é um objeto local de thread. Aqui está um exemplo.
Crie um módulo "/myapp/modules/mytest.py" que contenha:
from gluon import *
def ip(): return current.request.client
Agora de um controlador em "myapp" você pode fazer
import mytest
def index():
return "Your ip is " + mytest.ip()
Observe algumas coisas:
import mytest
procura primeiro o módulo na pasta de módulos do aplicativo atual e, em seguida, nas pastas listadassys.path
. Portanto, os módulos em nível de aplicativo sempre têm precedência sobre os módulos do Python. Isso permite que aplicativos diferentes sejam enviados com versões diferentes de seus módulos, sem conflitos.- Usuários diferentes podem chamar a mesma ação
index
concorrentemente, o que chama a função no módulo, e ainda não há conflito porquecurrent.request
é um objeto diferente em diferentes segmentos. Apenas tenha cuidado para não acessarcurrent.request
fora de funções ou classes (ou seja, no nível superior) no módulo. import mytest
é um atalho parafrom applications.appname.modules import mytest
. Usando a sintaxe mais longa, é possível importar módulos de outros aplicativos.
Para uniformidade com o comportamento normal do Python, por padrão, o web2py não recarrega os módulos quando são feitas alterações. Ainda isso pode ser alterado. Para ativar o recurso de recarregamento automático para módulos, use o track_changes
funcionam da seguinte maneira (normalmente em um arquivo de modelo, antes de qualquer importação):
from gluon.custom_import import track_changes; track_changes(True)
De agora em diante, toda vez que um módulo for importado, o importador verificará se o arquivo de origem do Python (.py) foi alterado. Se tiver mudado, o módulo será recarregado.
Não chame track_changes nos próprios módulos.
Rastrear alterações rastreia apenas alterações para módulos armazenados no aplicativo. Módulos que importam current
pode acessar:
current.request
current.response
current.session
current.cache
current.T
e qualquer outra variável que seu aplicativo escolher armazenar em corrente. Por exemplo, um modelo poderia fazer
auth = Auth(db)
from gluon import current
current.auth = auth
current.db = db #not needed in this case but useful
e agora todos os módulos importados podem acessar current.auth
.
current
e import
crie um mecanismo poderoso para criar módulos extensíveis e reutilizáveis para seus aplicativos.
Atenção! Não use o objeto atual no escopo global em um módulo
Cuidado! Dado from gluon import current
, é correto usar current.request
e qualquer um dos outros objetos locais de thread, mas nunca se deve atribuí-los a variáveis globais no módulo, como em
request = current.request # ERRADO! PERIGO!
nem deve usar corrente para atribuir atributos de classe:
class MyClass:
request = current.request # ERRADO! PERIGO!
Isso ocorre porque o objeto local de thread deve ser extraído no tempo de execução. Variáveis globais são definidas apenas uma vez quando o modelo é importado pela primeira vez.
Em vez disso, atribua dentro de uma função.
import * from gluon
...
def a_module_function():
db = current.db # assuming you assigned current.db = db in the model db.py
...
Outra advertência tem a ver com o cache. Você não pode usar o cache
objeto para decorar funções em módulos, isso é porque não se comportaria como esperado. Para armazenar uma função em cache f
em um módulo você deve usar lazy_cache
:
from gluon.cache import lazy_cache
@lazy_cache('key', time_expire=60, cache_model='ram')
def f(a, b, c): ....
Lembre-se de que a chave é definida pelo usuário, mas deve ser exclusivamente associada à função. Se omitido, o web2py determinará automaticamente uma chave.
request
request Storage request.cookies user_agent
o request
objeto é uma instância da classe web2py onipresente que é chamado gluon.storage.Storage
, que estende o Python dict
classe. É basicamente um dicionário, mas os valores dos itens também podem ser acessados como atributos:
request.vars
é o mesmo que:
request['vars']
Ao contrário de um dicionário, se um atributo (ou chave) não existir, ele não gera uma exceção. Em vez disso, ele retorna None
.
Às vezes é útil criar seus próprios objetos de armazenamento. Você pode fazer isso da seguinte maneira:
from gluon.storage import Storage my_storage = Storage() # empty storage object my_other_storage = Storage(dict(a=1, b=2)) # convert dictionary to Storage
request
tem os seguintes itens/atributos, alguns dos quais são também uma instância do Storage
classe:
request.cookies
: umaCookie.SimpleCookie()
objeto contendo os cookies passados com o pedido HTTP. Ele age como um dicionário de cookies. Cada cookie é um objeto Morsel [morsel] .request.env
: umaStorage
objeto contendo as variáveis de ambiente transmitidas ao controlador, incluindo variáveis de cabeçalho HTTP da solicitação HTTP e parâmetros WSGI padrão. As variáveis de ambiente são todas convertidas em minúsculas e os pontos são convertidos em sublinhados para facilitar a memorização.request.application
: o nome do aplicativo solicitado.request.controller
: o nome do controlador solicitado.request.function
: o nome da função solicitada.request.extension
: a extensão da ação solicitada. O padrão é "html". Se a função do controlador retornar um dicionário e não especificar uma visão, isso é usado para determinar a extensão do arquivo de exibição que renderizará o dicionário (analisado a partir dorequest.env.path_info
).request.folder
: o diretório do aplicativo. Por exemplo, se o aplicativo for "bem-vindo",request.folder
está definido para o caminho absoluto "/ caminho/para/bem-vindo". Em seus programas, você deve sempre usar essa variável eos.path.join
função para construir caminhos para os arquivos que você precisa acessar. Embora o web2py sempre use caminhos absolutos, é uma boa regra nunca alterar explicitamente a pasta de trabalho atual (seja lá qual for), já que essa não é uma prática segura para encadeamentos.request.now
: umadatetime.datetime
objeto que armazena o datetime da solicitação atual.request.utcnow
: umadatetime.datetime
objeto que armazena o datetime UTC da solicitação atual.request.args
: Uma lista dos componentes do caminho da URL após o nome da função do controlador; equivalente arequest.env.path_info.split('/')[3:]
request.vars
: umagluon.storage.Storage
objeto contendo todos os parâmetros de solicitação.request.get_vars
: umagluon.storage.Storage
objeto contendo apenas parâmetros passados para a string de consulta (um pedido para/a/c/f?var1=1&var2=2
terminará em{var1: "1", var2: "2"}
)request.post_vars
: umagluon.storage.Storage
objeto contendo apenas os parâmetros passados para o corpo da solicitação (geralmente em solicitações POST, PUT, DELETE).request.client
: O endereço IP do cliente, conforme determinado, se presenterequest.env.http_x_forwarded_for
ou pelarequest.env.remote_addr
de outra forma. Embora isso seja útil, não deve ser confiável porque ohttp_x_forwarded_for
pode ser falsificado.request.is_local
:True
se o cliente for localhost,False
de outra forma. Deve funcionar atrás de um proxy se o proxy suportarhttp_x_forwarded_for
.request.is_https
:True
se a solicitação estiver usando o protocolo HTTPS,False
de outra forma.request.body
: um fluxo de arquivos somente leitura que contém o corpo da solicitação HTTP. Isso é automaticamente analisado para obterrequest.post_vars
e depois rebobinado. Pode ser lido comrequest.body.read()
.request.ajax
é True se a função estiver sendo chamada por meio de uma solicitação Ajax.request.cid
é oid
do componente que gerou a solicitação do Ajax (se houver). Você pode ler mais sobre os componentes no Capítulo 12.request.requires_https()
impede a execução de código adicional se a solicitação não for por HTTPS e redirecionar o visitante para a página atual por HTTPS.request.restful
Este é um novo e muito útil decorador que pode ser usado para alterar o comportamento padrão de ações web2py, separando as solicitações GET/POST/PUT/DELETE. Isso será discutido com algum detalhe no Capítulo 10.request.user_agent()
analisa o campo user_agent do cliente e retorna as informações na forma de um dicionário. É útil para detectar dispositivos móveis. Ele usa "gluon/contrib/user_agent_parser.py" criado por Ross Peoples. Para ver o que acontece, tente incorporar o seguinte código em uma visualização:{{=BEAUTIFY(request.user_agent())}}
request.global_settings
request.global_settingscontém configurações do sistema web2py. Eles são definidos automaticamente e você não deve alterá-los. Por exemplorequest.global_settings.gluon_parent
contém o caminho completo para a pasta web2py,request.global_settings.is_pypy
determina se o web2py está sendo executado no PyPy.request.wsgi
é um gancho que permite chamar aplicativos WSGI de terceiros a partir de ações internas
Este último inclui:
request.wsgi.environ
request.wsgi.start_response
request.wsgi.middleware
seu uso é discutido no final deste capítulo.
Como exemplo, a seguinte chamada em um sistema típico:
http://127.0.0.1:8000/examples/default/status/x/y/z?p=1&q=2
resulta no seguinte request
objeto:
variável | valor |
request.application | examples |
request.controller | default |
request.function | status |
request.extension | html |
request.view | status |
request.folder | applications/examples/ |
request.args | ['x', 'y', 'z'] |
request.vars | <Storage {'p': 1, 'q': 2}> |
request.get_vars | <Storage {'p': 1, 'q': 2}> |
request.post_vars | <Storage {}> |
request.is_local | False |
request.is_https | False |
request.ajax | False |
request.cid | None |
request.wsgi | <hook> |
request.env.content_length | 0 |
request.env.content_type | |
request.env.http_accept | text/xml,text/html; |
request.env.http_accept_encoding | gzip, deflate |
request.env.http_accept_language | en |
request.env.http_cookie | session_id_examples=127.0.0.1.119725 |
request.env.http_host | 127.0.0.1:8000 |
request.env.http_referer | http://web2py.com/ |
request.env.http_user_agent | Mozilla/5.0 |
request.env.path_info | /examples/simple_examples/status |
request.env.query_string | remote_addr:127.0.0.1 |
request.env.request_method | GET |
request.env.script_name | |
request.env.server_name | 127.0.0.1 |
request.env.server_port | 8000 |
request.env.server_protocol | HTTP/1.1 |
request.env.server_software | Rocket 1.2.6 |
request.env.web2py_path | /Users/mdipierro/web2py |
request.env.web2py_version | Version 2.4.1 |
request.env.wsgi_errors | <open file, mode 'w' at > |
request.env.wsgi_input | |
request.env.wsgi_url_scheme | http |
Quais variáveis de ambiente são realmente definidas depende do servidor da web. Aqui estamos assumindo o servidor wsgi Rocket integrado. O conjunto de variáveis não é muito diferente ao usar o servidor da web Apache.
o request.env.http_*
as variáveis são analisadas a partir do cabeçalho HTTP da solicitação.
o request.env.web2py_*
as variáveis não são analisadas no ambiente do servidor da Web, mas são criadas pelo web2py, caso seus aplicativos precisem saber sobre o local e a versão do web2py e se estão sendo executados no Google App Engine (porque otimizações específicas podem ser necessárias).
Observe também o request.env.wsgi_*
variáveis. Eles são específicos para o adaptador wsgi.
response
response
é outra instância do Storage
classe. Ele contém o seguinte:
response.body
: umaStringIO
objeto no qual web2py escreve o corpo da página de saída. NUNCA MUDE ESTA VARIÁVEL.response.cookies
: igual arequest.cookies
, mas enquanto o último contém os cookies enviados do cliente para o servidor, o primeiro contém cookies enviados pelo servidor para o cliente. O cookie da sessão é tratado automaticamente.response.download(request, db)
: um método usado para implementar a função do controlador que permite o download de arquivos enviados.response.download
espera o últimoarg
dentrorequest.args
para ser o nome de arquivo codificado (ou seja, o nome do arquivo gerado no tempo de upload e armazenado no campo de upload). Ele extrai o nome do campo de upload e o nome da tabela, bem como o nome do arquivo original do nome de arquivo codificado.response.download
leva dois argumentos opcionais:chunk_size
define o tamanho em bytes para streaming em partes (o padrão é 64K) eattachments
determina se o arquivo baixado deve ser tratado como um anexo ou não (padrãoTrue
). Nota,response.download
é especificamente para baixar arquivos associadosdb
campos de upload. Usarresponse.stream
(veja abaixo) para outros tipos de downloads e streaming de arquivos. Além disso, observe que não é necessário usarresponse.download
para acessar arquivos enviados para a pasta/static - arquivos estáticos podem (e geralmente devem) ser acessados diretamente por meio de URL (por exemplo, /app/static/files/myfile.pdf).response.files
: uma lista de.css
,.js
,.coffee
e.less
arquivos exigidos pela página. Eles serão automaticamente vinculados na cabeça do padrão "layout.html" por meio do "web2py_ajax.html" incluído. Para incluir um novo arquivo CSS, JS, COFFEE ou LESS, basta anexá-lo a essa lista. Ele irá lidar com duplicatas. A ordem é importante.response.include_files()
gera tags de cabeçalho html para incluir todosresponse.files
(usado em "views/web2py_ajax.html").response.flash
: parâmetro opcional que pode ser incluído nas visualizações. Normalmente usado para notificar o usuário sobre algo que aconteceu.response.headers
: umadict
para cabeçalhos de resposta HTTP. O Web2py define alguns cabeçalhos por padrão, incluindo "Content-Length", "Content-Type" e "X-Powered-By" (conjunto igual a web2py). O Web2py também define os cabeçalhos "Cache-Control", "Expires" e "Pragma" para impedir o armazenamento em cache do lado do cliente, exceto para solicitações de arquivo estático, para as quais o cache do cliente está habilitado. Os cabeçalhos que conjuntos web2py podem ser sobrescritos ou removidos, e novos cabeçalhos podem ser adicionados (por exemplo,response.headers['Cache-Control'] = 'private'
). Você pode remover um cabeçalho removendo sua chave do dict de response.headers, por exemplo.del response.headers['Custom-Header']
, no entanto, os cabeçalhos padrão do web2py serão adicionados novamente antes de retornar a resposta. Para evitar esse comportamento, basta definir o valor do cabeçalho como Nenhum, por exemplo, para remover o cabeçalho padrão Content-Type,response.headers['Content-Type'] = None
response.menu
: parâmetro opcional que pode ser incluído nas visualizações, normalmente usado para passar uma árvore de menu de navegação para a exibição. Pode ser processado pelo auxiliar do MENU.response.meta
: um objeto de armazenamento que contém opcional<meta>
informações comoresponse.meta.author
,.description
e/ou.keywords
. O conteúdo de cada meta variável é automaticamente colocado noMETA
tag pelo código em "views/web2py_ajax.html", que é incluído por padrão em "views/layout.html".response.include_meta()
gera uma string que inclui todosresponse.meta
cabeçalhos serializados (usados em "views/web2py_ajax.html").response.postprocessing
: esta é uma lista de funções, vazia por padrão. Essas funções são usadas para filtrar o objeto de resposta na saída de uma ação, antes que a saída seja renderizada pela exibição. Pode ser usado para implementar suporte para outras linguagens de modelo.response.render(view, vars)
: um método usado para chamar a visão explicitamente dentro do controlador.view
é um parâmetro opcional que é o nome do arquivo de visão,vars
é um dicionário de valores nomeados passado para a exibição.response.session_file
: fluxo de arquivos contendo a sessão.response.session_file_name
: nome do arquivo onde a sessão será salva.response.session_id
: o id da sessão atual. É determinado automaticamente. NUNCA MUDE ESTA VARIÁVEL.response.session_id_name
: o nome do cookie de sessão para este aplicativo. NUNCA MUDE ESTA VARIÁVEL.response.static_version
: um número de versão para o gerenciamento de ativos estáticos.response.status
: o inteiro do código de status HTTP a ser passado para a resposta. O padrão é 200 (OK).response.stream(file, chunk_size, request=request, attachment=False, filename=None)
: quando um controlador o retorna, o web2py transmite o conteúdo do arquivo de volta ao cliente em blocos de tamanhochunk_size
. orequest
parâmetro é necessário para usar o início do chunk no cabeçalho HTTP.file
deve ser um caminho de arquivo (para compatibilidade com versões anteriores, também pode ser um objeto de arquivo aberto, mas isso não é recomendado). Como observado acima,response.download
deve ser usado para recuperar arquivos armazenados através de um campo de upload.response.stream
pode ser usado em outros casos, como retornar um arquivo temporário ou um objeto StringIO criado pelo controlador. E seattachment
é True, o cabeçalho Content-Disposition será definido como "anexo" e, sefilename
também é fornecido, ele também será incluído no cabeçalho Content-Disposition (mas somente quandoattachment
é verdade). Se ainda não estiver incluídoresponse.headers
, os seguintes cabeçalhos de resposta serão definidos automaticamente: Content-Type, Content-Length, Cache-Control, Pragma e Last-Modified (os três últimos são configurados para permitir o armazenamento em cache do navegador do arquivo). Para substituir qualquer uma dessas configurações automáticas de cabeçalho, basta defini-lasresponse.headers
antes de ligarresponse.stream
.response.subtitle
: parâmetro opcional que pode ser incluído nas visualizações. Deve conter o subtítulo da página.response.title
: parâmetro opcional que pode ser incluído nas visualizações. Ele deve conter o título da página e deve ser renderizado dentro da tag de título html no cabeçalho.response.toolbar
: uma função que permite incorporar uma barra de ferramentas na página para fins de depuração{{=response.toolbar()}}
. A barra de ferramentas exibe solicitação, resposta, variáveis de sessão e tempo de acesso ao banco de dados para cada consulta.response._vars
: esta variável é acessível apenas em uma visão, não na ação. Ele contém os valores retornados pela ação para a exibição.response._caller
: esta é uma função que envolve todas as chamadas de ação. O padrão é a função de identidade, mas ela pode ser modificada para capturar tipos especiais de exceção para fazer logging extra;
response._caller = lambda f: f()
response.optimize_css
: pode ser definido como "concat, minify, inline" para concatenar, minify e inline os arquivos CSS incluídos pelo web2py.response.optimize_js
: pode ser definido como "concat, minify, inline" para concatenar, minify e inline os arquivos JavaScript incluídos pelo web2py.response.view
: o nome do modelo de visualização que deve renderizar a página. Isso é definido por padrão para:
"%s/%s.%s" % (request.controller, request.function, request.extension)
"generic.%s" % (request.extension)
response.delimiters
o padrão é('{{','}}')
. Ele permite que você altere o delimitador do código incorporado nas visualizações.response.xmlrpc(request, methods)
: quando um controlador retorna, esta função expõe os métodos via XML-RPC [xmlrpc] . Esta função está obsoleta, pois um mecanismo melhor está disponível e descrito no Capítulo 10.response.write(text)
: um método para escrever texto no corpo da página de saída.response.js
pode conter código Javascript. Este código será executado se, e somente se, a resposta for recebida por um componente web2py, conforme discutido no Capítulo 12.response.models_to_run
contém uma lista de expressões regulares que escolhe quais modelos serão executados.- Por padrão, isso é configurado automaticamente para carregar arquivos /a/models/*.py, /a/models/c/*.py e /a/models/c/f/*.py quando
/a/c/f
é requerido. Você pode definir, por exemplo,response.models_to_run = ['myfolder/']
para forçar a execução apenas dos modelos dentro do seu aplicativomodels/myfolder
subpasta. - NB:
response.models_to_run
é uma lista de regex, não uma lista de caminhos de arquivos. O regex é relativo aos models/folder, portanto, qualquer arquivo de modelo com um caminho de arquivo relativo que corresponda a um dos regexes será executado. Observe também que isso não pode afetar nenhum modelo que já tenha sido avaliado porque era anterior na ordem de classificação alfabética. Ou seja, se um modelo condicional para o controlador laranja for laranja/orange_model.py e for definido o regex como [. *], Essa alteração não afetará nenhum modelo rejeitado anteriormente para carregamento, como o modelo apple/apple_model.py; corresponde ao novo regex, mas foi avaliado e rejeitado antes de orange/orange_model.py alterar o regex. - Isso significa que, se você quiser usar models_to_run para compartilhar modelos condicionais entre controladores, coloque os modelos em um subdiretório que classificará por último, como zzz, e use um regex 'zzz'.
Desde a response
é um gluon.storage.Storage
objeto, ele pode ser usado para armazenar outros atributos que você deseja passar para a exibição. Embora não haja restrições técnicas, nossa recomendação é armazenar apenas as variáveis que serão renderizadas por todas as páginas no layout geral ("layout.html").
De qualquer forma, sugerimos fortemente que se atenha às variáveis listadas aqui:
response.title
response.subtitle
response.flash
response.menu
response.meta.author
response.meta.description
response.meta.keywords
response.meta.*
porque isso tornará mais fácil para você substituir o arquivo padrão "layout.html" que vem com o web2py com outro arquivo de layout, um que use o mesmo conjunto de variáveis.
Versões antigas do web2py usadas response.author
ao invés de response.meta.author
e similar para os outros meta atributos.
session
session
é outra instância da classe Storage
. O que quer que seja armazenado em session
por exemplo:session.myvariable = "hello"
pode ser recuperado mais tarde:
a = session.myvariable
contanto que o código seja executado na mesma sessão pelo mesmo usuário (desde que o usuário não tenha excluído os cookies de sessão e a sessão não tenha expirado). Porque session
é um Storage
objeto, tentando acessar um atributo/chave que não foi definido não gera uma exceção; ele retorna None
em vez de.
O objeto de sessão possui três métodos importantes. Um é forget
:
session.forget(response)
Diz ao web2py para não salvar a sessão. Isso deve ser usado nos controladores cujas ações são chamadas frequentemente e não precisam rastrear a atividade do usuário. session.forget()
impede que o arquivo de sessão seja gravado, independentemente de ter sido modificado. session.forget(response)
Além disso, desbloqueia e fecha o arquivo de sessão. Você raramente precisa chamar esse método, pois as sessões não são salvas quando não são alteradas. No entanto, se a página fizer várias solicitações simultâneas de Ajax, é uma boa idéia que as ações chamadas pelo Ajax sejam chamadas session.forget(response)
(supondo que a sessão não seja necessária pela ação). Caso contrário, cada ação do Ajax terá que aguardar a conclusão do anterior (e desbloquear o arquivo da sessão) antes de prosseguir, o que retardará o carregamento da página. Observe que as sessões não são bloqueadas quando armazenadas no banco de dados.
Outro método é:
session.secure()
que informa ao web2py para definir o cookie de sessão como um cookie seguro. Isso deve ser definido se o aplicativo estiver passando por https. Ao definir o cookie de sessão como seguro, o servidor está pedindo ao navegador para não enviar o cookie de sessão de volta ao servidor, a menos que por uma conexão https.
O outro método é connect
. Por padrão, as sessões são armazenadas no sistema de arquivos e um cookie de sessão é usado para armazenar e recuperar session.id
. Usando o método connect, é possível dizer ao web2y para armazenar sessões no banco de dados ou nos cookies, eliminando assim a necessidade de acessar o sistema de arquivos para o gerenciamento de sessões.
Por exemplo, para armazenar sessões no banco de dados:
session.connect(request, response, db, masterapp=None)
Onde db
é o nome de uma conexão de banco de dados aberta (conforme retornado pelo DAL). Diz ao web2py que você deseja armazenar as sessões no banco de dados e não no sistema de arquivos. session.connect
deve vir depois db=DAL(...)
, mas antes de qualquer outra lógica que requer sessão, por exemplo, configurando Auth
.
O web2py cria uma tabela:
db.define_table('web2py_session',
Field('locked', 'boolean', default=False),
Field('client_ip'),
Field('created_datetime', 'datetime', default=now),
Field('modified_datetime', 'datetime'),
Field('unique_key'),
Field('session_data', 'text'))
e armazena sessões cPickled no session_data
campo.
A opção masterapp=None
, por padrão, informa ao web2py para tentar recuperar uma sessão existente para o aplicativo com nome em request.application
, na aplicação em execução.
Se você quiser que dois ou mais aplicativos compartilhem sessões, defina masterapp
ao nome do aplicativo mestre.
Em vez disso, para armazenar sessões em cookies você pode fazer:
session.connect(request, response, cookie_key='yoursecret', compression_level=None)
Aqui cookie_key
é uma chave de criptografia simétrica. compression_level
é um opcional zlib
nível de criptografia.
Embora as sessões no cookie sejam geralmente recomendadas por motivos de escalabilidade, elas são limitadas em tamanho. Sessões grandes resultarão em cookies quebrados.
Você pode verificar o estado do seu aplicativo a qualquer momento imprimindo request
, session
e response
variáveis do sistema. Uma maneira de fazer isso é criar uma ação dedicada:
def status():
return dict(request=request, session=session, response=response)
Na exibição "generic.html", isso é feito usando {{=response.toolbar()}}
.
Não armazene classes definidas pelo usuário na sessão
As variáveis armazenadas na sessão são preservadas entre solicitações por serialização.
Sessões são recuperadas antes que o código do módulo seja executado e, portanto, antes que as classes sejam definidas. Portanto, as classes definidas pelo usuário não podem ser decapadas.
As classes definidas nos módulos também são uma área cinza e não devem ser armazenadas. Na maior parte do tempo eles trabalham, mas podem quebrar. Isso ocorre porque, por exemplo, se você reiniciar o servidor da Web e um usuário recuperar uma sessão, isso pode acontecer antes que o módulo seja importado. Mesmo problema quando o servidor web inicia um novo processo de trabalho. Mesmo problema em um ambiente distribuído.
Sessões separadas
Se você estiver armazenando sessões no sistema de arquivos e tiver muitas delas, o acesso ao sistema de arquivos poderá se tornar um gargalo. Uma solução é a seguinte:
session.connect(request, response, separate=True)
Definindo separate=True
O web2py armazenará sessões não na pasta "sessions /", mas em subpastas da pasta "sessions /". A subpasta será criada automaticamente. Sessões com o mesmo prefixo estarão na mesma subpasta. Novamente, observe que o acima deve ser chamado antes de qualquer lógica que possa requerer a sessão.
cache
cache
é um objeto global que também está disponível no ambiente de execução web2py. Tem dois atributos:cache.ram
: o cache do aplicativo na memória principal.cache.disk
: o cache do aplicativo no disco.cache
é chamado, isso permite que ele seja usado como decorador para ações e visualizações de armazenamento em cache.
O exemplo a seguir armazena em cache time.ctime()
função na RAM:
def cache_in_ram():
import time
t = cache.ram('time', lambda: time.ctime(), time_expire=5)
return dict(time=t, link=A('click me', _href=request.url))
A saída de lambda: time.ctime()
é armazenado em cache na RAM por 5 segundos. A corda 'time'
é usado como chave de cache.
O exemplo a seguir armazena em cache time.ctime()
função no disco:
def cache_on_disk():
import time
t = cache.disk('time', lambda: time.ctime(), time_expire=5)
return dict(time=t, link=A('click me', _href=request.url))
A saída de lambda: time.ctime()
é armazenado em cache no disco (usando o módulo shelve) por 5 segundos.
Note, o segundo argumento para cache.ram
e cache.disk
deve ser uma função ou objeto que pode ser chamado. Se você quiser armazenar em cache um objeto existente em vez da saída de uma função, você pode simplesmente retorná-lo através de uma função lambda:
cache.ram('myobject', lambda: myobject, time_expire=60*60*24)
O próximo exemplo armazena em cache time.ctime()
função para a RAM e disco:
def cache_in_ram_and_disk():
import time
t = cache.ram('time', lambda: cache.disk('time', lambda: time.ctime(), time_expire=5), time_expire=5)
return dict(time=t, link=A('click me', _href=request.url))
A saída de lambda: time.ctime()
é armazenado em cache no disco (usando o módulo shelve) e, em seguida, na RAM por 5 segundos. O web2py parece primeiro na RAM e, se não está lá, fica no disco. Se não estiver na RAM ou no disco, lambda: time.ctime()
é executado e o cache é atualizado. Essa técnica é útil em um ambiente multiprocessador. As duas vezes não precisam ser as mesmas.
O exemplo a seguir é o armazenamento em cache na RAM da saída da função do controlador (mas não da exibição):
@cache(request.env.path_info, time_expire=5, cache_model=cache.ram)
def cache_controller_in_ram():
import time
t = time.ctime()
return dict(time=t, link=A('click me', _href=request.url))
O dicionário retornado por cache_controller_in_ram
é armazenado em cache na RAM por 5 segundos. Observe que o resultado de uma seleção de banco de dados não pode ser armazenado em cache sem primeiro ser serializado. Uma maneira melhor é armazenar em cache o banco de dados diretamente usando o select
métodos cache
argumento.
O exemplo a seguir é o armazenamento em cache da saída da função do controlador no disco (mas não na visualização):
@cache(request.env.path_info, time_expire=5, cache_model=cache.disk)
def cache_controller_on_disk():
import time
t = time.ctime()
return dict(time=t, link=A('click to reload', _href=request.url))
O dicionário retornado por cache_controller_on_disk
é armazenado em cache no disco por 5 segundos. Lembre-se de que o web2py não pode armazenar em cache um dicionário que contenha objetos não selecionáveis.
Também é possível armazenar em cache a exibição. O truque é renderizar a visão na função do controlador, para que o controlador retorne uma string. Isso é feito retornando response.render(d)
Onde d
é o dicionário que pretendíamos transmitir para a visualização. O exemplo a seguir armazena em cache a saída da função do controlador na RAM (incluindo a exibição renderizada):
@cache(request.env.path_info, time_expire=5, cache_model=cache.ram)
def cache_controller_and_view():
import time
t = time.ctime()
d = dict(time=t, link=A('click to reload', _href=request.url))
return response.render(d)
response.render(d)
retorna a exibição renderizada como uma string, que agora é armazenada em cache por 5 segundos. Essa é a melhor e mais rápida maneira de armazenar em cache.Nós recomendamos @cache.action a partir de web2py
>
2.4.6
Nota, time_expire
é usado para comparar a hora atual com a hora em que o objeto solicitado foi salvo pela última vez no cache. Não afeta pedidos futuros. Isso permite time_expire
para ser definido dinamicamente quando um objeto é solicitado em vez de ser corrigido quando o objeto é salvo. Por exemplo:
message = cache.ram('message', lambda: 'Hello', time_expire=5)
Agora, suponha que a seguinte chamada seja feita 10 segundos após a chamada acima:
message = cache.ram('message', lambda: 'Goodbye', time_expire=20)
Porque time_expire
está definido para 20 segundos na segunda chamada e apenas 10 segundos se passaram desde que a mensagem foi salva pela primeira vez, o valor "Olá" será recuperado do cache e não será atualizado com "Adeus". o time_expire
O valor de 5 segundos na primeira chamada não tem impacto na segunda chamada.
Configuração time_expire=0
(ou um valor negativo) força o item em cache a ser atualizado (porque o tempo decorrido desde o último salvamento será sempre> 0), e a configuração time_expire=None
força a recuperação do valor em cache, independentemente do tempo decorrido desde que foi salvo (se time_expire
é sempre None
, o item em cache irá efetivamente nunca expirar).
Você pode limpar uma ou mais variáveis de cache com
cache.ram.clear(regex='...')
Onde regex
é uma expressão regular que corresponde a todas as chaves que você deseja remover do cache. Você também pode limpar um único item com:
cache.ram(key, None)
Onde key
é a chave do item em cache.
Também é possível definir outros mecanismos de cache, como o memcache. Memcache está disponível via gluon.contrib.memcache
e é discutido em mais detalhes no Capítulo 14.
Tenha cuidado ao armazenar em cache para lembrar que o armazenamento em cache geralmente está no nível do aplicativo, não no nível do usuário. Se você precisar, por exemplo, armazenar em cache o conteúdo específico do usuário, escolha uma chave que inclua o ID do usuário.
O aplicativo de administração de um aplicativo permite exibir chaves de cache (e limpar o cache). Acesse a partir da tela de gerenciamento de banco de dados do admin.
cache.action
O Web2py, por padrão, supõe que o conteúdo retornado não será armazenado em cache, pois isso reduz as deficiências de um cache incorreto da página do lado do cliente.
Por exemplo, quando você mostra um formulário para o usuário ou uma lista de registros, a página da Web não deve ser armazenada em cache, pois outros usuários podem ter inserido novos registros na tabela que você está mostrando.
Em vez disso, se você estiver mostrando ao usuário uma página wiki cujo conteúdo nunca será alterado (ou muda uma vez por semana), é útil armazenar essa página, mas é ainda mais útil para informar ao cliente que essa página não vai mudar.
Isso é feito enviando alguns cabeçalhos específicos junto com a página: quando o navegador do cliente recebe o conteúdo, ele é armazenado no cache do navegador e não será solicitado novamente para o seu site. Claro que isso é um principal aceleração para sites voltados para o público.
Web2py> 2.4.6 introduziu um novo cache.action
decorador para permitir um tratamento mais inteligente desta situação. cache.action
pode ser usado:
- para definir cabeçalhos de cache inteligentes
- para armazenar os resultados em conformidade
NB: vai fazer um ou outro ou ambos.
O principal problema com o armazenamento em cache de uma view @cache(request.env.path_info, time_expire=300, cache_model=cache.ram)
é que request.env.path_info
como chave leva a vários problemas, por ex.
- Vars de URL não são consideradas
- Você armazenou em cache o resultado de /app/default/index? pesquisa = foo: pelos próximos 300 segundos /app/default/index?pesquisa = bar retornará exatamente a mesma coisa de /app/default/index?pesquisa = foo
- Usuário não é considerado
- Seu usuário acessa uma página com freqüência e você escolhe armazená-la em cache. No entanto, você armazenou em cache o resultado de /app/default/index usando request.env.path_info como chave, para que outro usuário verá uma página que não foi feita para ele - Você armazenou uma página em cache para "Bill", mas "Bill" acessou a página da área de trabalho. Agora ele tenta acessá-lo pelo celular: se você preparou um modelo para usuários de dispositivos móveis que é diferente do padrão, "Joe" não o verá
- O idioma não é considerado
- Quando você armazena em cache a página, se você usar T () para alguns elementos, a página será armazenada com uma tradução fixa
- Método não é considerado
- Quando você armazena em cache uma página, você deve armazená-la em cache somente se for resultado de uma operação GET
- Código de status não é considerado
- Quando você colocou a página em cache pela primeira vez, algo deu errado e você retornou uma boa página 404. Você não quer esconder erros ^ _ ^
Em vez de permitir que os usuários escrevam muito código boilerplate para cuidar de todos esses problemas, cache.action
foi criado. Por padrão, usará cabeçalhos de cache inteligentes para permitir que o navegador armazene em cache o resultado: se você passar um modelo de cache para ele, ele também descobrirá a melhor chave automaticamente, Assim, versões diferentes da mesma página podem ser armazenadas e recuperadas adequadamente (por exemplo, uma para usuários ingleses e outra para usuários espanhóis)
São necessários vários parâmetros, com padrões inteligentes:
- time_expire: o usual, o padrão é 300 segundos
- cache_model: por padrão, é None. Isso significa que @ cache.action será só altere os cabeçalhos padrão para permitir que o navegador do cliente armazene em cache o conteúdo
- se você passar, por exemplo, cache.ram
, o resultado será armazenado no cache também
- prefixo: se você quiser prefixar a chave gerada automaticamente (útil para limpá-la mais tarde, por exemplo,
cache.ram.clear(prefix*)
) - session: se você quiser considerar a sessão, o padrão é False
- vars: se você quiser considerar vars de URL, o padrão é True
- lang: se você quiser considerar o idioma, o padrão é True
- user_agent: se você quiser considerar o user agent, o padrão é False
- public: se você quiser a mesma página para todos os usuários que já acessam, o padrão é True
- valid_statuses: padrão para None. O cache.client armazenará em cache apenas as páginas solicitadas com um método GET, cujos códigos de status começam com 1,2 ou 3.
Você pode transmitir uma lista de códigos de status (quando quiser que as páginas sejam armazenadas em cache com esses status, por exemplo, status_codes = [200] armazenará apenas páginas em cache que resultou em um código de status 200)
- quick: padrão para None, mas você pode passar uma lista de iniciais para definir um recurso específico:
- Session, Vars euang, vocêser_agent, Public por exemplo. @cache.action(time_expire=300, cache_model=cache.ram, quick='SVP')
é o mesmo que @cache.action(time_expire=300, cache_model=cache.ram, session=True, vars=True, public=True)
"Considerar" significa, por exemplo, vars, que você deseja armazenar em cache páginas diferentes se vars são diferentes, então /app/default/index?pesquisa = foo não será o mesmo para /app/default/index?pesquisa = bar Algumas configurações substituem outras, por exemplo, se você definir session=True, public=True
este último será descartado. Use-os com sabedoria!
URL
o URL
A função é uma das funções mais importantes do web2py. Ele gera caminhos de URL internos para as ações e os arquivos estáticos.
Aqui está um exemplo:
URL('f')
está mapeado
/[application]/[controller]/f
Observe que a saída do URL
A função depende do nome do aplicativo atual, do controlador de chamada e de outros parâmetros. O web2py suporta mapeamento de URL e mapeamento reverso de URL. O mapeamento de URL permite que você redefina o formato de URLs externos. Se você usar o URL
Para gerar todas as URLs internas, as adições ou alterações nos mapeamentos de URL impedirão links quebrados dentro do aplicativo web2py.
Você pode passar parâmetros adicionais para o URL
função, ou seja, termos extras no caminho da URL (args) e variáveis de consulta da URL (vars):
URL('f', args=['x', 'y'], vars=dict(z='t'))
está mapeado
/[application]/[controller]/f/x/y?z=t
o args
atributos são automaticamente analisados, decodificados e finalmente armazenados em request.args
por web2py. Da mesma forma, vars
são analisadas, decodificadas e armazenadas em request.vars
. args
e vars
fornecer o mecanismo básico pelo qual o web2py troca informações com o navegador do cliente.
Se args contiver apenas um elemento, não há necessidade de passá-lo em uma lista.
Você também pode usar o URL
função para gerar URLs para ações em outros controladores e outras aplicações:
URL('a', 'c', 'f', args=['x', 'y'], vars=dict(z='t'))
está mapeado
/a/c/f/x/y?z=t
Também é possível especificar aplicativo, controlador e função usando argumentos nomeados:
URL(a='a', c='c', f='f')
Se o nome do aplicativo a estiver faltando, o aplicativo atual será assumido.
URL('c', 'f')
Se o nome do controlador c estiver faltando, o nome atual será assumido.
URL('f')
Em vez de passar o nome de uma função do controlador, também é possível passar a própria função
URL(f)
Pelas razões mencionadas acima, você deve sempre usar o URL
função para gerar URLs de arquivos estáticos para seus aplicativos. Os arquivos estáticos são armazenados no aplicativo static
subpasta (é onde eles vão quando carregados usando a interface administrativa). O web2py fornece um controlador virtual 'estático' cujo trabalho é recuperar arquivos do static
subpasta, determine seu tipo de conteúdo e envie o arquivo para o cliente. O exemplo a seguir gera o URL para o arquivo estático "image.png":
URL('static', 'image.png')
está mapeado
/[application]/static/image.png
Se o arquivo estático estiver em uma subpasta dentro do static
pasta, você pode incluir a (s) subpasta (s) como parte do nome do arquivo. Por exemplo, para gerar:
/[application]/static/images/icons/arrow.png
deve-se usar:
URL('static', 'images/icons/arrow.png')
Você não precisa codificar/escapar do args
e vars
argumentos; isso é feito automaticamente para você.
Por padrão, a extensão correspondente à solicitação atual (que pode ser encontrada em request.extension
) é anexado à função, a menos que request.extension seja html, o padrão. Isso pode ser substituído explicitamente incluindo uma extensão como parte do nome da função URL(f='name.ext')
ou com o argumento de extensão:
URL(..., extension='css')
A extensão atual pode ser explicitamente suprimida:
URL(..., extension=False)
URLs absolutos
Por padrão, URL
gera URLs relativos. No entanto, você também pode gerar URLs absolutos especificando scheme
e host
argumentos (isso é útil, por exemplo, ao inserir URLs em mensagens de e-mail):
URL(..., scheme='http', host='www.mysite.com')
Você pode incluir automaticamente o esquema e o host da solicitação atual simplesmente configurando os argumentos para True
.
URL(..., scheme=True, host=True)
o URL
função também aceita um port
argumento para especificar a porta do servidor, se necessário.
URLs assinados digitalmente
Ao gerar um URL, você tem a opção de assiná-lo digitalmente. Isso adicionará um _signature
GET variável que pode ser verificada pelo servidor. Isso pode ser feito de duas maneiras.
Você pode passar para a função URL os seguintes argumentos:
hmac_key
: a chave para assinar o URL (uma string)salt
: uma string opcional para salgar os dados antes de assinarhash_vars
: uma lista opcional de nomes de variáveis da cadeia de consulta da URL (ou seja, variáveis GET) a serem incluídas na assinatura. Também pode ser definido paraTrue
(o padrão) para incluir todas as variáveis, ouFalse
para incluir nenhuma das variáveis.
Aqui está um exemplo de uso:
KEY = 'mykey'
def one():
return dict(link=URL('two', vars=dict(a=123), hmac_key=KEY))
def two():
if not URL.verify(request, hmac_key=KEY): raise HTTP(403)
# do something
return locals()
Isso faz com que a ação two
acessível apenas através de um URL assinado digitalmente. Um URL assinado digitalmente se parece com isso:
'/welcome/default/two?a=123&_signature=4981bc70e13866bb60e52a09073560ae822224e9'
Note que a assinatura digital é verificada através do URL.verify
função. URL.verify
também leva o hmac_key
, salt
e hash_vars
argumentos descritos acima, e seus valores devem corresponder aos valores que foram passados para o URL
função quando a assinatura digital foi criada para verificar o URL.
Um segundo e mais sofisticado, porém mais comum, uso de URLs assinadas digitalmente é em conjunto com o Auth. Isso é melhor explicado com um exemplo:
@auth.requires_login()
def one():
return dict(link=URL('two', vars=dict(a=123), user_signature=True)
@auth.requires_signature()
def two():
# do something
return locals()
Neste caso, o hmac_key
é gerado e compartilhado automaticamente na sessão. Isso permite ação two
delegar qualquer controle de acesso à ação one
. Se o link é gerado e assinado, é válido; outra coisa não é. Se o link for roubado por outro usuário, o link será inválido.
É uma boa prática sempre assinar digitalmente retornos de chamada do Ajax. Se você usa o web2py LOAD
função, tem um user_signature
argumento também que pode ser usado para este fim:
{{=LOAD('default', 'two', vars=dict(a=123), ajax=True, user_signature=True)}}
HTTP
e redirect
HTTP redirect
web2py define apenas uma nova exceção chamada HTTP
. Essa exceção pode ser levantada em qualquer lugar em um modelo, um controlador ou uma visualização com o comando:
raise HTTP(400, "my message")
Isso faz com que o fluxo de controle saia do código do usuário, retorne ao web2py e retorne uma resposta HTTP como:
HTTP/1.1 400 BAD REQUEST
Date: Sat, 05 Jul 2008 19:36:22 GMT
Server: Rocket WSGI Server
Content-Type: text/html
Via: 1.1 127.0.0.1:8000
Connection: close
Transfer-Encoding: chunked
my message
O primeiro argumento de HTTP
é o código de status HTTP. O segundo argumento é a string que será retornada como o corpo da resposta. Argumentos nomeados opcionais adicionais são usados para construir o cabeçalho HTTP de resposta. Por exemplo:
raise HTTP(400, 'my message', test='hello')
gera:
HTTP/1.1 400 BAD REQUEST
Date: Sat, 05 Jul 2008 19:36:22 GMT
Server: Rocket WSGI Server
Content-Type: text/html
Via: 1.1 127.0.0.1:8000
Connection: close
Transfer-Encoding: chunked
test: hello
my message
Se você não deseja consolidar a transação de banco de dados aberta, retroceda antes de ativar a exceção.
Qualquer exceção diferente de HTTP
faz com que o web2py faça rollback de qualquer transação de banco de dados aberta, registre o erro traceback, emita um ticket para o visitante e retorne uma página de erro padrão.
Isso significa que apenas HTTP
pode ser usado para fluxo de controle de página cruzada. Outras exceções devem ser detectadas pelo aplicativo, caso contrário, elas são emitidas pelo web2py.
O comando:
redirect('http://www.web2py.com')
é simplesmente um atalho para:
raise HTTP(303,
'You are being redirected <a href="%s">here</a>' % location,
Location='http://www.web2py.com')
Os argumentos nomeados do HTTP
O método initializer é traduzido em diretivas de cabeçalho HTTP, nesse caso, o local de destino de redirecionamento. redirect
leva um segundo argumento opcional, que é o código de status HTTP para o redirecionamento (303 por padrão). Altere esse número para 307 para um redirecionamento temporário ou para 301 para um redirecionamento permanente.
A maneira mais comum de usar o redirecionamento é redirecionar para outras páginas no mesmo aplicativo e (opcionalmente) passar parâmetros:
redirect(URL('index', args=(1, 2, 3), vars=dict(a='b')))
No capítulo 12, discutimos os componentes do web2py. Eles fazem solicitações de Ajax para ações web2py. Se a ação chamada executar um redirecionamento, você pode querer que a solicitação do Ajax siga o redirecionamento ou você pode querer que a página inteira execute o redirecionamento da solicitação do Ajax. Neste último caso, você pode definir:
redirect(..., client_side=True)
Internacionalização e Pluralização com T
T internationalization
O objeto T
é o tradutor da língua. Constitui uma única instância global da classe web2py gluon.language.translator
. Todas as constantes de string (e apenas constantes de string) devem ser marcadas T
, por exemplo:
a = T("hello world")
Cordas marcadas com T
são identificados pelo web2py como precisando de tradução e serão traduzidos quando o código (no modelo, controlador ou visão) for executado. Se a string a ser traduzida não for uma constante, mas sim uma variável, ela será adicionada ao arquivo de tradução no tempo de execução (exceto no GAE) para ser traduzida posteriormente.
o T
objeto também pode conter variáveis interpoladas e suporta várias sintaxes equivalentes:
a = T("hello %s", ('Tim', ))
a = T("hello %(name)s", dict(name='Tim'))
a = T("hello %s") % ('Tim', )
a = T("hello %(name)s") % dict(name='Tim')
A última sintaxe é recomendada porque facilita a tradução. A primeira string é traduzida de acordo com o arquivo de idioma solicitado e name
variável é substituída independentemente do idioma.
Você pode concatenar cadeias traduzidas e strings normais:
T("blah ") + name + T(" blah")
O seguinte código também é permitido e muitas vezes preferível:
T("blah %(name)s blah", dict(name='Tim'))
ou a sintaxe alternativa
T("blah %(name)s blah") % dict(name='Tim')
Em ambos os casos, a tradução ocorre antes que o nome da variável seja substituído no slot "% (name) s". A seguinte alternativa NÃO DEVE SER USADA:
T("blah %(name)s blah" % dict(name='Tim'))
porque a tradução ocorreria após a substituição.
Determinando o idioma
O idioma solicitado é determinado pelo campo "Accept-Language" no cabeçalho HTTP, mas essa seleção pode ser sobregravada programaticamente, solicitando um arquivo específico, por exemplo:
T.force('it-it')
que lê o arquivo de idioma "languages/it-it.py". Arquivos de idioma podem ser criados e editados através da interface administrativa.
Você também pode forçar uma linguagem por string:
T("Hello World", language="it-it")
No caso de vários idiomas serem solicitados, por exemplo, "it-it, fr-ft", o web2py tenta localizar os arquivos de tradução "it-it.py" e "fr-fr.py". Se nenhum dos arquivos solicitados estiver presente, ele tentará usar "it.py" e "fr.py". Se esses arquivos não estiverem presentes, o padrão é "default.py". Se isso também não estiver presente, o padrão é sem tradução. A regra mais geral é que web2py tenta "xx-xy-yy.py", "xx-xy.py", "xx.py", "default.py" para cada um dos idiomas aceitos "xx-xy-yy" tentando encontrar a correspondência mais próxima às preferências do visitante.
Você pode desativar as traduções completamente via
T.force(None)
Normalmente, a tradução de string é avaliada preguiçosamente quando a exibição é renderizada; daí, o tradutor force
método não deve ser chamado dentro de uma visão.
É possível desabilitar a avaliação lenta via
T.lazy = False
Desta forma, as cordas são traduzidas imediatamente pelo T
operador baseado na linguagem atualmente aceita ou forçada.
Também é possível desabilitar a avaliação lenta de strings individuais:
T("Hello World", lazy=False)
Um problema comum é o seguinte. O pedido original é em inglês. Suponha que exista um arquivo de tradução (por exemplo, italiano, "it-it.py") e o cliente HTTP declare que aceita inglês (en) e italiano (it-it) nessa ordem. A seguinte situação indesejada ocorre: web2py não sabe que o padrão está escrito em inglês (en). Portanto, ele prefere traduzir tudo para o italiano (it-it) porque ele só encontrou o arquivo de tradução italiano. Se não tivesse encontrado o arquivo "it-it.py", ele usaria as strings de idioma padrão (inglês).
Existem duas soluções para esse problema: criar um idioma de tradução para o inglês, que seria redundante e desnecessário, ou melhor, informar ao web2py quais idiomas devem usar as cadeias de idioma padrão (as cadeias de caracteres codificadas no aplicativo). Isso pode ser feito com:
T.set_current_languages('en', 'en-en')
Armazena em T.current_languages
uma lista de idiomas que não exigem tradução e força o recarregamento dos arquivos de idioma.
Observe que "it" e "it-it" são idiomas diferentes do ponto de vista do web2py. Para suportar ambos, seria necessário dois arquivos de tradução, sempre em letras minúsculas. O mesmo é verdade para todos os outros idiomas.
O idioma atualmente aceito é armazenado em
T.accepted_language
Traduzindo Variáveis
T (...) não traduz apenas sequências, mas também pode traduzir valores armazenados em variáveis:
>>> a = "test"
>>> print T(a)
Nesse caso, a palavra "teste" é traduzida, mas, se não for encontrada e se o sistema de arquivos for gravável, ela será adicionada à lista de palavras a serem traduzidas no arquivo de idioma.
Observe que isso pode resultar em muitos arquivos IO e você pode querer desabilitá-lo:
T.is_writable = False
impede que T atualize dinamicamente os arquivos de idioma.
Comentários e múltiplas traduções
É possível que a mesma sequência apareça em diferentes contextos no aplicativo e precise de diferentes traduções com base no contexto. Para fazer isso, pode-se adicionar comentários à string original. Os comentários não serão renderizados, mas serão usados pelo web2py para determinar a tradução mais apropriada. Por exemplo:
T("hello world ## first occurrence")
T("hello world ## second occurrence")
O texto após o ##
, incluindo o duplo ##
são comentários.
Motor de pluralização
Desde a versão 2.0, o web2py inclui um poderoso sistema de pluralização (PS). Isso significa que quando o texto marcado para tradução depende de uma variável numérica, ele pode ser traduzido de forma diferente com base no valor numérico. Por exemplo, em inglês, podemos renderizar:
x book(s)
com
a book (x==1)
5 books (x==5)
O inglês tem uma forma singular e uma forma plural. A forma plural é construída pela adição de um "-s" ou "-es" ou usando uma forma excepcional. O web2py fornece uma maneira de definir regras de pluralização para cada idioma, além de exceções às regras padrão. Na verdade, o web2py já conhece regras de pluralização para muitos idiomas. Sabe, por exemplo, que o esloveno tem uma forma singular e 3 formas plurais (para x == 2, x == 3 ou x == 4 e x> 4). Essas regras são codificadas em arquivos "gluon/contrib/plural_rules/*. Py" e novos arquivos podem ser criados. Pluralizações explícitas para palavras são criadas pela edição de arquivos de pluralização usando a interface administrativa.
Por padrão, o PS não está ativado. É desencadeada pela symbol
argumento do T
função. Por exemplo:
T("You have %s %%{book}", symbols=10)
Agora o PS é ativado para a palavra "livro" e para o número 10. O resultado em inglês será: "Você tem 10 livros". Observe que "livro" foi pluralizado em "livros".
O PS é composto por 3 partes:
- espaços reservados
%%{}
para marcar palavras noT
entrada - regra para decidir qual forma de palavra usar ("regras/plural_rules/*. py")
- dicionário com formas plurais de palavra ("app/languages/plural - *. py")
O valor dos símbolos pode ser uma única variável, uma lista/tupla de variáveis ou um dicionário.
O espaço reservado %%{}
consiste em 3 partes:
%%{[<modifier>]<word>[<parameter>]},
Onde:
<modifier>::= ! | !! | !!!
<word> ::= any word or phrase in singular in lower case (!)
<parameter> ::= [index] | (key) | (number)
Por exemplo:
%%{word}
é equivalente a%%{word[0]}
(se nenhum modificador for usado).%%{word[index]}
é usado quando os símbolos são uma tupla. símbolos [índice] nos dá um número usado para tomar uma decisão sobre qual forma de palavra escolher.%%{word(key)}
é usado para obter o parâmetro numérico dos símbolos [key]%%{word(number)}
permite definir umnumber
diretamente (por exemplo:%%{word(%i)}
)%%{?word?number}
retorna "palavra" senumber==1
, retorna onumber
de outra forma%%{?number} or %%{??number}
retornanumber
E senumber!=1
, não devolva nada de outra forma
T("blabla %s %%{word}", symbols=var)
%%{word}
por padrão significa %%{word[0]}
, Onde [0]
é um índice de item na tupla de símbolos.
T("blabla %s %s %%{word[1]}", (var1, var2))
PS é usado para "word" e "var2" respectivamente.
Você pode usar vários %%{}
espaços reservados com um índice:
T("%%{this} %%{is} %s %%{book}", var)
ou
T("%%{this[0]} %%{is[0]} %s %%{book[0]}", var)
Eles geram:
var output
------------------
1 this is 1 book
2 these are 2 books
3 these are 2 books
Da mesma forma você pode passar um dicionário para símbolos:
T("blabla %(var1)s %(wordcnt)s %%{word(wordcnt)}",
dict(var1="tututu", wordcnt=20))
que produz
blabla tututu 20 words
Você pode substituir "1" por qualquer palavra desejada por este espaço reservado %%{?word?number}
. Por exemplo
T("%%{this} %%{is} %%{?a?%s} %%{book}", var)
produz:
var output
------------------
1 this is a book
2 these are 2 books
3 these are 3 books
...
Dentro %%{...}
você também pode usar os seguintes modificadores:
!
para capitalizar o texto (equivalente astring.capitalize
)!!
para capitalizar cada palavra (equivalente astring.title
)!!!
para capitalizar todos os caracteres (equivalente astring.upper
)
Observe que você pode usar \ para escapar !
e ?
.
Traduções, pluralização e MARKMIN
Você também pode usar a poderosa sintaxe MARKMIN dentro de strings de tradução, substituindo
T("hello world")
com
T.M("hello world")
Agora, a string aceita a marcação MARKMIN, conforme descrito em Capítulo 5
Cookies
O web2py usa os módulos de cookies do Python para manipular cookies.
Cookies do navegador estão em request.cookies
e os cookies enviados pelo servidor estão em response.cookies
.
Você pode definir um cookie da seguinte maneira:
response.cookies['mycookie'] = 'somevalue'
response.cookies['mycookie']['expires'] = 24 * 3600
response.cookies['mycookie']['path'] = '/'
A segunda linha diz ao navegador para manter o cookie por 24 horas. A terceira linha diz ao navegador para enviar o cookie de volta para qualquer aplicativo (caminho de URL) no domínio atual. Observe que, se você não especificar um caminho para o cookie, o navegador assumirá o caminho da URL que foi solicitada, portanto, o cookie só será retornado ao servidor quando esse mesmo caminho de URL for solicitado.
O cookie pode ser protegido com:
response.cookies['mycookie']['secure'] = True
Isso informa ao navegador apenas para enviar o cookie de volta por HTTPS e não por HTTP.
O cookie pode ser recuperado com:
if request.cookies.has_key('mycookie'):
value = request.cookies['mycookie'].value
A menos que as sessões sejam desativadas, o web2py, sob o capô, define o seguinte cookie e o usa para lidar com sessões:
response.cookies[response.session_id_name] = response.session_id
response.cookies[response.session_id_name]['path'] = "/"
Observe que, se um único aplicativo incluir vários subdomínios e você quiser compartilhar a sessão entre esses subdomínios (por exemplo, sub1.seudomínio.com, sub2.seudomínio.com etc.), você deve definir explicitamente o domínio do cookie de sessão como segue:
if not request.env.remote_addr in ['127.0.0.1', 'localhost']:
response.cookies[response.session_id_name]['domain'] = ".yourdomain.com"
O acima pode ser útil se, por exemplo, você quiser permitir que o usuário permaneça conectado entre subdomínios.
Aplicação init
Ao implantar o web2py, você desejará definir um aplicativo padrão, ou seja, o aplicativo iniciado quando houver um caminho vazio no URL, como em:
http://127.0.0.1:8000
Por padrão, quando confrontado com um caminho vazio, o web2py procura por um aplicativo chamado nisso. Se não houver aplicativo init, ele procura por um aplicativo chamado bem vinda.
O nome do aplicativo padrão pode ser alterado de nisso para outro nome, definindo default_application
em routes.py:
default_application = "myapp"
Nota: default_application
apareceu pela primeira vez no web2py versão 1.83.
Aqui estão quatro maneiras de definir o aplicativo padrão:
- Ligue para o seu aplicativo padrão "init".
- Set
default_application
para o nome da sua aplicação em routes.py - Faça um link simbólico de "aplicativos/init" para a pasta do seu aplicativo.
- Use a reescrita do URL, conforme discutido na próxima seção.
Reescrita de URL
O web2py tem a capacidade de reescrever o caminho da URL de solicitações recebidas antes de chamar a ação do controlador (mapeamento de URL) e, inversamente, o web2py pode reescrever o caminho da URL gerado pelo URL
função (mapeamento de URL reverso). Uma razão para fazer isso é manipular URLs herdadas, outra é simplificar caminhos e torná-los mais curtos.
O web2py inclui dois sistemas distintos de reescrita de URL: um sistema baseado em parâmetros fácil de usar para a maioria dos casos de uso, e um sistema flexível baseado em padrões para casos mais complexos. Para especificar as regras de regravação de URL, crie um novo arquivo na pasta "web2py" chamada routes.py
(o conteúdo de routes.py
dependerá de qual dos dois sistemas de reescrita escolhidos, conforme descrito nas próximas duas seções). Os dois sistemas não podem ser misturados.
Observe que, se você editar o arquivo routes.py, deverá recarregá-lo. Isso pode ser feito de duas maneiras: reiniciando o servidor web ou clicando no botão de recarregamento de rotas em admin. Se houver um bug nas rotas, elas não serão recarregadas.
Sistema baseado em parâmetros
O roteador (paramétrico) baseado em parâmetros fornece acesso fácil a vários métodos de reescrita de URLs "enlatados". Suas capacidades incluem:
- Omitir nomes padrão de aplicativo, controlador e função a partir de URLs visíveis externamente (aquelas criadas pela função URL ())
- Mapeamento de domínios (e/ou portas) para aplicativos ou controladores
- Incorporação de um seletor de idioma na URL
- Removendo um prefixo fixo de URLs recebidos e adicionando-o de volta a URLs de saída
- Mapeamento de arquivos raiz, como /robots.txt, para um diretório estático de aplicativos
O roteador paramétrico também fornece validação um pouco mais flexível de URLs de entrada.
Suponha que você tenha escrito um aplicativo chamado myapp
e deseja torná-lo o padrão, para que o nome do aplicativo não faça mais parte do URL conforme visto pelo usuário. Seu controlador padrão ainda é default
e você também deseja remover o nome dos URLs visíveis pelo usuário. Aqui está o que você coloca routes.py
:
routers = dict(
BASE = dict(default_application='myapp'),
)
É isso aí. O roteador paramétrico é inteligente o suficiente para saber como fazer a coisa certa com URLs como:
http://domain.com/myapp/default/myapp
ou
http://domain.com/myapp/myapp/index
onde o encurtamento normal seria ambíguo. Se você tem dois aplicativos, myapp
e myapp2
, você terá o mesmo efeito e, além disso, myapp2
O controlador padrão será retirado da URL sempre que for seguro (o que ocorre na maior parte do tempo).
Aqui está outro caso: suponha que você queira oferecer suporte a idiomas baseados em URL, em que seus URLs se parecem com isso:
http://myapp/en/some/path
ou (reescrito)
http://en/some/path
Veja como:
routers = dict(
BASE = dict(default_application='myapp'),
myapp = dict(languages=['en', 'it', 'jp'], default_language='en'),
)
Agora, um URL de entrada como este:
http:/domain.com/it/some/path
será encaminhado para /myapp/some/path
, e request.uri_language será definido como 'it', para que você possa forçar a tradução. Você também pode ter arquivos estáticos específicos do idioma.
http://domain.com/it/static/filename
será mapeado para:
applications/myapp/static/it/filename
se esse arquivo existir. Caso contrário, URLs como:
http://domain.com/it/static/base.css
ainda mapeará para:
applications/myapp/static/base.css
(porque não há static/it/base.css
).
Portanto, agora você pode ter arquivos estáticos específicos do idioma, incluindo imagens, se for necessário. O mapeamento de domínio também é suportado:
routers = dict(
BASE = dict(
domains = {
'domain1.com' : 'app1',
'domain2.com' : 'app2',
}
),
)
faz o que você esperaria.
routers = dict(
BASE = dict(
domains = {
'domain.com:80' : 'app/insecure',
'domain.com:443' : 'app/secure',
}
),
)
mapas http://domain.com
acessos ao controlador nomeado insecure
, enquanto HTTPS
acessos vão para o secure
controlador. Como alternativa, você pode mapear diferentes portas para aplicativos diferentes, de maneira óbvia.
Para mais informações, consulte o arquivo "routes.parametric.example.py" fornecido na pasta "examples" da distribuição web2py padrão.
Nota: O sistema baseado em parâmetros apareceu pela primeira vez no web2py versão 1.92.1.
Sistema baseado em padrões
Embora o sistema baseado em parâmetros acabado de descrever deva ser suficiente para a maioria dos casos de uso, o sistema alternativo 'baseado em padrões' fornece alguma flexibilidade adicional para casos mais complexos. Para usar o sistema baseado em padrões, em vez de definir roteadores como dicionários de parâmetros de roteamento, você define duas listas (ou tuplas) de 2-tuplas, routes_in
e routes_out
. Cada tupla contém dois elementos: o padrão a ser substituído e a string que o substitui. Por exemplo:
routes_in = (
('/testme', '/examples/default/index'),
)
routes_out = (
('/examples/default/index', '/testme'),
)
Com estas rotas, o URL:
http://127.0.0.1:8000/testme
está mapeado em:
http://127.0.0.1:8000/examples/default/index
Para o visitante, todos os links para o URL da página parece /testme
.
Os padrões têm a mesma sintaxe que as expressões regulares do Python. Por exemplo:
('.*.php', '/init/default/index'),
mapeia todas as URLs que terminam em ".php" para a página de índice.
O segundo termo de uma regra também pode ser um redirecionamento para outra página:
('.*.php', '303->http://example.com/newpage'),
Aqui 303 é o código HTTP para a resposta de redirecionamento.
Às vezes, você quer se livrar do prefixo do aplicativo das URLs porque planeja expor apenas um aplicativo. Isso pode ser conseguido com:
routes_in = (
('/(?P<any>.*)', '/init/\g<any>'),
)
routes_out = (
('/init/(?P<any>.*)', '/\g<any>'),
)
Há também uma sintaxe alternativa que pode ser misturada com a notação de expressão regular acima. Consiste em usar $name
ao invés de (?P<name>\w+)
ou \g<name>
. Por exemplo:
routes_in = (
('/$c/$f', '/init/$c/$f'),
)
routes_out = (
('/init/$c/$f', '/$c/$f'),
)
Também eliminaria o prefixo do aplicativo "/ example" em todos os URLs.
Usando o $name
notação, você pode mapear automaticamente routes_in
para routes_out
, desde que você não use expressões regulares. Por exemplo:
routes_in = (
('/$c/$f', '/init/$c/$f'),
)
routes_out = [(x, y) for (y, x) in routes_in]
Se houver várias rotas, a primeira a corresponder à URL será executada. Se nenhum padrão corresponder, o caminho permanecerá inalterado.
Você pode usar $anything
para combinar com qualquer coisa ( .*
) até o final da linha.
Aqui está um "routes.py" mínimo para lidar com solicitações de favicon e robôs:
routes_in = (
('/favicon.ico', '/examples/static/favicon.ico'),
('/robots.txt', '/examples/static/robots.txt'),
)
routes_out = ()
Aqui está um exemplo mais complexo que expõe um único aplicativo "myapp" sem prefixos desnecessários, mas também expõe admin, appadmin e estático:
routes_in = (
('/admin/$anything', '/admin/$anything'),
('/static/$anything', '/myapp/static/$anything'),
('/appadmin/$anything', '/myapp/appadmin/$anything'),
('/favicon.ico', '/myapp/static/favicon.ico'),
('/robots.txt', '/myapp/static/robots.txt'),
)
routes_out = [(x, y) for (y, x) in routes_in[:-2]]
A sintaxe geral para rotas é mais complexa do que os exemplos simples que vimos até agora. Aqui está um exemplo mais geral e representativo:
routes_in = (
('140.191.\d+.\d+:https?://www.web2py.com:post /(?P<any>.*).php',
'/test/default/index?vars=\g<any>'),
)
Mapas http
ou https
POST
pedidos (note minúsculas "post") para hospedar www.web2py.com
de um IP remoto que corresponde à expressão regular
'140.191.\d+.\d+'
solicitando uma página que corresponda à expressão regular
'/(?P<any>.*).php'
para dentro
'/test/default/index?vars=\g<any>'
Onde \g<any>
é substituído pela expressão regular correspondente.
A sintaxe geral é
'[remote address]:[protocol]://[host]:[method] [path]'
Se a primeira seção do padrão (todos, mas [path]
) está faltando, o web2py fornece um padrão:
'.*?:https?://[^:/]+:[a-z]+'
A expressão inteira é correspondida como uma expressão regular, então "." deve ser escapado e qualquer subexpressão correspondente pode ser capturada usando (?P<...>...)
usando a sintaxe regex do Python. O método de solicitação (normalmente GET ou POST) deve estar em minúsculas. O URL que está sendo correspondido teve algum %xx
Escapa sem aspas.
Isso permite reencaminhar solicitações com base no endereço IP ou no domínio do cliente, com base no tipo da solicitação, no método e no caminho. Também permite que o web2py mapeie diferentes hosts virtuais em diferentes aplicativos. Qualquer subexpressão correspondente pode ser usada para criar o URL de destino e, eventualmente, passar como uma variável GET.
Todos os principais servidores da Web, como o Apache e o lighttpd, também têm a capacidade de reescrever URLs. Em um ambiente de produção que pode ser uma opção em vez de routes.py
. Independentemente do que você decidir fazer, sugerimos que você não codifique URLs internos em seu aplicativo e use a função de URL para gerá-los. Isso tornará seu aplicativo mais portátil caso as rotas sejam alteradas.
Reescrita de URL específica do aplicativo
Ao usar o sistema baseado em padrão, um aplicativo pode definir suas próprias rotas em um arquivo routes.py específico do aplicativo, localizado na pasta base do aplicativo. Isso é habilitado pela configuração routes_app
na base routes.py para determinar a partir de uma URL de entrada o nome da aplicação a ser selecionada. Quando isso acontece, o routes.py específico da aplicação é usado no lugar das rotas base.py.
O formato de routes_app
é idêntico a routes_in
, exceto que o padrão de substituição é simplesmente o nome do aplicativo. Se aplicar routes_app
para o URL de entrada não resulta em um nome de aplicativo, ou o routes.py específico do aplicativo resultante não foi encontrado, o routes.py de base é usado normalmente.
Nota: routes_app
apareceu pela primeira vez no web2py versão 1.83.
Aplicação, controlador e função padrão
Ao usar o sistema baseado em padrão, o nome do aplicativo, controlador e função padrão pode ser alterado de nisso, padrãoe índice respectivamente para outro nome, definindo o valor apropriado em routes.py:
default_application = "myapp"
default_controller = "admin"
default_function = "start"
Nota: Esses itens apareceram pela primeira vez na versão 1.83 do web2py.
Rotas em erro
Você também pode usar routes.py
reencaminhar pedidos para ações especiais no caso de haver um erro no servidor. Você pode especificar esse mapeamento globalmente, para cada aplicativo, para cada código de erro ou para cada aplicativo e código de erro. Aqui está um exemplo:
routes_onerror = [
('init/400', '/init/default/login'),
('init/*', '/init/static/fail.html'),
('*/404', '/init/static/cantfind.html'),
('*/*', '/init/error/index')
]
Para cada tupla, a primeira string é comparada com "[nome do aplicativo]/[código de erro]". Se uma correspondência for encontrada, a solicitação com falha será roteada novamente para a URL na segunda cadeia da tupla correspondente. Se a URL de tratamento de erros não for um arquivo estático, as seguintes variáveis GET serão transmitidas para a ação de erro:
code
: o código de status do HTTP (por exemplo, 404, 500)ticket
: na forma de "[nome do aplicativo]/[número do ticket]" (ou "Nenhum", se não houver ticket)requested_uri
: equivalente arequest.env.request_uri
request_url
: equivalente arequest.url
Essas variáveis estarão acessíveis para a ação de tratamento de erros via request.vars
e pode ser usado na geração da resposta de erro. Em particular, é uma boa idéia para a ação de erro retornar o código de erro HTTP original em vez do código de status padrão 200 (OK). Isso pode ser feito configurando response.status = request.vars.code
. Também é possível que a ação de erro envie (ou enfileire) um email para um administrador, incluindo um link para o ticket em admin
.
Erros não correspondentes exibem uma página de erro padrão. Esta página de erro padrão também pode ser personalizada aqui (veja "routes.parametric.example.py" e "routes.patterns.example.py" na pasta "examples"):
error_message = '<html><body><h1>%s</h1></body></html>'
error_message_ticket = '''<html><body><h1>Internal error</h1>
Ticket issued: <a href="/admin/default/ticket/%(ticket)s"
target="_blank">%(ticket)s</a></body></html>'''
A primeira variável contém a mensagem de erro quando uma aplicação ou função inválida é solicitada. A segunda variável contém a mensagem de erro quando um ticket é emitido.
routes_onerror
trabalhar com ambos os mecanismos de roteamento.
Em "routes.py" você também pode especificar uma ação responsável pelo tratamento de erros:
error_handler = dict(application='error',
controller='default',
function='index')
Se o error_handler
é especificado que a ação é chamada sem redirecionamento do usuário e a ação do manipulador será responsável por lidar com o erro. No caso de a própria página de tratamento de erros retornar um erro, o web2py retornará às suas antigas respostas estáticas.
Gerenciamento de ativos estáticos
Desde a versão 2.1.0, o web2py tem a capacidade de gerenciar ativos estáticos.
Quando um aplicativo está em desenvolvimento, o arquivo estático pode ser alterado com frequência, portanto, o web2py envia arquivos estáticos sem cabeçalhos de cache. Isso tem o efeito colateral de "forçar" o navegador a solicitar arquivos estáticos em todas as solicitações. Isso resulta em baixo desempenho ao carregar a página.
Em um site de "produção", você pode querer exibir arquivos estáticos com cache
cabeçalhos para evitar downloads desnecessários, pois os arquivos estáticos não são alterados.
cache
os cabeçalhos permitem que o navegador busque cada arquivo apenas uma vez, economizando largura de banda e reduzindo o tempo de carregamento.
No entanto, há um problema: o que os cabeçalhos de cache devem declarar? Quando os arquivos devem expirar? Quando os arquivos são exibidos pela primeira vez, o servidor não pode prever quando eles serão alterados.
Uma abordagem manual consiste em criar subpastas para diferentes versões de arquivos estáticos. Por exemplo, uma versão inicial de "layout.css" pode ser disponibilizada no URL "/myapp/static/css/1.2.3/layout.css". Quando você altera o arquivo, cria uma nova subpasta e a vincula como "/myapp/static/css/1.2.4/layout.css".
Este procedimento funciona, mas é pedante, pois toda vez que você atualizar o arquivo css, você deve se lembrar de movê-lo para outra pasta, alterar a URL do arquivo em seu layout.html e implantar.
O gerenciamento de ativos estáticos resolve o problema permitindo que o desenvolvedor declare uma versão para um grupo de arquivos estáticos e eles serão solicitados novamente somente quando o número da versão for alterado. O número da versão do ativo é parte do URL do arquivo, como no exemplo anterior. A diferença da abordagem anterior é que o número da versão aparece apenas na URL, não no sistema de arquivos.
Se você quiser exibir "/myapp/static/layout.css" com os cabeçalhos de cache, basta incluir o arquivo com um URL modificado que inclua um número de versão:
/myapp/static/_1.2.3/layout.css
(observe que o URL define um número de versão, ele não aparece em nenhum outro lugar).
Observe que o URL começa com "/ myapp/static /", seguido por um número de versão composto por um sublinhado e 3 inteiros separados por um ponto (conforme descrito em SemVer ), seguido pelo nome do arquivo. Observe também que você não precisa criar uma pasta "_1.2.3 /".
Toda vez que o arquivo estático é solicitado com uma versão no URL, ele será exibido com cabeçalhos de cache "no futuro", especificamente:
Cache-Control : max-age=315360000
Expires: Thu, 31 Dec 2037 23:59:59 GMT
Isso significa que o navegador buscará esses arquivos apenas uma vez e eles serão salvos "para sempre" no cache do navegador.
Toda vez que o "_1.2.3/nome_do_arquivo" é solicitado, o web2py removerá a parte da versão do caminho e exibirá seu arquivo com cabeçalhos no futuro, para que eles sejam armazenados em cache para sempre. Se você alterou o número da versão no URL, isso engana o navegador, achando que está solicitando um arquivo diferente, e o arquivo é buscado novamente.
Você pode usar "_1.2.3", "_0.0.0", "_999.888.888", desde que a versão comece com sublinhado seguido de três números separados por ponto.
Quando em desenvolvimento, você pode usar response.files.append(...)
para vincular as URLs estáticas de arquivos estáticos. Nesse caso, você pode incluir a parte "_1.2.3 /" manualmente ou aproveitar um novo parâmetro do objeto de resposta: response.static_version
. Basta incluir os arquivos da maneira que você usou, por exemplo
{{response.files.append(URL('static', 'layout.css'))}}
e no conjunto de modelos
response.static_version = '1.2.3'
Isso irá regravar automaticamente cada URL "/myapp/static/layout.css" como "/myapp/static/_1.2.3/layout.css", para cada arquivo incluído no response.files
.
Muitas vezes, na produção, você permite que o servidor da Web (apache, nginx, etc.) atenda aos arquivos estáticos. Você precisa ajustar sua configuração de tal forma que "pule" a parte "_1.2.3 /".
Por exemplo, no Apache, altere isso:
AliasMatch ^/([^/]+)/static/(.*) /home/www-data/web2py/applications/$1/static/$2
nisso:
AliasMatch ^/([^/]+)/static/(?:_[\d]+.[\d]+.[\d]+/)?(.*) /home/www-data/web2py/applications/$1/static/$2
Da mesma forma, no Nginx, altere isso:
location ~* /(\w+)/static/ {
root /home/www-data/web2py/applications/;
expires max;
}
nisso:
location ~* /(\w+)/static(?:/_[\d]+.[\d]+.[\d]+)?/(.*)$ {
alias /home/www-data/web2py/applications/$1/static/$2;
expires max;
}
Executando tarefas em segundo plano
No web2py, cada solicitação HTTP é exibida em seu próprio encadeamento. Os segmentos são reciclados para eficiência e gerenciados pelo servidor da web. Por segurança, o servidor web define um tempo limite em cada solicitação. Isso significa que as ações não devem executar tarefas que demoram muito, não devem criar novos encadeamentos e não devem bifurcar processos (é possível, mas não recomendado).
A maneira correta de executar tarefas demoradas é fazer isso em segundo plano. Não existe uma única maneira de fazer isso, mas aqui descrevemos três mecanismos que são construídos no web2py: cron, filas de tarefas caseiras e Agendador.
De cron nos referimos a uma funcionalidade web2py não ao mecanismo Unix Cron. O cron web2py também funciona no windows.
O web2py cron é o caminho a percorrer se você precisar de tarefas em segundo plano em horários programados e essas tarefas demorarem um tempo relativamente curto em comparação com o intervalo de tempo entre duas chamadas. Cada tarefa é executada em seu próprio processo e várias tarefas podem ser executadas simultaneamente, mas você não tem controle sobre quantas tarefas são executadas. Se acidentalmente uma tarefa se sobrepuser a si mesma, isso pode causar um bloqueio no banco de dados e um aumento no uso da memória.
O agendador web2py usa uma abordagem diferente. O número de processos em execução é fixo e eles podem ser executados em máquinas diferentes. Cada processo é chamado de trabalhador. Cada trabalhador seleciona uma tarefa quando disponível e a executa assim que possível após a data em que ela está programada para ser executada, mas não necessariamente no momento exato. Não pode haver mais processos em execução do que o número de tarefas agendadas e, portanto, não há picos de memória. As tarefas do planejador podem ser definidas nos modelos e armazenadas no banco de dados. O agendador web2py não implementa uma fila distribuída, uma vez que assume que o tempo para distribuir as tarefas é insignificante comparado com o tempo de execução das tarefas. Trabalhadores pegam a tarefa do banco de dados.
As filas de tarefas caseiras podem ser uma alternativa mais simples ao agendador web2py em alguns casos.
Cron
O web2py cron permite que os aplicativos executem tarefas em horários predefinidos, de maneira independente da plataforma.
Para cada aplicativo, a funcionalidade cron é definida por um arquivo crontab:
app/cron/crontab
Segue a sintaxe definida na ref. [cron] (com algumas extensões específicas do web2py).
Antes do web2py 2.1.1, o cron era ativado por padrão e podia ser desabilitado com o
-N
opção de linha de comando. Desde o 2.1.1, o cron é desabilitado por padrão e pode ser ativado pelo-Y
opção. Essa mudança foi motivada pelo desejo de levar os usuários a usar o novo planejador (que é superior ao mecanismo cron) e também porque o cron pode afetar o desempenho.
Isso significa que cada aplicativo pode ter uma configuração cron separada e que a configuração do cron pode ser alterada de dentro do web2py sem afetar o próprio SO do host.
Aqui está um exemplo:
0-59/1 * * * * root python /path/to/python/script.py
30 3 * * * root *applications/admin/cron/db_vacuum.py
*/30 * * * * root **applications/admin/cron/something.py
@reboot root *mycontroller/myfunction
@hourly root *applications/admin/cron/expire_sessions.py
As duas últimas linhas neste exemplo usam extensões para a sintaxe regular do cron para fornecer funcionalidade web2py adicional.
O arquivo "applications/admin/cron/expire_sessions.py" realmente existe e vem com o admin aplicativo. Ele verifica as sessões expiradas e as exclui. "aplicações/admin/cron/crontab" executa esta tarefa por hora.
Se a tarefa/script for prefixada com um asterisco ( *
) e termina com .py
, será executado no ambiente web2py. Isso significa que você terá todos os controladores e modelos à sua disposição. Se você usar dois asteriscos ( **
), os modelos não serão executados. Essa é a maneira recomendada de chamar, pois tem menos sobrecarga e evita possíveis problemas de bloqueio.
Observe que scripts/funções executadas no ambiente web2py requerem um manual db.commit()
no final da função ou a transação será revertida.
web2py não gera tickets ou tracebacks significativos no modo shell, que é como o cron é executado, por isso certifique-se de que seu código web2py é executado sem erros antes de configurá-lo como uma tarefa Cron, pois você provavelmente não conseguirá ver esses erros quando executado a partir do cron. Além disso, tenha cuidado ao usar modelos: enquanto a execução ocorre em um processo separado, os bloqueios de banco de dados devem ser levados em conta para evitar páginas aguardando tarefas do cron que possam estar bloqueando o banco de dados. Use o **
sintaxe se você não precisar usar o banco de dados em sua tarefa Cron.
Você também pode chamar uma função de controlador e, nesse caso, não há necessidade de especificar um caminho. O controlador e a função serão os do aplicativo de chamada. Tome especial cuidado com as ressalvas listadas acima. Exemplo:
*/30 * * * * root *mycontroller/myfunction
Se você especificar @reboot
no primeiro campo no arquivo crontab, a tarefa dada será executada apenas uma vez, na inicialização web2py. Você pode usar esse recurso se desejar pré-armazenar em cache, verificar ou inicializar dados para um aplicativo na inicialização de web2py. Observe que as tarefas cron são executadas em paralelo com o aplicativo - se o aplicativo não estiver pronto para atender a solicitações até que a tarefa Cron seja concluída, você deverá implementar verificações para refletir isso. Exemplo:
@reboot root *mycontroller/myfunction
Dependendo de como você está invocando o web2py, existem quatro modos de operação para o web2py cron.
- soft cron : disponível em todos os modos de execução
- cron difícil : disponível se estiver usando o servidor web embutido (diretamente ou via Apache mod_proxy)
- cron externo : disponível se você tiver acesso ao próprio serviço cron do sistema
- não cron
O padrão é o cron, se você estiver usando o servidor web embutido; em todos os outros casos, o padrão é soft cron. Soft cron é o método padrão se você estiver usando CGI, FASTCGI ou WSGI (mas note que o soft cron não é enabled
por padrão no padrão wsgihandler.py
arquivo fornecido com web2py).
Suas tarefas serão executadas na primeira chamada (carregamento da página) para web2py após o horário especificado no crontab; mas somente depois de processar a página, portanto, nenhum atraso será observado pelo usuário. Obviamente, há alguma incerteza sobre quando a tarefa será executada, dependendo do tráfego que o site recebe. Além disso, a tarefa Cron pode ser interrompida se o servidor da Web tiver um tempo limite de carregamento da página definido. Se estas limitações não forem aceitáveis, veja cron externo . Soft cron é um último recurso razoável, mas se o seu servidor web permitir outros métodos cron, eles devem ser preferidos ao soft cron.
O cron difícil é o padrão se você estiver usando o servidor da web integrado (diretamente ou via Apache mod_proxy). O cron difícil é executado em um encadeamento paralelo, portanto, ao contrário do soft cron, não há limitações em relação ao tempo de execução ou à precisão do tempo de execução.
O cron externo não é padrão em nenhum cenário, mas requer que você tenha acesso aos recursos do cron do sistema. Ele é executado em um processo paralelo, portanto, nenhuma das limitações do cron macio se aplica. Esta é a maneira recomendada de usar o cron em WSGI ou FASTCGI.
Exemplo de linha para adicionar ao crontab do sistema (normalmente/etc/crontab):
0-59/1 * * * * web2py cd /var/www/web2py/ && python web2py.py -J -C -D 1 >> /tmp/cron.output 2>&1
Com externo cron
, certifique-se de adicionar -J
(ou --cronjob
, que é o mesmo) conforme indicado acima, para que o web2py saiba que a tarefa é executada pelo cron. Web2py define isso internamente com soft e hard cron
.
Filas de tarefas caseiras
Embora o cron seja útil para executar tarefas em intervalos de tempo regulares, nem sempre é a melhor solução para executar uma tarefa em segundo plano. Para isso, o web2py fornece a capacidade de executar qualquer script python como se estivesse dentro de um controlador:
python web2py.py -S app -M -R applications/app/private/myscript.py -A a b c
Onde -S app
diz ao web2py para executar "myscript.py" como "app", -M
diz ao web2py para executar modelos e -A a b c
passa argumentos de linha de comando opcionais sys.argv=['applications/app/private/myscript.py','a','b','c']
para "myscript.py".
Este tipo de processo em segundo plano não deve ser executado via cron (exceto talvez para o cron @reboot) porque você precisa ter certeza de que não mais de uma instância está sendo executada ao mesmo tempo. Com o cron é possível que um processo inicie na iteração cron 1 e não seja completado pela iteração cron 2, então o cron o inicia novamente, e de novo, e novamente - assim atolando o servidor de email.
No capítulo 8, forneceremos um exemplo de como usar o método acima para enviar emails.
Agendador web2py
A solução mainstream web2py para executar tarefas em segundo plano (e, portanto, longe do processo do servidor web) é o agendador embutido.
A API estável consiste nessas funções:
disable()
resume()
terminate()
kill()
queue_task(),
task_status()
stop_task()
O agendador web2py funciona da mesma forma que a fila de tarefas descrita na subseção anterior com algumas diferenças:
- Fornece um mecanismo padrão para criar, agendar e monitorar tarefas.
- Não há um único processo de segundo plano, mas um conjunto de processos de trabalho.
- O trabalho dos nós do trabalhador pode ser monitorado porque seu estado, assim como o estado das tarefas, é armazenado no banco de dados.
- Funciona sem web2py, mas isso não está documentado aqui.
O planejador não usa o cron, embora seja possível usar o cron @reboot para iniciar os nós do trabalhador.
Mais informações sobre como implantar o agendador no Linux e no Windows estão no capítulo de receitas de implantação.
No planejador, uma tarefa é simplesmente uma função definida em um modelo (ou em um módulo e importada por um modelo). Por exemplo:
def task_add(a, b):
return a + b
As tarefas sempre serão chamadas no mesmo ambiente visto pelos controladores e, portanto, elas verão todas as variáveis globais definidas nos modelos, incluindo conexões de banco de dados ( db
). As tarefas diferem de uma ação do controlador porque elas não estão associadas a uma solicitação HTTP e, portanto, não há request.env
. Além disso, as tarefas podem acessar outra variável de ambiente que não está presente em solicitações normais: W2P_TASK
. W2P_TASK.id
detém o scheduler_task.id
e W2P_TASK.uuid
a scheduler_task.uuid
campo da tarefa em execução.
Lembre-se de ligar
db.commit()
no final de cada tarefa, se envolver inserções/atualizações no banco de dados. web2py confirma por padrão no final de uma ação bem-sucedida, mas as tarefas do planejador não são ações.
Para ativar o planejador, você deve instanciar a classe Scheduler em um modelo. A maneira recomendada de ativar o agendador para seu aplicativo é criar um arquivo de modelo chamado scheduler.py
e defina sua função lá. Após as funções, você pode colocar o seguinte código no modelo:
from gluon.scheduler import Scheduler
scheduler = Scheduler(db)
Se suas tarefas estiverem definidas em um módulo (em oposição a um modelo), talvez seja necessário reiniciar os trabalhadores.
A tarefa está agendada com
scheduler.queue_task(task_add, pvars=dict(a=1, b=2))
Parâmetros
O primeiro argumento do Scheduler
class deve ser o banco de dados a ser usado pelo agendador para se comunicar com os trabalhadores. Isso pode ser o db
do aplicativo ou outro dedicado db
, talvez um compartilhado por vários aplicativos. Se você usa o SQLite, é recomendável usar um banco de dados separado daquele usado pelo seu aplicativo para manter o aplicativo responsivo. Depois de definidas as tarefas e Scheduler
é instanciado, tudo o que é necessário fazer é iniciar os trabalhadores. Você pode fazer isso de várias maneiras:
python web2py.py -K myapp
inicia um trabalhador para o aplicativo myapp
. Se você quiser iniciar vários funcionários para o mesmo aplicativo, basta passar myapp,myapp
. Você também pode passar group_names
(substituindo o conjunto em seu modelo) com
python web2py.py -K myapp:group1:group2,myotherapp:group1
Se você tem um modelo chamado scheduler.py
você pode iniciar/parar os trabalhadores da janela padrão do web2py (aquela que você usa para definir o endereço IP e a porta).
Implantação do agendador
Uma última adição interessante: se você usar o servidor da Web incorporado, poderá iniciar o servidor da web e o agendador com apenas uma linha de código (isso pressupõe que você não quer que a janela web2py apareça, senão você pode usar o menu "Agendadores") )
python web2py.py -a yourpass -K myapp -X
Você pode passar os parâmetros usuais (-i, -p, aqui -a impede que a janela apareça), passe qualquer aplicativo no parâmetro -K e anexe um -X. O agendador será executado ao lado do servidor da Web!
Os usuários do Windows que desejam criar um serviço devem ver o capítulo Receitas de implantação.
Assinatura completa do Scheduler
A assinatura completa do agendador é:
Scheduler(db,
tasks=None,
migrate=True,
worker_name=None,
group_names=None,
heartbeat=HEARTBEAT,
max_empty_runs=0,
discard_results=False,
utc_time=False)
Vamos vê-los em ordem:
db
é a instância DAL do banco de dados em que você deseja que as tabelas do planejador sejam colocadas.tasks
é um dicionário que mapeia nomes de tarefas em funções. Se você não passar este parâmetro, a função será pesquisada no ambiente do aplicativo.worker_name
é None por padrão. Assim que o trabalhador é iniciado, um nome de trabalhador é gerado como hostname-uuid. Se você quiser especificar isso, certifique-se de que seja exclusivo.group_names
é por padrão definido como [a Principal]. Todas as tarefas têm umgroup_name
parâmetro, definido como a Principal por padrão. Os trabalhadores só podem pegar tarefas do grupo atribuído.
NB: Isso é útil se você tiver instâncias de trabalhadores diferentes (por exemplo, em máquinas diferentes) e desejar atribuir tarefas a um trabalhador específico.
NB2: É possível atribuir um trabalhador a mais grupos, e eles podem ser todos iguais,
['mygroup','mygroup']
. As tarefas serão distribuídas levando em consideração que um trabalhador com group_names['mygroup','mygroup']
é capaz de processar o dobro das tarefas de um trabalhador com group_names['mygroup']
é.
heartbeat
por padrão, é definido como 3 segundos. Este parâmetro é aquele que controla a frequência com que um agendador verifica seu status noscheduler_worker
mesa e ver se há algum ATRIBUÍDO tarefas para se processar.max_empty_runs
é 0 por padrão, isso significa que o trabalhador continuará a processar as tarefas assim que elas estiverem ATRIBUÍDO. Se você definir isso para um valor de, digamos, 10, um trabalhador morrerá automaticamente se for ATIVO e nenhuma tarefa é ATRIBUÍDO para 10 loops. Um loop é quando um trabalhador procura tarefas, a cada 3 segundos (ou o conjuntoheartbeat
)discard_results
é Falso por padrão. Se configurado como True, nenhum registro scheduler_run será criado.
NB: registros scheduler_run serão criados como antes para os status das tarefas FAILED, TIMEOUT and STOPPED.
utc_time
é Falso por padrão. Se você precisar coordenar com os trabalhadores que vivem em fusos horários diferentes, ou não tiver problemas com os tempos de solar/DST, fornecer dados de diferentes países, etc., você pode definir isso como Verdadeiro. O agendador honrará o horário UTC e trabalhará deixando o horário local de lado. Advertência: você precisa agendar tarefas com horários UTC (para start_time, stop_time e assim por diante).
Agora temos a infraestrutura no local: definimos as tarefas, contamos ao agendador sobre elas, iniciamos o (s) trabalhador (es). O que resta é realmente agendar as tarefas
Tarefas
As tarefas podem ser agendadas programaticamente ou via appadmin. Na verdade, uma tarefa é planejada simplesmente adicionando uma entrada na tabela "scheduler_task", que você pode acessar via appadmin:
http://127.0.0.1:8000/myapp/appadmin/insert/db/scheduler_task
O significado dos campos nesta tabela é óbvio. Os campos "args" e "vars" "são os valores a serem passados para a tarefa no formato JSON. No caso do" task_add "acima, um exemplo de" args "e" vars "poderia ser:
args = [3, 4]
vars = {}
ou
args = []
vars = {'a': 3, 'b': 4}
o scheduler_task
table é aquela em que as tarefas são organizadas.
Para adicionar tarefas através da API, use
scheduler.queue_task('mytask', ...)
que está documentado below .
Ciclo de Vida da Tarefa
Todas as tarefas seguem um ciclo de vida
Por padrão, quando você envia uma tarefa ao agendador, está no status QUEUED (na fila). Se você precisar que seja executado mais tarde, use o start_time
parâmetro (padrão = agora). Se por algum motivo você precisar ter certeza de que a tarefa não ser executado após um determinado ponto no tempo (talvez um pedido para um serviço da web que encerra às 01:00, um e-mail que precisa ser enviado não após o horário de trabalho, etc ...) você pode definir um stop_time
(default = None) para isso. Se a sua tarefa NÃO é apanhada por um worker
antes do stop_time
, será definido como EXPIRED. Tarefas sem stop_time
definido ou selecionados ANTES do stop_time são ASSIGNED (atribuídos) para um worker
. Quando um trabalhador pega uma tarefa, seu status é definido como RUNNING.
tarefas RUNNING podem acabar:
- TIMEOUT quando mais de
n
segundos passaram com o parâmetrotimeout
(padrão = 60 segundos). - FAILED quando uma exceção é detectada,
- COMPLETED quando eles completam com sucesso.
Valores para start_time
e stop_time
deve ser objetos de data e hora. Para agendar "mytask" para ser executado a 30 segundos da hora atual, por exemplo, você faria o seguinte:
from datetime import timedelta as timed
scheduler.queue_task('mytask', start_time=request.now + timed(seconds=30))
Além disso, você pode controlar quantas vezes uma tarefa deve ser repetida (ou seja, você precisa agregar alguns dados em intervalos especificados). Para fazer isso, defina repeats
parâmetro (padrão = 1 hora apenas, 0 = ilimitado). Você pode influenciar quantos segundos devem passar entre as execuções com o period
parâmetro (padrão = 60 segundos).
Comportamento padrão: O período de tempo não é calculado entre o FIM do primeiro turno e o START do próximo, mas do horário de INÍCIO do primeiro turno até o horário de INÍCIO do próximo ciclo). Isso pode causar o acúmulo de 'desvios' no horário de início de um trabalho. Após v 2.8.2, um novo parâmetro
prevent_drift
foi adicionado, com o padrão False. Se configurado como True ao enfileirar uma tarefa, o parâmetro start_time terá precedência sobre o período, evitando o desvio.
Você também pode definir quantas vezes a função pode gerar uma exceção (ou seja, solicitar dados de um serviço web lento) e ser enfileirada novamente em vez de parar em FALHA status usando o parâmetro retry_failed
(padrão = 0, -1 = ilimitado).
Resumo: você tem
period
erepeats
obter uma função reprogramada automaticamentetimeout
para ter certeza de que uma função não excede um certo período de temporetry_failed
para controlar quantas vezes a tarefa pode "falhar"start_time
estop_time
agendar uma função em um período de tempo restrito
queue_task
O método:
scheduler.queue_task(function,
pargs=[],
pvars={},
start_time=now, # datetime
stop_time=None, # datetime
timeout = 60, # seconds
prevent_drift=False,
period=60, # seconds
immediate=False,
repeats=1)
permite que você enfileire tarefas a serem executadas pelos trabalhadores. Retorna uma linha (veja here ), e leva os seguintes parâmetros:
function
(obrigatório): pode ser um nome de tarefa ou uma referência a uma função real.pargs
: são os argumentos a serem passados para a tarefa, armazenados como uma lista do Python.pvars
: são os argumentos nomeados a serem passados para a tarefa, armazenados como um dicionário Python.- todas as outras colunas scheduler_task podem ser passadas como argumentos de palavras-chave; os mais importantes são mostrados.
Por exemplo:
scheduler.queue_task('demo1', [1, 2])
faz exatamente a mesma coisa que
scheduler.queue_task('demo1', pvars={'a': 1, 'b': 2})
Como
st.validate_and_insert(function_name='demo1', args=json.dumps([1, 2]))
e como:
st.validate_and_insert(function_name='demo1', vars=json.dumps({'a': 1, 'b': 2}))
Aqui está um exemplo completo mais complexo:
def task_add(a, b):
return a + b
scheduler = Scheduler(db, tasks=dict(demo1=task_add))
scheduler.queue_task('demo1', pvars=dict(a=1, b=2), repeats = 0, period=180)
Desde a versão 2.4.1, se você passar um parâmetro adicional immediate=True
forçará o trabalhador principal a reatribuir tarefas. Até 2.4.1, o worker verifica novas tarefas a cada 5 ciclos (assim, 5*heartbeats
segundos). Se você tivesse um aplicativo que precisasse verificar com frequência novas tarefas, para obter um comportamento "rápido", seria forçado a diminuir o número de tarefas. heartbeat
parâmetro, colocando o banco de dados sob pressão sem motivo. Com immediate=True
você pode forçar a verificação de novas tarefas: isso acontecerá no máximo heartbeat
segundos são passados
Uma chamada para scheduler.queue_task
devolve a tarefa id
e uuid
da tarefa que você enfileirou (pode ser aquela que você transmitiu ou a que gerou automaticamente) e possível errors
:
<Row {'errors': {}, 'id': 1, 'uuid': '08e6433a-cf07-4cea-a4cb-01f16ae5f414'}>
Se houver erros (geralmente erro de sintaxe ou erros de validação de entrada), você obtém o resultado da validação, e id e uuid serão None
<Row {'errors': {'period': 'enter an integer greater than or equal to 0'}, 'id': None, 'uuid': None}>
task_status
Para consultar o agendador sobre tarefas, use task_status
scheduler.task_status(ref, output=False)
O argumento ref
pode ser
- integer -> a pesquisa será feita por scheduler_task.id
- string -> pesquisa será feita por scheduler_task.uuid
- consulta -> pesquisa como você deseja (como em db.scheduler_task.task_name == 'test1')
output=True
obtém o registro scheduler_run
Ele retorna um único objeto Row, para a tarefa enfileirada mais recente que corresponde aos critérios.
O registro scheduler_run é obtido por uma junção esquerda, de modo que pode tem todos os campos == Nenhum
Exemplo: recuperando o status da tarefa do planejador, resultados e tracebacks
Aqui a instância do agendador é mysched
task = mysched.queue_task(f, ...)
task_status = mysched.task_status(task.id, output=True)
traceback = task_status.scheduler_run.traceback
result = task_status.scheduler_run.run_result #or
result = task_status.result
Resultados e saída
A tabela "scheduler_run" armazena o status de todas as tarefas em execução. Cada registro faz referência a uma tarefa que foi escolhida por um trabalhador. Uma tarefa pode ter várias execuções. Por exemplo, uma tarefa programada para repetir 10 vezes por hora provavelmente terá 10 execuções (a menos que uma delas falhe ou demore mais de 1 hora). Esteja ciente de que, se a tarefa não tiver valores de retorno, ela será removida da tabela scheduler_run assim que for concluída.
Os possíveis estados de execução são:
RUNNING, COMPLETED, FAILED, TIMEOUT
Se a execução for concluída, nenhuma exceção será lançada e não haverá tempo limite de tarefa, a execução será marcada como COMPLETED
e a tarefa é marcada como QUEUED
ou COMPLETED
dependendo se é para ser executado novamente em um momento posterior. A saída da tarefa é serializada em JSON e armazenada no registro de execução.
Quando um RUNNING
tarefa lança uma exceção, a corrida é marca como FAILED
e a tarefa é marcada como FAILED
. O traceback é armazenado no registro de execução.
Da mesma forma, quando uma execução excede o tempo limite, ela é interrompida e marcada como TIMEOUT
, e a tarefa é marcada como TIMEOUT
.
Em qualquer caso, o stdout é capturado e também registrado no registro de execução.
Devido a limitações de multiprocessamento, cuidado ao usar valores de retorno enormes ou instruções de impressão enormes nas funções da fila. Como a saída é armazenada em buffer, sua tarefa pode falhar apenas porque o processo pai trava em valores de leitura. Além disso, deixe as instruções de impressão no mínimo e, se necessário, use uma biblioteca de registro adequada que não confunda a saída padrão. Quanto a valores de retorno enormes, uma opção melhor pode ser usar uma tabela onde a função salva o resultado: você pode retornar somente a referência à linha específica de resultados sem dificultar o processo mestre do escalonador.
Usando appadmin, pode-se verificar todos RUNNING
tarefas, a saída de COMPLETED
tarefas, o erro de FAILED
tarefas, etc.
O agendador também cria mais uma tabela chamada "scheduler_worker", que armazena a pulsação dos funcionários e seu status.
Gerenciando processos
O gerenciamento fino do trabalhador é difícil. Este módulo tenta não deixar para trás nenhuma plataforma (Mac, Win, Linux).
Quando você inicia um trabalhador, você pode querer mais tarde:
- mate "não importa o que esteja fazendo"
- mate-o apenas se não estiver processando tarefas
- colocá-lo para dormir
Talvez você ainda tenha algumas tarefas enfileiradas e queira economizar alguns recursos. Você sabe que você quer que eles sejam processados a cada hora, então, você vai querer:
- processar todas as tarefas enfileiradas e morrer automaticamente
Todas essas coisas são possíveis de gerenciar Scheduler
parâmetros ou o scheduler_worker
mesa. Para ser mais preciso, para os trabalhadores iniciados você pode mudar status
valor de qualquer trabalhador para influenciar seu comportamento. Quanto às tarefas, os trabalhadores podem estar em um dos seguintes status: ATIVOS, DESATIVADOS, TERMINADOS ou MATURADOS.
ATIVO e DESATIVADO são "persistentes", enquanto TERMINAR ou MATAR, como status sugiro o nome, são mais "comandos" do que status reais. Atingir ctrl + c é igual a definir um trabalhador para MATAR
Existem algumas funções de commodity desde a versão 2.4.1 (auto-explicativa)
scheduler.disable()
scheduler.resume()
scheduler.terminate()
scheduler.kill()
cada função recebe um parâmetro opcional, que pode ser uma string ou uma lista, para gerenciar os trabalhadores com base em suas group_names
. O padrão é o group_names
definido na istantiation scheduler.
Um exemplo é melhor que mil palavras: scheduler.terminate('high_prio')
TERMINARÁ todos os trabalhadores que estão processando o high_prio
tarefas, enquanto scheduler.terminate(['high_prio', 'low_prio'])
terminará tudo high_prio
e low_prio
trabalhadores.
Cuidado: se você tem um trabalhador processando
high_prio
elow_prio
,scheduler.terminate('high_prio')
terminará o trabalhador, mesmo que você não queira terminarlow_prio
também.
Tudo o que alguém pode fazer via appadmin pode fazer programaticamente inserindo e atualizando registros nessas tabelas.
De qualquer forma, não se deve atualizar registros relativos a RUNNING
tarefas como isso pode criar um comportamento inesperado. A melhor prática é enfileirar tarefas usando o método "queue_task".
Por exemplo:
scheduler.queue_task(function_name='task_add',
pargs=[],
pvars={'a': 3, 'b': 4},
repeats=10, # run 10 times
period=3600, # every 1h
timeout=120, # should take less than 120 seconds
)
Observe que os campos "times_run", "last_run_time" e "assigned_worker_name" não são fornecidos no horário programado, mas são preenchidos automaticamente pelos trabalhadores.
Você também pode recuperar a saída de tarefas concluídas:
completed_runs = db(db.scheduler_run.run_status='COMPLETED').select()
O escalonador é considerado experimental porque precisa de testes mais extensos e porque a estrutura da tabela pode mudar à medida que mais recursos são adicionados.
Relatando porcentagens de progresso
Uma "palavra" especial encontrada nas declarações de impressão de suas funções limpa todos a saída anterior. Essa palavra é !clear!
. Isso, juntamente com o sync_output
parâmetro, permite relatar porcentagens.
Aqui está um exemplo:
def reporting_percentages():
time.sleep(5)
print '50%'
time.sleep(5)
print '!clear!100%'
return 1
A função reporting_percentages
dorme por 5 segundos, saídas 50%
. Então, dorme outros 5 segundos e saídas 100%
. Observe que a saída na tabela scheduler_run é sincronizada a cada 2 segundos e que a segunda instrução de impressão que contém !clear!100%
Obtém o 50%
saída limpa e substituída por 100%
só.
scheduler.queue_task(reporting_percentages, sync_output=2)
Módulos de terceiros
O web2py é escrito em Python, portanto, pode importar e usar qualquer módulo Python, incluindo módulos de terceiros. Só precisa ser capaz de encontrá-los. Como em qualquer aplicativo Python, os módulos podem ser instalados no diretório oficial "site-packages" do Python, e eles podem ser importados de qualquer lugar dentro do seu código.
Módulos no diretório "site-packages" são, como o nome sugere, pacotes no nível do site. Aplicativos que exigem pacotes de sites não são portáteis, a menos que esses módulos sejam instalados separadamente. A vantagem de ter módulos em "pacotes de sites" é que vários aplicativos podem compartilhá-los. Vamos considerar, por exemplo, o pacote de plotagem chamado "matplotlib". Você pode instalá-lo a partir do shell usando o pico easy_install
comando [easy-install] (ou o seu substituto moderno pip
[PIP] ):
easy_install py-matplotlib
e então você pode importá-lo para qualquer modelo/controller/view com:
import matplotlib
A distribuição de origem web2py e a distribuição binária do Windows têm pacotes de site na pasta de nível superior. A distribuição binária do Mac tem uma pasta site-packages na pasta:
web2py.app/Contents/Resources/site-packages
O problema com o uso de pacotes de sites é que é difícil usar diferentes versões de um único módulo ao mesmo tempo, por exemplo, pode haver dois aplicativos, mas cada um usa uma versão diferente do mesmo arquivo. Neste exemplo, sys.path
não pode ser alterado porque afetaria os dois aplicativos.
Para este tipo de situação, o web2py fornece outra maneira de importar módulos de tal forma que o global sys.path
não é alterado: colocando-os na pasta "modules" de uma aplicação. Um benefício colateral é que o módulo será automaticamente copiado e distribuído com o aplicativo.
Uma vez que o módulo "mymodule.py" é colocado em uma pasta "modules /", ele pode ser importado de qualquer lugar dentro de um aplicativo web2py (sem precisar alterar
sys.path
com):import mymodule
Ambiente de execução
Embora tudo o que foi discutido aqui funcione bem, recomendamos que você crie seu aplicativo usando componentes, conforme descrito no capítulo 12.
O modelo web2py e os arquivos controladores não são módulos Python, pois não podem ser importados usando o Python import
declaração. A razão para isso é que os modelos e controladores são projetados para serem executados em um ambiente preparado que foi pré-preenchido com objetos globais web2py (solicitação, resposta, sessão, cache e T) e funções auxiliares. Isso é necessário porque o Python é uma linguagem com escopo estatístico (léxico), enquanto o ambiente web2py é criado dinamicamente.
web2py fornece o exec_environment
função para permitir que você acesse modelos e controladores diretamente. exec_environment
cria um ambiente de execução web2py, carrega o arquivo nele e retorna um objeto Storage contendo o ambiente. O objeto Storage também serve como um mecanismo de namespace. Qualquer arquivo Python projetado para ser executado no ambiente de execução pode ser carregado usando exec_environment
. Usos para exec_environment
incluir:
- Acessando dados (modelos) de outros aplicativos.
- Acessando objetos globais de outros modelos ou controladores.
- Execução de funções do controlador de outros controladores.
- Carregando bibliotecas auxiliares de todo o site.
Este exemplo lê as linhas do user
mesa no cas
aplicação:
from gluon.shell import exec_environment
cas = exec_environment('applications/cas/models/db.py')
rows = cas.db().select(cas.db.user.ALL)
Outro exemplo: suponha que você tenha um controlador "other.py" que contém:
def some_action():
return dict(remote_addr=request.env.remote_addr)
Aqui está como você pode chamar essa ação de outro controlador (ou do shell web2py):
from gluon.shell import exec_environment
other = exec_environment('applications/app/controllers/other.py', request=request)
result = other.some_action()
Na linha 2, request=request
é opcional. Tem o efeito de passar o pedido atual para o ambiente de "outro". Sem esse argumento, o ambiente conteria um novo e vazio (além de request.folder
) objeto de solicitação. Também é possível passar uma resposta e um objeto de sessão para exec_environment
. Tenha cuidado ao passar objetos de solicitação, resposta e sessão --- a modificação pela ação chamada ou as dependências de codificação na ação chamada podem levar a efeitos colaterais inesperados.
A chamada de função na linha 3 não executa a exibição; simplesmente retorna o dicionário a menos que response.render
é chamado explicitamente por "some_action".
Uma última advertência: não use exec_environment
inapropriadamente. Se você quiser os resultados de ações em outro aplicativo, você provavelmente deve implementar uma API XML-RPC (implementar uma API XML-RPC com web2py é quase trivial). Não use exec_environment
como mecanismo de redirecionamento; use o redirect
ajudante.
Cooperação
Existem muitas maneiras pelas quais as aplicações podem cooperar:
- Os aplicativos podem se conectar ao mesmo banco de dados e, portanto, compartilhar tabelas. Não é necessário que todas as tabelas no banco de dados sejam definidas por todos os aplicativos, mas devem ser definidas pelos aplicativos que as utilizam. Todos os aplicativos que usam a mesma tabela, mas um, devem definir a tabela com
migrate=False
. - Aplicativos podem incorporar componentes de outros aplicativos usando o auxiliar LOAD (descrito no Capítulo 12).
- Aplicativos podem compartilhar sessões.
- Aplicativos podem chamar ações uns dos outros remotamente via XML-RPC.
- Aplicativos podem acessar os arquivos uns dos outros através do sistema de arquivos (assumindo que eles compartilham o mesmo sistema de arquivos).
- Os aplicativos podem chamar ações uns dos outros localmente usando
exec_environment
como discutido acima. - Os aplicativos podem importar os módulos uns dos outros usando a sintaxe:
from applications.otherapp.modules import mymodule
ou
import applications.otherapp.modules.othermodule
- Aplicativos podem importar qualquer módulo no
PYTHONPATH
caminho de pesquisa,sys.path
.
Um aplicativo pode carregar a sessão de outro aplicativo usando o comando:
session.connect(request, response, masterapp='appname', db=db)
Aqui "appname" é o nome do aplicativo mestre, aquele que define o session_id inicial no cookie. db
é uma conexão de banco de dados ao banco de dados que contém a tabela de sessão ( web2py_session
). Todos os aplicativos que compartilham sessões devem usar o mesmo banco de dados para armazenamento de sessão.
Exploração madeireira
Python fornece APIs de registro. Web2py fornece um mecanismo para configurá-lo assim que os aplicativos podem usá-lo.
Em seu aplicativo, você pode criar um registrador, por exemplo, em um modelo:
import logging
logger = logging.getLogger("web2py.app.myapp")
logger.setLevel(logging.DEBUG)
e você pode usá-lo para registrar mensagens de várias importâncias
logger.debug("Just checking that %s" % details)
logger.info("You ought to know that %s" % details)
logger.warn("Mind that %s" % details)
logger.error("Oops, something bad happened %s" % details)
logging
é um módulo python padrão descrito aqui:
http://docs.python.org/library/logging.html
A string "web2py.app.myapp" define um registrador em nível de aplicativo.
Para que isso funcione corretamente, você precisa de um arquivo de configuração para o logger. Um é fornecido pelo web2py na pasta "examples" "logging.example.conf". Você precisa copiar o arquivo para o diretório web2py e renomear o arquivo para "logging.conf" e personalizá-lo conforme necessário.
Este arquivo é auto documentado, então você deve abri-lo e lê-lo.
Para criar um logger configurável para o aplicativo "myapp", você deve adicionar myapp para lista de chaves [loggers]:
[loggers]
keys=root,rocket,markdown,web2py,rewrite,app,welcome,myapp
e você deve adicionar uma seção [logger_myapp] usando [logger_welcome] como ponto de partida.
[logger_myapp]
level=WARNING
qualname=web2py.app.myapp
handlers=consoleHandler
propagate=0
A diretiva "handlers" especifica o tipo de registro e aqui está registrando "myapp" no console.
WSGI
web2py e WSGI têm um relacionamento de amor e ódio. Nossa perspectiva é que o WSGI foi desenvolvido como um protocolo para conectar servidores web a aplicativos da Web de maneira portátil, e nós o usamos para esse propósito. O Web2py em seu núcleo é um aplicativo WSGI: gluon.main.wsgibase
. Alguns desenvolvedores levaram o WSGI ao limite como um protocolo para comunicações de middleware e desenvolvem aplicativos da Web como uma cebola com muitas camadas (cada camada sendo um middleware WSGI desenvolvido independentemente de toda a estrutura). O web2py não adota essa estrutura internamente. Isso porque sentimos que a funcionalidade principal de um framework (manipulação de cookies, sessão, erros, transações, despacho) pode ser melhor otimizada para velocidade e segurança se forem tratadas por uma única camada abrangente.
No entanto, o web2py permite que você use aplicativos WSGI de terceiros e middleware de três maneiras (e suas combinações):
- Você pode editar o arquivo "wsgihandler.py" e incluir qualquer middleware WSGI de terceiros.
- Você pode conectar terceiros middleware WSGI a qualquer ação específica em seus aplicativos.
- Você pode chamar um aplicativo WSGI de terceiros de suas ações.
A única limitação é que você não pode usar middleware de terceiros para substituir as principais funções do web2py.
Middleware externo
Considere o arquivo "wsgibase.py":
#...
LOGGING = False
#...
if LOGGING:
application = gluon.main.appfactory(wsgiapp=gluon.main.wsgibase,
logfilename='httpserver.log',
profilerfilename=None)
else:
application = gluon.main.wsgibase
Quando LOGGING
está configurado para True
, gluon.main.wsgibase
é encapsulado pela função de middleware gluon.main.appfactory
. Ele fornece o log para o arquivo "httpserver.log". De maneira semelhante, você pode adicionar qualquer middleware de terceiros. Nós nos referimos à documentação oficial do WSGI para mais detalhes.
Middleware Interno
Dada qualquer ação em seus controladores (por exemplo index
) e qualquer aplicativo de middleware de terceiros (por exemplo, MyMiddleware
, que converte saída para maiúsculas), você pode usar um decorador web2py para aplicar o middleware a essa ação. Aqui está um exemplo:
class MyMiddleware:
"""converts output to upper case"""
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
items = self.app(environ, start_response)
return [item.upper() for item in items]
@request.wsgi.middleware(MyMiddleware)
def index():
return 'hello world'
Não podemos prometer que todo o middleware de terceiros funcionará com esse mecanismo.
Chamando aplicativos WSGI
É fácil chamar o aplicativo WSGI de uma ação web2py. Aqui está um exemplo:
def test_wsgi_app(environ, start_response):
"""this is a test WSGI app"""
status = '200 OK'
response_headers = [('Content-type', 'text/plain'),
('Content-Length', '13')]
start_response(status, response_headers)
return ['hello world!\n']
def index():
"""a test action that calls the previous app and escapes output"""
items = test_wsgi_app(request.wsgi.environ,
request.wsgi.start_response)
for item in items:
response.write(item, escape=False)
return response.body.getvalue()
Neste caso, o index
chamadas de ação test_wsgi_app
e escapa o valor retornado antes de devolvê-lo. Notar que index
não é em si um aplicativo WSGI e deve usar a API web2py normal (como response.write
para escrever no socket).