Pylons 0.9.6 Cheat Sheet

Pylons 0.9.6 Cheat Sheet

Author: Copyright (C) 2007 Dipl.-Inform. Christoph Haas <email@christoph-haas.de>
IRC:Meet me at #pylons on irc.freenode.net as Signum
License:

This document is published under the terms of the MIT license:

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

The software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the software.

Note

O fonte desse documento está disponível em um repositório Mercurial em http://workaround.org/cgi-bin/hg-pylons-cheatsheet - por favor, envie patches para email@christoph-haas.de mesmo que você esteja lendo esse texto em wiki.pylonshq.com. Obrigado.

Pylons

Instalação

  • Debian/Ubuntu
    • Instalação: aptitude install python-pylons (disponível no lenny/testing ou sid/unstable mas não no Etch!)
    • Atualização: aptitude update && aptitude install python-pylons
  • Easy-Install:
    • Instalação: easy_install Pylons
    • Atualização: easy_install -U Pylons
  • virtualenv

Projeto (Paste)

  • Criar um novo projeto
    • paster create -t pylons myapplicationsname
    • Remova a página de boas vindas public/index.html
    • Configure o roteamento em config/routing.py (por exemplo, colocar o '' para iniciar um controlador)
  • Atualizar o projeto para a nova versão do Pylons: paster create -t pylons myapplicationsname (De um diretório acima de onde o seu development.ini está localizado)
  • Servir a aplicação via HTTP: paster serve --reload development.ini (a aplicação executa em http://localhost:5000)
  • Shell interativo: paster shell (instale o ipython para facilidades adicionais)
  • Detalhes de como funciona a execução do Pylons

development.ini

  • [server:main] (Paste)
    • use: aponta para o egg do servidor web do Paste
    • host: O IP que irá esperar as requisições
    • port: a porta TCP no qual o servidor web espera as requisições
  • [app:main] (Pylons)
    • use: aponta para a sua aplicação
    • full_stack: XXX
    • cache_dir: diretório onde os templates HTML em cache e as sessões são salvas

(%(here)s refere-se a raiz do projeto (onde está o development.ini))

  • Acessando configurações da seção [app:main] :
from pylons import config
my_setting = config['foo.bar']

Estrutura de Diretórios

data/sessions Sessões são salvas em arquivos nesse diretório.
data/templates Arquivos HTML em cache que são renderizados a partir de seus templates são armazenados aqui.
development.ini A configuração de inicialização do Paste.
myapplication/config/ Arquivos de configuração globais do seu projeto. Usados para definir rotas para o processamento de URLS, middleware (como a adição de autentição) ou o sistema de template de sua preferência
myapplication/controllers/ A localização das classes que contém a lógica da sua aplicação. Esse código controla sua aplicação, renderiza templates e consulta o banco de dados.
myapplication/docs/ Coloque qualquer documentação da aplicação aqui. De preferência no formato rest (restructured text).
myapplication/i18n/ Arquivos que lidam com as mensagens localizadas do seu projeto. (i18n = internacionalização)
myapplication/lib/ Contém arquivos que configuram certas variáveis globais e objetos que você pode usar em seus controladores. Por exemplo, tudo em lib/base.py está disponível em todos os seus controladores.
myapplication/model/ Aqui vai o model do seu banco de dados. Eles definem o esquema do banco de dados e configuram o mapeamento objeto-relacional
myapplication/public/ Arquivos estáticos como imagens, CSS ou javascripts devem ficar aqui
myapplication/templates/ Seus templates
myapplication/tests/ Cada controlador que você cria ganha uma contraparte para implementar testes automatizados aqui.
README.txt Algumas instruções básicas que você pode dar ao administrador que irá instalar sua aplicação
ez_setup, myapplication.egg-info, setup.cfg, setup.py Arquivos administrativos que são usados para criar um egg do seu projeto para que possa ser feito o deploy no servidor web.
test.ini Semelhante ao development.ini. Esse arquivo é usando quando você quer rodar testes automatizados no seu projeto

Controladores

  • Criar um novo controlador: paster controller name-of-new-controller
  • O controlador mycontroller está localizado em controllers/mycontroller.py como a classe MycontrollerController
  • O método index é chamado quando nenhuma ação é especificada
  • Todos os símbolos de lib/base.py são importados
  • Os parâmetros definidos no Routes podem ser aceitos como argumentos:
def index(self, id):
  return 'Your ID is ' + id
  • Outros parâmetros estão disponíveis através de request.params['my_paramter']
  • Valores de retorno
    • Uma string (unicode)
    • Renderização de um template Mako: render('/mytemplate')
    • Envio de um código de erro HTTP: abort(404)
    • Um iterador (Exemplo de um StreamingController
    • Redirecionando para outra URL: redirect_to(controller='start', action='about') ou redirect_to('/start/about')
  • Modificando o objeto de respota (a ser feito antes da declaração return da action:
response.headers['content-type'] = 'text/xml; charset=utf-8'
response.set_cookie('sitelang', 'uk')
response.status_code = 201

Objetos globais

Os dados da configuração estão disponíveis através do objeto config

  • Variáveis da seção app:main do development.ini:

    dicionário config['app_conf']

  • Variáveis da seção [DEFAULT] do development.ini:

    dicionário config['global_conf']

  • Caminho absoluto do arquivo ini: config['__file__']

  • Nome da aplicação: config['package'] or config['pylons.package']

  • Caminho para o projeto atual: config['here']

  • O objeto 'h' (webhelpers): config['pylons.h'] (configurado em config/environment.py)

  • O objeto 'g' (globals): config['pylons.g'] (configurado em config/environment.py)

  • A configuração do Path (onde controladores, templates e arquivos estáticos são

    localizados): config['pylons.paths'] (configurado em config/environment.py)

Routes

  • Links
  • Definidos em config/routing.py
  • O controlador inicial (para o caminho /) pode ser especificado como map.connect('', controller='start')
  • map.connect('newstoday', controller='news') -> chama ''class NewsController'' no arquivo controllers/news.py.
  • Argumentos adicionais são passados para o método do controlador como argumentos nomeados (e.g. def __index__(self, id, name, city):)

Mako (Templates)

  • Unicode
    • Inicie todos os templates com : # -*- coding: utf-8 -*-
  • Comandos (não esqueça o ':' final)
    • % for / % endfor
    • % if / % elif / % else / % endif
  • Impressão de variáveis
    • ${ c.variablename }
    • The closing bracket must not be used alone on a line

Template base de exemplo (a ser herdado)

# -*- coding: utf-8 -*-
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
    <title>...........</title>
    ${ h.stylesheet_link_tag( '/style1.css', '/style2.css') }
    ${ h.javascript_include_tag( 'jquery.js', 'jquery.debug.js') }
    <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
</head>
<body>
    <h1>My application</h1>
    ${ next.body() }
</body>
</html>

Herdando de um template base

# -*- coding: utf-8 -*-
<%inherit file="master.mako"/>
<p>Hello World</p>

Acesso a banco de dados

  • Links de documentação

  • pylons.database e SAContext estão obsoletos!

  • Veja Usando SQLAlchemy com Pylons.

  • development.ini:

    sqlalchemy.url = sqlite:///%(here)s/phonebook.db
    sqlalchemy.url = mysql://username:password@host:3306/database
    sqlalchemy.url = postgres://username:password@host:5432/database
    sqlalchemy.url = oracle://username:password@host:1521/sidname
    
  • Opções

  • Definindo tabelas e mapeamento objeto relacional in model/__init__.py

    • Mapeamento simples:
    import sqlalchemy as sql
    import sqlalchemy.orm as orm
    
    # Define table
    my_table = sql.Table(
        'tablename', metadata,
        sql.Column('id', sql.Integer, primary_key=True),    # gets a sequence/serial assigned
        sql.Column('othertable_id', sql.Integer, sql.ForeignKey("othertable.id"))
        sql.Column('name', sql.Unicode(80), nullable=False, unique=True)
    )
    
    # Define object class for ORM-mapping (every object matches one row)
    class MyModel(object):
        def __repr__(self):
            return "MyModel (%s)" % self.name
    
    orm.mapper(MyModel, my_table)
    
  • Teste o acesso aos modelos mapeados confortavelmente usando paster shell

  • Criando consultas e obtendo resultados

    • Obter uma linha com certo conteúdo em um campo (levanta uma exceção se mais linhas são retornadas): model.Session.query(model.MyModel).filter_by(name='John').one()

    • Obter um iterador de itens combinando com certo critério: model.Session.query(model.MyModel).filter(model.MyModel.name=='John')

    • Obter um iterador de itens com certo conteúdo: model.Session.query(model.MyModel).filter_by(name='John')

    • Obter um iterador de itens selecionados por uma condição SQL personalizada: model.Session.query(model.MyModel).filter_by("id<:value and name=:name").params(value=224, name='fred')

    • Obter a linha com a chave primária 42: model.Session.query(model.MyModel).get(42)

    • Obter todas as linhas: model.Session.query(model.MyModel).all()

    • Obter a primeira linha: model.Session.query(model.MyModel).first()

    • Obter a única linha(levanta uma exceção se mais linhas são retornadas): model.Session.query(model.MyModel).one()

    • Obter um número limitado de linhas (como uma lista)

      • model.Session.query(model.MyModel).offset(50).limit(10).all()
      • model.Session.query(model.MyModel)[10:50].all()
    • Ordenar linhas

      • model.Session.query(model.MyModel).order_by(model.MyModel.age).all()
      • model.Session.query(model.MyModel).order_by(model.sql.desc(model.MyModel.age)).all()
      • model.Session.query(model.MyModel).order_by([model.MyModel.age, model.MyModel.city]).all()
    • Contar linhas em um resultado: model.Session.query(model.MyModel).count()

    • Joins (cuidado com produtos cartesianos): model.Session.query(model.Model1, model.Model2).all()

    • Use filter and filter_by for generative queries:

      query = model.Session.query(model.MyModel)
      query = query.filter_by(name='John', type=189)
      query = query.filter_by(city='Hamburg')
      results = query.all()
      
    • Operadores lógicos

      • AND: and_(condition1, condition2, ...)
      • AND: (condition1 & condition2 & ...)
      • OR: or_(condition1, condition2, ...)
      • OR: (condition1 | condition2 | ...)
      • NOT: not_(condition)
      • NOT: ~(condition)
  • Operadores de consulta:

    • ==
    • <
    • >
    • <=
    • >=
    • startswith('foo')
    • endswith('.com')
    • like('%jean')
    • between(100,500)
    • in('A', 'CNAME', 'MX')
    • + (concatenation of strings)
    • op('...') (custom operator)
    • ==None (NULL comparison)
  • Funções de consulta:

    • Sintaxe geral: func.FUNCTIONNAME(...)
    • e.g. func.count() or func.now()
  • Criando novos objetos:

new_object = model.MyModel()
new_object.name = 'Jane'
new_object.city = 'Tokio'
model.Session.flush() # not needed if set autoflush=True
model.Session.commit() # needed because SQLAlchemy 0.4 uses transactions everywhere
  • Alterando objetos:
old_object = model.Session.query(model.MyModel).get(42)
old_object.city = 'Paris'
model.Session.flush() # not needed if set autoflush=True
model.Session.commit() # needed because SQLAlchemy 0.4 uses transactions everywhere
  • Removendo objetos (não use del(...)):
old_object = model.sac.query(model.MyModel).get(42)
model.Session.delete(old_object)
model.Session.flush() # not needed if set autoflush=True
model.Session.commit() # needed because SQLAlchemy 0.4 uses transactions everywhere
  • Seleções arbitrárias (retorna o que você especifica ao invés de linhas completas):
query = model.sql.select([model.MyModel.some_column])
result = model.Session.execute(query).fetchall() # or .fetchmany() or .fetchone()
  • Tipos
    • String(length=None) [é melhor usar Unicode]
    • Integer
    • SmallInteger
    • Numeric(precision=10, length=2)
    • Float(precision=10)
    • DateTime [corresponds to datetime.datetime]
    • Date [corresponds to datetime.date]
    • Time [corresponds to datetime.time]
    • Binary(length=None)
    • Boolean
    • Unicode(length=None)
    • PickleType

Sessões baseadas em cookies

  • Página do Beaker

  • development.ini

    • beaker.session.key = myproject_session (nome do cookie de sessão sendo enviado ao navegador)
    • beaker.session.secret = somesecret (random string that the cookie string sent to the user is signed with)
    • beaker.session.cookie_expires = True (whether the cookie is a session cookie that expires when the browser is closed)
    • beaker.session.timeout = ... (time in seconds until the session times out)
    • beaker.session.data_dir = ... (path where the 'sessions' directory is located storing the session data)
    • beaker.session.type = ... (Storage type for session information.)
      • dbm stores sessions in files on disk
      • file stores sessions in files on disk
      • memory stores sessions in RAM
      • ext:memcached stores sessions on memcached servers. Memcache servers are configured as beaker.session.url = server1, server2, ..
  • A variavel session vem de pylons.session e é importada em seu lib/base.py. Ela se comporta como um dicionário.

  • Carregando um valor da sessão:

    value = session['whatever']
    
  • Salvando um valor na sessão:

    session['whatever'] = value
    session.save()
    
  • Removendo uma chave da sessão:

    del session['whatever']
    session.save()
    
  • Limpando a sessão:

    session.clear() session.save()

Formencode

  • Documentação
  • Usando formencode:
    • Defina um schema de validação
    • Decore o método/ação do seu controlador (@formencode.validate(MySchema))
    • Se o formulário que o usuário enviou não validar então o formencode irá reexecutare a ação atual (ou executar a ação que você especificou como form) como em @formencode.validate(MySchema, form='anotheraction') e mostrar as mensagens de erro bem acima dos inputs com uma classe CSS chamada '.error-message'
  • Exemplo de classe de validação:
# My form schema
class MySchema(formencode.Schema):
    allow_extra_fields = True
    filter_extra_fields = True
    days = formencode.validators.IntRange(min=0, max=365, not_empty=True)
    name = formencode.validators.MaxLength(50)

# MySchema schema without the 'days' field
class MySchema2(MySchema):
    days = None   # (removes days field from the superclass)

# MySchema schema with additional 'comment' field
class MySchema3(MySchema):
    comment = String(min=20)
  • Subclass variables:
    • allow_extra_fields = False (whether to display an error if fields are found in the POST request that are not mentioned in this subclass)
    • filter_extra_fields = False (whether to remove fields that are not mentioned in this subclass) are missing in the POST request but defined in this subclass)
    • ignore_key_missing = False (whether to display an error if fields are missing in the POST request but defined in this subclass)
    • if_key_missing (fields that are missing in the POST request will get this value assigned)

Bibliotecas Javascript

Webhelpers

  • As funções de webhelpers.rails são importadas automaticamente
  • Controllers and templates can access the webhelpers module by the 'h' (helper) name
  • Links:

Internacionalização (i18n)

  • Instale o Babel:

    • Debian/Ubuntu: aptitude install python-babel
    • Outros sistemas operacionais: easy_install Babel
  • Artigo do wiki

  • Edite o seu setup.py e habilite a seção message_extractors para interpretar strings gettext nos templates.

  • Use a função "_" (um alias para a função gettext) sempre que você precisar de strings normais.

    • Controladores: _('Hello World')
    • Templates Mako : ${ _('Hello World') }
  • Primeira vez:

    • Criar um pot template no diretório i18n: python setup.py extract_messages
    • Criar um arquivo po para cada idioma (aqui o idioma é 'es'): python setup.py init_catalog -l es
    • Editar o arquivo po em i18n/es/LC_MESSAGES/*.po e adicionar strings traduzidas nos campos msgstr
    • Compile the po files into mo files: python setup.py compile_catalog
  • Update:

    • Overwrite the english pot template:: python setup.py extract_messages
    • Update the po files with new strings (does not overwrite the already translated strings): python setup.py update_catalog -l es
    • Compile the po files into mo files: python setup.py compile_catalog
  • Dica: paster serve --reload não detecta mudanças na internacionalização. Você irá precisar reiniciar o servidor web.

  • Para configurar a linguagem do gettext de acordo com a língua do navegador adicione isso ao seu lib/base.py:

    def __before__(self):
        user_agent_language = request.languages[0][0:2]
        set_lang(user_agent_language)
    

    request.languages is an array of preferred languages that the users sets in the browser. I.e. ['de', 'en', 'en-us'].

Comunidade

  • #pylons @ irc.freenode.net

Add-ons Relacionados

  • FormAlchemy: Cria formulários HTML automaticamente a partir de schemas do SQLAlchemy (classes mapeadas)
  • Paginator: Ajuda a dividir um número grande de resultados em páginas e permite que o usuário navegue entre as páginas
  • DBSprockets: Automaticamente cria formulários do Toscawidget e validadores do Formencode para schemas (classes mapeadas)

...

Mergulhando no Python Phython: Guia de Bolso Guia de Bolso Expressões Regulares Guia de Bolso Expressões Regulares Administração de Redes com Scripts Medo e Delírio em Las Vegas