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:

password
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.

PAM

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:

command line
>>> 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_run
    conditional 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 em response.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:

image

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.

static files

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".

PARTIAL CONTENT
 
IF_MODIFIED_SINCE
Quando arquivos estáticos são baixados, o web2py não cria uma sessão, nem emite um cookie ou executa os modelos. O web2py sempre transmite arquivos estáticos em blocos de 1MB e envia o CONTEÚDO PARCIAL quando o cliente envia uma solicitação RANGE para um subconjunto do arquivo.

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 .)

request.application
 
request.controller
 
request.function
 
GET
 
POST
 
request.args
web2py mapeia solicitações GET/POST do formulário:

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
request.url

armazena o URL completo da solicitação atual (não incluindo variáveis GET).

request.ajax
 
request.cid

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.get_vars
 
request.post_vars
 
request.vars
Se o pedido HTTP for um GET, então 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) e html  (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:

jsonrpc

gluon/contrib/simplejsonrpc.py

memcache [memcache]   API do Python por Evan Martin:

gluon/contrib/memcache/__init__.py
gluon/contrib/memcache/memcache.py

redis_cache 

redis
 é um módulo para armazenar o cache no banco de dados do redis:

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:

heroku

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) e README (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:

about
 
license
 
cache
 
controllers
 
databases
 
errors
 
languages
 
models
 
modules
 
private
 
session
 
static
 
tests
 
uploads
 
views
 
__init__.py

__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

request, response, session, cache

Internacionalização: 

T
 
internationalization

T

Navegação: 

redirect
 
HTTP

redirect, HTTP

Assistentes: 

helpers

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: 

validators

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

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 listadas sys.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 porque current.request  é um objeto diferente em diferentes segmentos. Apenas tenha cuidado para não acessar current.request  fora de funções ou classes (ou seja, no nível superior) no módulo.
  • import mytest  é um atalho para from 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 : uma Cookie.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 : uma Storage  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 do request.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 e os.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 : uma datetime.datetime  objeto que armazena o datetime da solicitação atual.
  • request.utcnow : uma datetime.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 a request.env.path_info.split('/')[3:]
  • request.vars : uma gluon.storage.Storage  objeto contendo todos os parâmetros de solicitação.
  • request.get_vars : uma gluon.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 : uma gluon.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 presente request.env.http_x_forwarded_for  ou pela request.env.remote_addr  de outra forma. Embora isso seja útil, não deve ser confiável porque o http_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 suportar http_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 obter request.post_vars  e depois rebobinado. Pode ser lido com request.body.read() .
  • request.ajax  é True se a função estiver sendo chamada por meio de uma solicitação Ajax.
  • request.cid  é o id  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_settings
     contém configurações do sistema web2py. Eles são definidos automaticamente e você não deve alterá-los. Por exemplo request.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:

request
 
env

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
response.body
response.cookies
response.download
response.files
response.flash
response.headers
response.meta
response.menu
response.postprocessing
response.render
response.static_version
response.status
response.stream
response.subtitle
response.title
response.toolbar
response.view
response.delimiters
response.js
response.write
response.include_files
response.include_meta
response.optimize_css
response.optimize_js
response._caller
response.models_to_run

response  é outra instância do Storage  classe. Ele contém o seguinte:

  • response.body : uma StringIO  objeto no qual web2py escreve o corpo da página de saída. NUNCA MUDE ESTA VARIÁVEL.
  • response.cookies : igual a request.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 último arg  dentro request.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) e attachments  determina se o arquivo baixado deve ser tratado como um anexo ou não (padrão True ). Nota, response.download  é especificamente para baixar arquivos associados db  campos de upload. Usar response.stream  (veja abaixo) para outros tipos de downloads e streaming de arquivos. Além disso, observe que não é necessário usar response.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 todos response.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 : uma dict  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 como response.meta.author , .description e/ou .keywords . O conteúdo de cada meta variável é automaticamente colocado no META  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 todos response.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 tamanho chunk_size . o request  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 se attachment  é True, o cabeçalho Content-Disposition será definido como "anexo" e, se filename  também é fornecido, ele também será incluído no cabeçalho Content-Disposition (mas somente quando attachment  é verdade). Se ainda não estiver incluído response.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-las response.headers  antes de ligar response.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)
  
  ou, se o arquivo acima não puder ser localizado,   
  "generic.%s" % (request.extension)
  
  Altere o valor dessa variável para modificar o arquivo de exibição associado a uma ação específica.

  • 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 aplicativo models/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
session.connect
session.forget
session.secure
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
cache.ram
cache.disk
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 controller
@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 view
@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 clear
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.

  1. 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

  1. 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á

  1. 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

  1. 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

  1. 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

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

digitally signed URL

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 assinar
  • hash_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 para True  (o padrão) para incluir todas as variáveis, ou False  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 no T  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 um number  diretamente (por exemplo: %%{word(%i)} )
  • %%{?word?number}  retorna "palavra" se number==1 , retorna o number  de outra forma
  • %%{?number} or %%{??number}  retorna number  E se number!=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 a string.capitalize )
  • !!  para capitalizar cada palavra (equivalente a string.title )
  • !!!  para capitalizar todos os caracteres (equivalente a string.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

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

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.

default_application

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

url rewrite
routes_in
routes_out

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:

favicon
 
robots

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
routes_app

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
default_application
default_controller
default_function

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

routes_onerror

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 a request.env.request_uri
  • request_url : equivalente a request.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.

error_handler

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

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 um group_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 no scheduler_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 conjunto heartbeat )
  • 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

scheduler tasks

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âmetro timeout (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).

task repeats

Resumo: você tem

  • period  e repeats  obter uma função reprogramada automaticamente
  • timeout  para ter certeza de que uma função não excede um certo período de tempo
  • retry_failed  para controlar quantas vezes a tarefa pode "falhar"
  • start_time  e stop_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

workers statuses

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  e low_prio , scheduler.terminate('high_prio')  terminará o trabalhador, mesmo que você não queira terminar low_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

import

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

exec_environment

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

cooperation

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

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).

 top