<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="pt_BR"><title>Artigos</title><link href="http://artigos.waltercruz.com" rel="alternate"></link><id>http://artigos.waltercruz.com</id><updated>2009-08-18T14:59:59Z</updated><entry><title>Artigos</title><link href="http://artigos.waltercruz.com/index" rel="alternate"></link><updated>2009-08-18T14:59:59Z</updated><author><name>Walter Cruz</name><uri>http://waltercruz.com</uri></author><id>tag:artigos.waltercruz.com,2009-08-18:/index</id><summary type="html">&lt;div class="document" id="outros"&gt;
&lt;h1 class="title"&gt;Outros&lt;/h1&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.ohloh.net/accounts/waltercruz"&gt;Meu perfil no ohloh&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://djangopeople.net/waltercruz/"&gt;Meu perfil no djangopeople&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.pythonbrasil.com.br/moin.cgi/WalterCruz"&gt;Minha página no Python Brasil.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://lua-users.org/wiki/WalterCruz"&gt;Minha página no wiki de usuários da linguagem Lua.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.pythonbrasil.com.br/moin.cgi/PostgreSQLePython"&gt;Um pequeno texto abordando Stored Procedures em Python no PostgreSQL.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.pythonbrasil.com.br/moin.cgi/Decorators"&gt;Texto que comecei a escrever no wiki da Python Brasil sobre decorators. Incompleto ainda.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
</summary><category term="index"></category><category term="python"></category></entry><entry><title>Tutorial de agregação do Django</title><link href="http://artigos.waltercruz.com/agregacao_django_1_1" rel="alternate"></link><updated>2009-08-18T14:59:09Z</updated><author><name>Walter Cruz</name><uri>http://waltercruz.com</uri></author><id>tag:artigos.waltercruz.com,2009-08-18:/agregacao_django_1_1</id><summary type="html">&lt;div class="document" id="tutorial-de-agrega-o-do-django"&gt;
&lt;h1 class="title"&gt;Tutorial de agregação do Django&lt;/h1&gt;
&lt;p&gt;Originalmente publicado em &lt;a class="reference external" href="http://uswaretech.com/blog/2009/08/django-aggregation-tutorial/"&gt;http://uswaretech.com/blog/2009/08/django-aggregation-tutorial/&lt;/a&gt; , tradução de Walter Cruz&lt;/p&gt;
&lt;p&gt;Um das funcionalidades mais aguardados do Django 1.1 era a agregação. Como sempre, o Django vem com uma &lt;a class="reference external" href="http://docs.djangoproject.com/en/dev/topics/db/aggregation/"&gt;documentação muito completa&lt;/a&gt; sobre isso. Aqui, eu tentei colocar isso de uma forma mais semelhante a um howto.&lt;/p&gt;
&lt;p&gt;Pule para os howtos ou &lt;a class="reference external" href="http://github.com/uswaretech/Shiny-New-Django-1.1/tree/master"&gt;obtenha o código no Github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Para fazer essas operações o Django adicionou dois métodos aos querysets:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;aggregate&lt;/li&gt;
&lt;li&gt;annotate&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Quando você tem um queryset você pode fazer duas operações nele,&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Operar sobre o conjunto de linhas para obter um único valor dele. (Como a soma de todos os salários em um conjunto de linhas)&lt;/li&gt;
&lt;li&gt;Operar sobre o conjunto de linhas para obter um valor de cada linha no conjunto de linhas por meio de alguma tabela relacionada.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Deve ser reparado que a opção 1 irá criar uma linha do conjunto de dados, enquanto a opção 2 não irá mudar o número de linhas no conjunto de dados. Se você gosta de analogias, pode pensar na opção 1 como um reduce e na opção 2 como um map.&lt;/p&gt;
&lt;p&gt;Em termos de sql, agregação é uma operação (SUM, AVG, MIN, MAX) sem um group by, enquanto um annotate é uma operação com um group by em rowset_table.id. (A menos que explicitamente sobrescrito).&lt;/p&gt;
&lt;p&gt;Ok, falei demais, vamos ver isso funcionando. Aqui está um models.py fictício representando um sistema de departamento pessoal. Usaremos isso para ver como a agregação resolve nossos problemas comuns.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Department&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="n"&gt;dept_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;established_on&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DateField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__unicode__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dept_name&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Level&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="n"&gt;level_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;pay_min&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PositiveIntegerField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="n"&gt;pay_max&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PositiveIntegerField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__unicode__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;level_name&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="n"&gt;emp_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;department&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Department&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Level&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;reports_to&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;self&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blank&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

   &lt;span class="n"&gt;pay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PositiveIntegerField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="n"&gt;joined_on&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DateField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Leave&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="n"&gt;employee&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;leave_day&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DateField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;#Populate DB, so we can do some meaningful queries.&lt;/span&gt;
&lt;span class="sd"&gt;#Create Dept, Levels manually.&lt;/span&gt;
&lt;span class="sd"&gt;#Get the names file from http://dl.getdropbox.com/u/271935/djaggregations/names.pickle&lt;/span&gt;
&lt;span class="sd"&gt;#Or the whole sqlite database from http://dl.getdropbox.com/u/271935/djaggregations/bata.db&lt;/span&gt;
&lt;span class="sd"&gt;import random&lt;/span&gt;
&lt;span class="sd"&gt;from datetime import timedelta, date&lt;/span&gt;
&lt;span class="sd"&gt;import pickle&lt;/span&gt;
&lt;span class="sd"&gt;names = pickle.load(file(&amp;#39;/home/shabda/names.pickle&amp;#39;))&lt;/span&gt;
&lt;span class="sd"&gt;for i in range(1000):&lt;/span&gt;
&lt;span class="sd"&gt;    emp = Employee()&lt;/span&gt;
&lt;span class="sd"&gt;           emp.name = random.choice(names)&lt;/span&gt;
&lt;span class="sd"&gt;           emp.department = random.choice(list(Department.objects.all()))&lt;/span&gt;
&lt;span class="sd"&gt;           emp.level = random.choice(Level.objects.all())&lt;/span&gt;
&lt;span class="sd"&gt;           try: emp.reports_to = random.choice(list(Employee.objects.filter(department=emp.department)))&lt;/span&gt;
&lt;span class="sd"&gt;           except:pass&lt;/span&gt;
&lt;span class="sd"&gt;           emp.pay = random.randint(emp.level.pay_min, emp.level.pay_max)&lt;/span&gt;
&lt;span class="sd"&gt;           emp.joined_on = emp.department.established_on + timedelta(days = random.randint(0, 200))&lt;/span&gt;
&lt;span class="sd"&gt;           emp.save()&lt;/span&gt;
&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;employees = list(Employees.objects.all())&lt;/span&gt;
&lt;span class="sd"&gt;for i in range(100):&lt;/span&gt;
&lt;span class="sd"&gt;   employee = random.choice(employees)&lt;/span&gt;
&lt;span class="sd"&gt;   leave = Leave(employee = employee)&lt;/span&gt;
&lt;span class="sd"&gt;   leave.leave_day = date.today() - timedelta(days = random.randint(0, 365))&lt;/span&gt;
&lt;span class="sd"&gt;   leave.save()&lt;/span&gt;
&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="encontrar-o-n-mero-total-de-empregados"&gt;
&lt;h1&gt;Encontrar o número total de empregados.&lt;/h1&gt;
&lt;p&gt;Em sql você poderia fazer algo como,&lt;/p&gt;
&lt;p&gt;select count(id) from hrms_employee&lt;/p&gt;
&lt;p&gt;Que se torna,&lt;/p&gt;
&lt;p&gt;Employee.objects.all().aggregate(total=Count('id'))&lt;/p&gt;
&lt;p&gt;De fato, fazendo um connection.queries.pop() mostra exaramente essa consulta.&lt;/p&gt;
&lt;p&gt;SELECT COUNT(&amp;quot;hrms_employee&amp;quot;.&amp;quot;id&amp;quot;) AS &amp;quot;total&amp;quot; FROM &amp;quot;hrms_employee&amp;quot;&lt;/p&gt;
&lt;p&gt;Mas espera, já temos um método padrão para isso, o Employee.objects.all().count(), então vamos tentar outra coisa.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="encontrar-o-pagamento-total-dos-empregados"&gt;
&lt;h1&gt;Encontrar o pagamento total dos empregados&lt;/h1&gt;
&lt;p&gt;O CEO quer saber qual é o total gasto nos salários, isso também converte o queryset em um único valor, então vamos usad o .aggregate aqui.&lt;/p&gt;
&lt;p&gt;Employee.objects.all().aggregate(total_payment=Sum('pay'))&lt;/p&gt;
&lt;p&gt;Lhe informa a quantidade total que você paga a seus empregados.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="encontrar-o-n-mero-total-de-empregados-por-departamento"&gt;
&lt;h1&gt;Encontrar o número total de empregados, por departamento.&lt;/h1&gt;
&lt;p&gt;Aqui nós queremos um valor por linha no queryset, então precisamos usar um agregado aqui. Também, queremos um agregado por departamento, então precisamos anotar o queryset do Department.&lt;/p&gt;
&lt;p&gt;Department.objects.all().annotate(Count('employee'))&lt;/p&gt;
&lt;p&gt;Se você está apenas interessando no nome do departamento e no total de empregados nele, você pode fazer, Department.objects.values('dept_name').annotate(Count('employee'))&lt;/p&gt;
&lt;p&gt;O sql é&lt;/p&gt;
&lt;p&gt;SELECT &amp;quot;hrms_department&amp;quot;.&amp;quot;dept_name&amp;quot;, COUNT(&amp;quot;hrms_employee&amp;quot;.&amp;quot;id&amp;quot;) AS &amp;quot;employee__count&amp;quot; FROM &amp;quot;hrms_department&amp;quot; LEFT OUTER JOIN &amp;quot;hrms_employee&amp;quot; ON (&amp;quot;hrms_department&amp;quot;.&amp;quot;id&amp;quot; = &amp;quot;hrms_employee&amp;quot;.&amp;quot;department_id&amp;quot;) GROUP BY &amp;quot;hrms_department&amp;quot;.&amp;quot;dept_name&amp;quot;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="encontrar-o-n-mero-total-de-empregados-por-meio-de-um-departamnento-espec-fico"&gt;
&lt;h1&gt;Encontrar o número total de empregados, por meio de um departamnento específico.&lt;/h1&gt;
&lt;p&gt;Aqui você pode usar tanto o .annotate como o .aggregate&lt;/p&gt;
&lt;p&gt;Department.objects.filter(dept_name='Sales').values('dept_name').annotate(Count('employee'))
Department.objects.filter(dept_name='Sales').aggregate(Count('employee'))&lt;/p&gt;
&lt;p&gt;Se você olhar os SQLs, verá que o .annotate fez um group by, enquanto o .aggregate não, mas como só havia uma linha, o group by não teve efeito.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="encontrar-o-n-mero-total-de-empregados-e-seus-n-veis-por-departamento"&gt;
&lt;h1&gt;Encontrar o número total de empregados e seus níveis, por departamento&lt;/h1&gt;
&lt;p&gt;Dessa vez, queremos anotar ambos o modelo do Department ou do Level, já que precisamos agrupar tanto por um quanto pelo outro. Então vamos anotar no Employee&lt;/p&gt;
&lt;p&gt;Employee.objects.values('department__dept_name', 'level__level_name').annotate(Count('id'))&lt;/p&gt;
&lt;p&gt;Isso gera o sql,&lt;/p&gt;
&lt;p&gt;SELECT &amp;quot;hrms_department&amp;quot;.&amp;quot;dept_name&amp;quot;, &amp;quot;hrms_level&amp;quot;.&amp;quot;level_name&amp;quot;, COUNT(&amp;quot;hrms_employee&amp;quot;.&amp;quot;id&amp;quot;) AS &amp;quot;id__count&amp;quot; FROM &amp;quot;hrms_employee&amp;quot; INNER JOIN &amp;quot;hrms_department&amp;quot; ON (&amp;quot;hrms_employee&amp;quot;.&amp;quot;department_id&amp;quot; = &amp;quot;hrms_department&amp;quot;.&amp;quot;id&amp;quot;) INNER JOIN &amp;quot;hrms_level&amp;quot; ON (&amp;quot;hrms_employee&amp;quot;.&amp;quot;level_id&amp;quot; = &amp;quot;hrms_level&amp;quot;.&amp;quot;id&amp;quot;) GROUP BY &amp;quot;hrms_department&amp;quot;.&amp;quot;dept_name&amp;quot;, &amp;quot;hrms_level&amp;quot;.&amp;quot;level_name&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="que-combina-o-de-employee-e-deparments-emprega-a-maior-parte-das-pessoas"&gt;
&lt;h1&gt;Que combinação de Employee e Deparments emprega a maior parte das pessoas&lt;/h1&gt;
&lt;p&gt;Podemos ordenar pelos campos anotados, de forma que a última consulta fique assim,&lt;/p&gt;
&lt;p&gt;Employee.objects.values('department__dept_name', 'level__level_name').annotate(employee_count = Count('id')).order_by('-employee_count')[:1]&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="qual-o-nome-de-empregado-mais-comum"&gt;
&lt;h1&gt;Qual é o nome de empregado mais comum.&lt;/h1&gt;
&lt;p&gt;Podemos agrupar por emp_name, então emp_name é adicionado aos valores. Após isso nós ordenamos pelo campo anotado e objetos o primeiro elemento, para obter o nome mais comum.&lt;/p&gt;
&lt;p&gt;Employee.objects.values('emp_name').annotate(name_count=Count('id')).order_by('-name_count')[:1]&lt;/p&gt;
&lt;p&gt;Essa foi uma visão geral de como as anotações funcionam no django. Isso remove uma grabde parte de consultas para as quais você tinha de escrever sql na unha antigamente.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
</summary><category term="python"></category><category term="django"></category></entry><entry><title>Um Hello World para o Ruby em Ragel 6.0</title><link href="http://artigos.waltercruz.com/a-hello-world-for-ruby-on-ragel" rel="alternate"></link><updated>2009-07-28T23:08:26Z</updated><author><name>Walter Cruz</name><uri>http://waltercruz.com</uri></author><id>tag:artigos.waltercruz.com,2009-07-28:/a-hello-world-for-ruby-on-ragel</id><summary type="html">&lt;div class="document" id="um-hello-world-para-o-ruby-em-ragel-6-0"&gt;
&lt;h1 class="title"&gt;Um Hello World para o Ruby em Ragel 6.0&lt;/h1&gt;
&lt;p&gt;Tradução de: &lt;a class="reference external" href="http://www.devchix.com/2008/01/13/a-hello-world-for-ruby-on-ragel-60/"&gt;http://www.devchix.com/2008/01/13/a-hello-world-for-ruby-on-ragel-60/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Essa é uma versão atualizada &lt;a class="reference external" href="http://www.devchix.com/2007/12/13/a-hello-world-for-ruby-on-ragel/"&gt;desse tutorial&lt;/a&gt;. Essa versão atualizada é compatível com Ruby 1.8 e Ruby 1.9, e Ragel 6.0.&lt;/p&gt;
&lt;p&gt;No fim desse post, você será capaz de transformar uma simples string “h” em uma string muito mais interessante e maior, “hello world!”, usando a magia do Ragel, tudo com o conforto de Ruby. Ragel é um poderoso compilador de máquinas de estados e gerador de parser, que é o coração de softwares como Mongrel e Hpricot. É capaz de gerar código C, C++, Objective-C, D, Java ou código Ruby.&lt;/p&gt;
&lt;p&gt;Ragel tem documentação excelente fornecida pelo autor. Meu alvo aqui é somente fornecer algum contexto para que a documentação faça ainda mais sentido quando você a ler, e fornecer um exemplo que você pode modificar conforme explora a funcionalidade do Ragel. Se você quer ir direto ao ponto, o exemplo completo está &lt;a class="reference external" href="http://dev.agent.ie/svn/ragel/examples/simple_state_machine.rl"&gt;aqui&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;O primeiro passo, é claro, a instalação do Ragel. A &lt;a class="reference external" href="http://www.complang.org/ragel/"&gt;home page do Ragel&lt;/a&gt; tem uma seção de downloads que lista versões para várias plataformas. Se você já tem o Ragel instalado, verifique se a versão é 6.0 ou maior. Você pode também compilar e instalar o Ragel pelo fonte. Mesmo se você não quer instalar a partir do código fonte, é interessante ter uma cópia dele, pois ela contém alguns exemplos que você pode testar. O repositório subversion do ragel está localizado aqui:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
svn://mambo.cs.queensu.ca/ragel/trunk/
&lt;/pre&gt;
&lt;p&gt;Como de praxe, o diretório test/ é seu amigo, veja também o diretório examples/. Conforme essa &lt;a class="reference external" href="http://groups.google.com/group/ragel-users/browse_thread/thread/952c8cc75e6efef6"&gt;thread&lt;/a&gt;, tente procurar por  “LANG: ruby”.&lt;/p&gt;
&lt;p&gt;Ao escrever código Ragel, você pode criar um arquivo com uma extensão .rl. O arquivo .rl é escrito na linguagen &amp;quot;hospedeira&amp;quot;, nesse caso, Ruby, e a especificação da máquina de estados Ragel é embutida dentro do código Ruby usando delimitadores especiais. Não é obrigatório especificar uma máquina de estados, então um arquivo .rl perfeitamente válido pode ser:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
puts &amp;quot;hello world&amp;quot;
&lt;/pre&gt;
&lt;p&gt;Não se preocupe, eu farei um Hello World melhor que esse, mas esse é um bom lugar pra começar. Para converter esse arquivo .rl em um executável .rb file, use o comando &amp;quot;ragel&amp;quot; com o argumento -R para indicar que você quer código Ruby.&lt;/p&gt;
&lt;pre class="literal-block"&gt;
ragel -R hello_world.rl
&lt;/pre&gt;
&lt;p&gt;Isso irá criar um arquivo chamado hello_world.rb com o seguinte conteúdo:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# line 1 &amp;quot;hello_world.rl&amp;quot;
puts &amp;quot;hello world&amp;quot;
&lt;/pre&gt;
&lt;p&gt;Eu vou deixar a execução desse arquivo como um exercício para o estudante diligente.&lt;/p&gt;
&lt;p&gt;O Ragel na verdade faz essa conversão em dois estágios. Primeiro, ele criar um arquivo XML, então converte o XML para Ruby. Se você quer ver o XML intermediários então você pode especificar o argumento -x em adição ao argumento -R.&lt;/p&gt;
&lt;pre class="literal-block"&gt;
ragel -R -x simple_state_machine.rl &amp;gt; simple_state_machine.xml
&lt;/pre&gt;
&lt;p&gt;Agora, vamos escrever algum código Ragel pra valer. Inicie um novo arquivo .rl ou baixe o exemplo para acompanhar. Nós vamos criar uma máquina de estados que imprime a string &amp;quot;hello world!&amp;quot; quando recebe a string &amp;quot;h&amp;quot;. Para indicar ao compilar ragel que você está escrevendo instruções para ele, e não código Ruby, precisamos colocar nosso código Ragel dentro de dois sinais de porcento seguidos de chaves, %%{ e }%% , ou você pode simplesmente entrar uma instrução de uma única linha apenas digitando %%. (Veja a página 6 do Guia de Usuário.) Aqui está nossa especificação da máquina de estados:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
%%{
machine hello;
expr = &amp;quot;h&amp;quot;;
main := expr &amp;#64; { puts &amp;quot;hello world!&amp;quot; } ;
}%%
&lt;/pre&gt;
&lt;p&gt;Uma olhada rápida no que está acontecendo. O nome da máquina de estados é &amp;quot;hello&amp;quot; (o Ragel nos obrigada a nomeá-la). Ele reconhece um único token, a string &amp;quot;h&amp;quot;. Quando ele encontra esse token, ele executa (em Ruby) a ação:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
puts &amp;quot;hello world&amp;quot;
&lt;/pre&gt;
&lt;p&gt;Agora, se você executar o ragel nesse arquivo ele compilará, mas você vai terminar com um arquivo em Ruby em branco. Nós apenas especificamos a máquina, nós também temos de dizer ao Ragel como traduzir essa máquina em código Ruby usando as declarações de escrita do Ragel. A primeira declaração de escrita que precisamos adicionar é&lt;/p&gt;
&lt;pre class="literal-block"&gt;
%% write data;
&lt;/pre&gt;
&lt;p&gt;Se você adicionar essa linha depois do bloco de definição da máquina de estados, ela irá compilar, contando que você lembre-se de adicionar uma linha em branco depois. (Depois que você trabalhar com parsers por um tempo, você vai apreciar quebras de linha de uma nova forma.) Após adicionar essa linha e compilar, você deve ter um arquivo Ruby significante, com várias declarações de  class &amp;lt;&amp;lt; self, todas geradas pelo Ragel. Você não precisa estudar esse código, pelo menos não agora. Ele é muito idiota e feio. E se você executá-lo, você não verá nenhuma saída.&lt;/p&gt;
&lt;p&gt;Existem mais duas declarações write para adicionar, e por conveniência, vamos colocá-los dentro de um método ruby. O argumento para esse método será a string que queremos parsear. O Ragel expera encontrar um variável chamada &amp;quot;data&amp;quot; contendo um array de códigos ASCII, então precisaremos converter nossa string em um array. Isso é feito muito facilmente em Ruby usando o método unpack.&lt;/p&gt;
&lt;pre class="literal-block"&gt;
def run_machine(data)
  data = data.unpack(&amp;quot;c*&amp;quot;) if data.is_a?(String)
  %% write init;
  %% write exec;
end
&lt;/pre&gt;
&lt;p&gt;O write init diz ao Ragel que nós queremos gerar código de inicialização para a máquina de estados. O código Ragel gerado aqui é:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
begin
  p ||= 0
  pe ||= data.length
  cs = hello_start
end
&lt;/pre&gt;
&lt;p&gt;A variável p mantém o registro de qual caracter na string data nós estamos parseando agora, iniciando em 0. pe é um limite superior para p. cs mantém o estado atual da máquina de estados, e aqui é inicializado o estado de início da máquina de estados. Essas variáveis são discutidas no Guia de Usuário.&lt;/p&gt;
&lt;p&gt;write exec diz ao Ragel para escrever o recheio do parser (finalmente!). O código gerado aqui irá realmente pegar uma entrada (o argumento data) e determinar qual deverá ser o estado do sistema baseado nessa entrada, executando quaisquer ações que possam ser disparadas pelo caminho. Vamos adicionar algumas declarações puts  para que possamos seguir a execução do código.&lt;/p&gt;
&lt;pre class="literal-block"&gt;
def run_machine(data)
  data = data.unpack(&amp;quot;c*&amp;quot;) if data.is_a?(String)
  puts &amp;quot;Running the state machine with input #{data}...&amp;quot;

  %% write init;
  %% write exec;

  puts &amp;quot;Finished. The state of the machine is: #{cs}&amp;quot;
  puts &amp;quot;p: #{p} pe: #{pe}&amp;quot;
end
&lt;/pre&gt;
&lt;p&gt;Apenas adicione mais duas linhas ao fim da chamada run_machine com vários argumentos e então finalmente poderemos compilar e executar nossa máquina de estados.&lt;/p&gt;
&lt;pre class="literal-block"&gt;
run_machine &amp;quot;h&amp;quot;
run_machine &amp;quot;x&amp;quot;
&lt;/pre&gt;
&lt;p&gt;E lá vamos nós…&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Running the state machine with input 104...
hello world!
Finished. The state of the machine is: 2
p: 1 pe: 1
Running the state machine with input 120...
Finished. The state of the machine is: 0
p: 0 pe: 1
&lt;/pre&gt;
&lt;p&gt;Funcionou! Agora, para nos ajudar a interpretar os valores de p, pe e cs vamos dar uma olhada em um gráfico de estados dessa máquina de estados. O Ragel tem suporte embutido a Graphviz para criar gráficos de estados. Precisamos usar o argumento -V ao invés do -R.&lt;/p&gt;
&lt;pre class="literal-block"&gt;
ragel -V simple_state_machine.rl &amp;gt; simple_state_machine.dot
&lt;/pre&gt;
&lt;p&gt;Se você renderizer o arquivo simple_state_machine.dot resultante no Graphviz, você deve obter algo semelhante a:&lt;/p&gt;
&lt;img alt="State Chart for Simple State Machine" src="http://static.waltercruz.com.br/media/blogs/devlog/simple_state_machine.png" /&gt;
&lt;p&gt;Nós podemos ver que essa máquina tem apenas uma transição possível, do estado 1 para o estado 2. Quando passamos &amp;quot;h&amp;quot; como parâmetro para run_machine nós terminaremos com a variável cs (current state) igual a 2 no fim de nossa execução. Quando &amp;quot;x&amp;quot; foi passado, nós terminamos com cs = 0. 0 é o estado de erro, indicando que um erro ocorreu na máquina de estados. (Você pode dizer que 0 é o estado de erro lendo algumas das atribuições de variáveis geradas pelo write data, o código que eu disse que era burro e feio.)&lt;/p&gt;
&lt;p&gt;No label 104/4:18 sobre a seta na transição do estado 1 para o estado 2, o label 104 corresponde ao código ASCII para a letra &amp;quot;h&amp;quot;. (Digite “?h” no irb.) A / indica que uma ação está sendo executada, e 4:18 nos diz que a ação inicia na linha 4, coluna 18 do arquivo .rl. Se nós tivéssemos dado um nome a nossa ação, isso teria aparecido no lugar da posição no arquivo.&lt;/p&gt;
&lt;p&gt;A propósito, aqui está o shell script (específico do textmate) que eu usei para executar esses passos rapidamente:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
ragel -R simple_state_machine.rl
ragel -V simple_state_machine.rl &amp;gt; simple_state_machine.dot
dot -Tpng simple_state_machine.dot &amp;gt; simple_state_machine.png
open simple_state_machine.png
ruby simple_state_machine.rb
mate simple_state_machine.out
&lt;/pre&gt;
&lt;p&gt;Agora, tente executar esse código:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
run_machine &amp;quot;hh&amp;quot;
&lt;/pre&gt;
&lt;p&gt;Você deve obter:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Running the state machine with input 104104...
hello world!
Finished. The state of the machine is: 0
p: 1 pe: 2
&lt;/pre&gt;
&lt;p&gt;Você não obtém &amp;quot;hello world!&amp;quot; duas vezes. Desculpe. Nossa máquina de estados está somente procurando o primeiro caractere que passamos. Ela sabe que passamos dois caracteres, demonstrado pela variável pe = 2, mas após avaliar o primeiro caractere, ela está no estado final. Não existe seta saindo do círculo no estado 2. Assim,  a passagem de inputs adicionais resulta no sistema entrando no estado de erro. Se queremos que toda a string data seja executada, precisamos fazer uma pequena mudança na especificação de nossa máquina de estados.&lt;/p&gt;
&lt;pre class="literal-block"&gt;
main := expr+ &amp;#64; { puts &amp;quot;hello world!&amp;quot; } ;
&lt;/pre&gt;
&lt;img alt="Endless Simple State Machine" src="http://static.waltercruz.com.br/media/blogs/devlog/never_ending_simple_state_machine.png" /&gt;
&lt;p&gt;(Tente expr* ao invés de expr+ e veja como o gráfico de estados é diferente.)&lt;/p&gt;
&lt;p&gt;Agora, tente executar essa nova máquina de, com as entradas &amp;quot;hhh&amp;quot; e &amp;quot;hxh&amp;quot;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Running the state machine with input 104104104...
hello world!
hello world!
hello world!
Finished. The state of the machine is: 2
p: 3 pe: 3
Running the state machine with input 104120104...
hello world!
Finished. The state of the machine is: 0
p: 1 pe: 3
&lt;/pre&gt;
&lt;p&gt;Quando passamos &amp;quot;hhh&amp;quot;, nós obtivemos um &amp;quot;hello world!&amp;quot; para cada &amp;quot;h&amp;quot;. Quando passamos &amp;quot;hxh&amp;quot;, nós obtivemos o primeiro &amp;quot;hello world!&amp;quot;, mas quando chegamos ao &amp;quot;x&amp;quot; entramos no estado de erro, então o último &amp;quot;h&amp;quot; não foi avaliado.&lt;/p&gt;
&lt;p&gt;Aqui está mais um &lt;cite&gt;exemplo &amp;lt;http://dev.agent.ie/svn/ragel/examples/multiple_state_machine.rl&amp;gt;&lt;/cite&gt;, dessa vez sem definir um método run_machine:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
%%{
  machine hello_and_welcome;
  main := ( 'h' &amp;#64; { puts &amp;quot;hello world!&amp;quot; }
          | 'w' &amp;#64; { puts &amp;quot;welcome&amp;quot; }
          )*;
}%%
  data = 'whwwwwhw'
  %% write data;
  %% write init;
  %% write exec;
&lt;/pre&gt;
&lt;img alt="Hello and Welcome State Machine" src="http://devlog.waltercruz.com/media/blogs/devlog/multiple_state_machine.png" /&gt;
&lt;pre class="literal-block"&gt;
welcome
hello world!
welcome
welcome
welcome
welcome
hello world!
welcome
&lt;/pre&gt;
&lt;p&gt;Então, é isso. Horas de entretenimento o aguardam. Tivemos uma visão muito pequena das característica do Ragel aqui, mas você deve ser capaz de acompanhar o Guia do Usuário sem muito problema. Se você precisa de uma razão melhor que &amp;quot;divertimento&amp;quot; para brincar com o Ragel, tenha em mente que parsers são uma grande ferramenta para construir Domain Specific Languages (DSLs), e máquinas de estados são máquinas mágicas de encolhimento de código para situações onde você precisa seguir o estado de algo e controlar as mudanças entre os estados(i.e. lógica de negócio). Eu recomendaria que todos lessem &lt;a class="reference external" href="http://www.zedshaw.com/tips/ragel_state_charts.html"&gt;esse artigo sobre o Ragel&lt;/a&gt; que me inspirou a estudá-lo. Se você usa Rails, então veja o plugin acts_as_state_machine que pode ser mais intuitivo que o Ragel no início. Se DSLs são a sua preferência, então você pode querer dar uma olhada no &lt;a class="reference external" href="http://www.antlr.org/"&gt;ANTLR&lt;/a&gt; , que tem um foco e conjunto de características diferentes do Ragel.&lt;/p&gt;
&lt;/div&gt;
</summary><category term="compiladores"></category><category term="ruby"></category></entry><entry><title>Pylons 0.9.6 Cheat Sheet</title><link href="http://artigos.waltercruz.com/pylons_cheatsheet" rel="alternate"></link><updated>2008-11-16T14:40:43Z</updated><author><name>Walter Cruz</name><uri>http://waltercruz.com</uri></author><id>tag:artigos.waltercruz.com,2008-11-16:/pylons_cheatsheet</id><summary type="html">&lt;div class="document" id="pylons-0-9-6-cheat-sheet"&gt;
&lt;h1 class="title"&gt;Pylons 0.9.6 Cheat Sheet&lt;/h1&gt;
&lt;table class="docinfo" frame="void" rules="none"&gt;
&lt;col class="docinfo-name" /&gt;
&lt;col class="docinfo-content" /&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;th class="docinfo-name"&gt;Author:&lt;/th&gt;
&lt;td&gt;Copyright (C) 2007 Dipl.-Inform. Christoph Haas &amp;lt;&lt;a class="reference external" href="mailto:email&amp;#64;christoph-haas.de"&gt;email&amp;#64;christoph-haas.de&lt;/a&gt;&amp;gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr class="field"&gt;&lt;th class="docinfo-name"&gt;IRC:&lt;/th&gt;&lt;td class="field-body"&gt;Meet me at #pylons on irc.freenode.net as Signum&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="field"&gt;&lt;th class="docinfo-name"&gt;License:&lt;/th&gt;&lt;td class="field-body"&gt;&lt;p class="first"&gt;This document is published under the terms of the MIT license:&lt;/p&gt;
&lt;p&gt;Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the &amp;quot;Software&amp;quot;),
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:&lt;/p&gt;
&lt;p&gt;The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.&lt;/p&gt;
&lt;p class="last"&gt;The software is provided &amp;quot;as is&amp;quot;, 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.&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;!-- -*- coding: utf-8 -*-

This file is written in REST (restructured text) format.
The format is documented at http://docutils.sourceforge.net/docs/
Install the "python-docutils" Debian package to get tools like
rst2html or rst2latex that convert this file into other formats. --&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;O fonte desse documento está disponível em um repositório Mercurial
em &lt;a class="reference external" href="http://workaround.org/cgi-bin/hg-pylons-cheatsheet"&gt;http://workaround.org/cgi-bin/hg-pylons-cheatsheet&lt;/a&gt; -
por favor, envie patches para &lt;a class="reference external" href="mailto:email&amp;#64;christoph-haas.de"&gt;email&amp;#64;christoph-haas.de&lt;/a&gt; mesmo que você esteja
lendo esse texto em wiki.pylonshq.com. Obrigado.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title first"&gt;Contents&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#pylons" id="id1"&gt;Pylons&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="#links" id="id2"&gt;Links&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#instala-o" id="id3"&gt;Instalação&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#projeto-paste" id="id4"&gt;Projeto (Paste)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#development-ini" id="id5"&gt;development.ini&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#estrutura-de-diret-rios" id="id6"&gt;Estrutura de Diretórios&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#controladores" id="id7"&gt;Controladores&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#objetos-globais" id="id8"&gt;Objetos globais&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#routes" id="id9"&gt;Routes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#mako-templates" id="id10"&gt;Mako (Templates)&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="#template-base-de-exemplo-a-ser-herdado" id="id11"&gt;Template base de exemplo (a ser herdado)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#herdando-de-um-template-base" id="id12"&gt;Herdando de um template base&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#acesso-a-banco-de-dados" id="id13"&gt;Acesso a banco de dados&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#sess-es-baseadas-em-cookies" id="id14"&gt;Sessões baseadas em cookies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#formencode" id="id15"&gt;Formencode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#bibliotecas-javascript" id="id16"&gt;Bibliotecas Javascript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#webhelpers" id="id17"&gt;Webhelpers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#internacionaliza-o-i18n" id="id18"&gt;Internacionalização (i18n)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#testes-unit-rios-nose" id="id19"&gt;Testes Unitários (Nose)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#comunidade" id="id20"&gt;Comunidade&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#add-ons-relacionados" id="id21"&gt;Add-ons Relacionados&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="pylons"&gt;
&lt;h1&gt;&lt;a class="toc-backref" href="#id1"&gt;Pylons&lt;/a&gt;&lt;/h1&gt;
&lt;div class="section" id="links"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id2"&gt;Links&lt;/a&gt;&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://pylonshq.com/"&gt;Home&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://wiki.pylonshq.com/display/pylonsdocs/Getting+Started"&gt;Iniciando&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://pylonshq.com/docs/module-index.html"&gt;Referência dos módulos&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://wiki.pylonshq.com/display/pylonsfaq/Home"&gt;FAQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://wiki.pylonshq.com/dashboard.action"&gt;Wiki&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="instala-o"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id3"&gt;Instalação&lt;/a&gt;&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Debian/Ubuntu&lt;ul&gt;
&lt;li&gt;Instalação: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;aptitude&lt;/span&gt; &lt;span class="pre"&gt;install&lt;/span&gt; &lt;span class="pre"&gt;python-pylons&lt;/span&gt;&lt;/tt&gt; (disponível no lenny/testing ou
sid/unstable mas não no Etch!)&lt;/li&gt;
&lt;li&gt;Atualização: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;aptitude&lt;/span&gt; &lt;span class="pre"&gt;update&lt;/span&gt; &lt;span class="pre"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="pre"&gt;aptitude&lt;/span&gt; &lt;span class="pre"&gt;install&lt;/span&gt; &lt;span class="pre"&gt;python-pylons&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Easy-Install:&lt;ul&gt;
&lt;li&gt;Instalação: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;easy_install&lt;/span&gt; &lt;span class="pre"&gt;Pylons&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Atualização: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;easy_install&lt;/span&gt; &lt;span class="pre"&gt;-U&lt;/span&gt; &lt;span class="pre"&gt;Pylons&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://python.org/pypi/virtualenv/"&gt;virtualenv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="projeto-paste"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id4"&gt;Projeto (Paste)&lt;/a&gt;&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Criar um novo projeto&lt;ul&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;paster&lt;/span&gt; &lt;span class="pre"&gt;create&lt;/span&gt; &lt;span class="pre"&gt;-t&lt;/span&gt; &lt;span class="pre"&gt;pylons&lt;/span&gt; &lt;span class="pre"&gt;myapplicationsname&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Remova a página de boas vindas &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;public/index.html&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Configure o roteamento em &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;config/routing.py&lt;/span&gt;&lt;/tt&gt; (por exemplo, colocar o '' para iniciar um controlador)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Atualizar o projeto para a nova versão do Pylons: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;paster&lt;/span&gt; &lt;span class="pre"&gt;create&lt;/span&gt; &lt;span class="pre"&gt;-t&lt;/span&gt; &lt;span class="pre"&gt;pylons&lt;/span&gt; &lt;span class="pre"&gt;myapplicationsname&lt;/span&gt;&lt;/tt&gt;
(De um diretório acima de onde o seu development.ini está localizado)&lt;/li&gt;
&lt;li&gt;Servir a aplicação via HTTP: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;paster&lt;/span&gt; &lt;span class="pre"&gt;serve&lt;/span&gt; &lt;span class="pre"&gt;--reload&lt;/span&gt; &lt;span class="pre"&gt;development.ini&lt;/span&gt;&lt;/tt&gt;
(a aplicação executa em &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;http://localhost:5000&lt;/span&gt;&lt;/tt&gt;)&lt;/li&gt;
&lt;li&gt;Shell interativo: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;paster&lt;/span&gt; &lt;span class="pre"&gt;shell&lt;/span&gt;&lt;/tt&gt; (instale o &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ipython&lt;/span&gt;&lt;/tt&gt; para facilidades adicionais)&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://sluggo.scrapping.cc/python/pylons/pylons-execution.html"&gt;Detalhes de como funciona a execução do Pylons&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="development-ini"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id5"&gt;development.ini&lt;/a&gt;&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;[server:main]&lt;/span&gt;&lt;/tt&gt; (Paste)&lt;ul&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;use&lt;/span&gt;&lt;/tt&gt;: aponta para o egg do servidor web do Paste&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;host&lt;/span&gt;&lt;/tt&gt;: O IP que irá esperar as requisições&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;port&lt;/span&gt;&lt;/tt&gt;: a porta TCP no qual o servidor web espera as requisições&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;[app:main]&lt;/span&gt;&lt;/tt&gt; (Pylons)&lt;ul&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;use&lt;/span&gt;&lt;/tt&gt;: aponta para a sua aplicação&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;full_stack&lt;/span&gt;&lt;/tt&gt;: XXX&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cache_dir&lt;/span&gt;&lt;/tt&gt;: diretório onde os templates HTML em cache e as sessões são salvas&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;(&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;%(here)s&lt;/span&gt;&lt;/tt&gt; refere-se a raiz do projeto (onde está o development.ini))&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Acessando configurações da seção &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;[app:main]&lt;/span&gt;&lt;/tt&gt; :&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pylons&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;
&lt;span class="n"&gt;my_setting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;foo.bar&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="estrutura-de-diret-rios"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id6"&gt;Estrutura de Diretórios&lt;/a&gt;&lt;/h2&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="43%" /&gt;
&lt;col width="57%" /&gt;
&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;data/sessions&lt;/td&gt;
&lt;td&gt;Sessões são salvas em arquivos nesse
diretório.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;data/templates&lt;/td&gt;
&lt;td&gt;Arquivos HTML em cache que são
renderizados a partir de seus templates
são armazenados aqui.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;development.ini&lt;/td&gt;
&lt;td&gt;A configuração de inicialização
do Paste.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;myapplication/config/&lt;/td&gt;
&lt;td&gt;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&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;myapplication/controllers/&lt;/td&gt;
&lt;td&gt;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.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;myapplication/docs/&lt;/td&gt;
&lt;td&gt;Coloque qualquer documentação da
aplicação aqui. De preferência no formato
rest (restructured text).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;myapplication/i18n/&lt;/td&gt;
&lt;td&gt;Arquivos que lidam com as mensagens
localizadas do seu projeto.
(i18n = internacionalização)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;myapplication/lib/&lt;/td&gt;
&lt;td&gt;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.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;myapplication/model/&lt;/td&gt;
&lt;td&gt;Aqui vai o model do seu banco de dados.
Eles definem o esquema do banco de dados
e configuram o mapeamento objeto-relacional&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;myapplication/public/&lt;/td&gt;
&lt;td&gt;Arquivos estáticos como imagens, CSS
ou javascripts devem ficar aqui&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;myapplication/templates/&lt;/td&gt;
&lt;td&gt;Seus templates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;myapplication/tests/&lt;/td&gt;
&lt;td&gt;Cada controlador que você cria
ganha uma contraparte para implementar
testes automatizados aqui.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;README.txt&lt;/td&gt;
&lt;td&gt;Algumas instruções básicas que você
pode dar ao administrador
que irá instalar sua aplicação&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;ez_setup,
myapplication.egg-info,
setup.cfg,
setup.py&lt;/td&gt;
&lt;td&gt;Arquivos administrativos que são usados
para criar um egg do seu projeto
para que possa ser feito o deploy
no servidor web.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;test.ini&lt;/td&gt;
&lt;td&gt;Semelhante ao development.ini.
Esse arquivo é usando quando você quer
rodar testes automatizados no seu projeto&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="controladores"&gt;
&lt;h1&gt;&lt;a class="toc-backref" href="#id7"&gt;Controladores&lt;/a&gt;&lt;/h1&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Criar um novo controlador: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;paster&lt;/span&gt; &lt;span class="pre"&gt;controller&lt;/span&gt; &lt;span class="pre"&gt;name-of-new-controller&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;O controlador &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;mycontroller&lt;/span&gt;&lt;/tt&gt; está localizado em &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;controllers/mycontroller.py&lt;/span&gt;&lt;/tt&gt; como a classe &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;MycontrollerController&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;O método &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;index&lt;/span&gt;&lt;/tt&gt;  é chamado quando nenhuma ação é especificada&lt;/li&gt;
&lt;li&gt;Todos os símbolos de &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;lib/base.py&lt;/span&gt;&lt;/tt&gt; são importados&lt;/li&gt;
&lt;li&gt;Os parâmetros definidos no Routes podem ser aceitos como argumentos:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Your ID is &amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Outros parâmetros estão disponíveis através de  &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;request.params['my_paramter']&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Valores de retorno&lt;ul&gt;
&lt;li&gt;Uma string (unicode)&lt;/li&gt;
&lt;li&gt;Renderização de um template Mako: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;render('/mytemplate')&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Envio de um código de erro HTTP: abort(404)&lt;/li&gt;
&lt;li&gt;Um iterador (&lt;a class="reference external" href="http://docs.pythonweb.org/display/pylonsfaq/Streaming+Content+to+the+Browser"&gt;Exemplo de um StreamingController&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Redirecionando para outra URL: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;redirect_to(controller='start',&lt;/span&gt; &lt;span class="pre"&gt;action='about')&lt;/span&gt;&lt;/tt&gt;
ou &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;redirect_to('/start/about')&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Modificando o objeto de respota (a ser feito antes da declaração &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;return&lt;/span&gt;&lt;/tt&gt; da action:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;content-type&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;text/xml; charset=utf-8&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_cookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;sitelang&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;uk&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;201&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="objetos-globais"&gt;
&lt;h1&gt;&lt;a class="toc-backref" href="#id8"&gt;Objetos globais&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Os dados da configuração estão disponíveis através do objeto &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;config&lt;/span&gt;&lt;/tt&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;Variáveis da seção &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;app:main&lt;/span&gt;&lt;/tt&gt; do &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;development.ini&lt;/span&gt;&lt;/tt&gt;:&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first last"&gt;dicionário &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;config['app_conf']&lt;/span&gt;&lt;/tt&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;Variáveis da seção &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;[DEFAULT]&lt;/span&gt;&lt;/tt&gt; do &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;development.ini&lt;/span&gt;&lt;/tt&gt;:&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first last"&gt;dicionário &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;config['global_conf']&lt;/span&gt;&lt;/tt&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Caminho absoluto do arquivo ini: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;config['__file__']&lt;/span&gt;&lt;/tt&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Nome da aplicação: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;config['package']&lt;/span&gt;&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;config['pylons.package']&lt;/span&gt;&lt;/tt&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Caminho para o projeto atual: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;config['here']&lt;/span&gt;&lt;/tt&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;O objeto 'h' (webhelpers): &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;config['pylons.h']&lt;/span&gt;&lt;/tt&gt; (configurado em config/environment.py)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;O objeto 'g' (globals): &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;config['pylons.g']&lt;/span&gt;&lt;/tt&gt; (configurado em config/environment.py)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;A configuração do Path (onde controladores, templates e arquivos estáticos são&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first last"&gt;localizados): &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;config['pylons.paths']&lt;/span&gt;&lt;/tt&gt; (configurado em config/environment.py)&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="routes"&gt;
&lt;h1&gt;&lt;a class="toc-backref" href="#id9"&gt;Routes&lt;/a&gt;&lt;/h1&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Links&lt;ul&gt;
&lt;li&gt;&lt;a class="reference external" href="http://routes.groovie.org/"&gt;Home&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://routes.groovie.org/manual.html"&gt;Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Definidos em &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;config/routing.py&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;O controlador inicial (para o caminho &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/&lt;/span&gt;&lt;/tt&gt;) pode ser especificado como &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;map.connect('',&lt;/span&gt; &lt;span class="pre"&gt;controller='start')&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;map.connect('newstoday',&lt;/span&gt; &lt;span class="pre"&gt;controller='news')&lt;/span&gt;&lt;/tt&gt; -&amp;gt; chama ''class
NewsController'' no  arquivo &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;controllers/news.py&lt;/span&gt;&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;Argumentos adicionais são passados para o método do controlador como argumentos nomeados
(e.g. &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;def&lt;/span&gt; &lt;span class="pre"&gt;__index__(self,&lt;/span&gt; &lt;span class="pre"&gt;id,&lt;/span&gt; &lt;span class="pre"&gt;name,&lt;/span&gt; &lt;span class="pre"&gt;city):&lt;/span&gt;&lt;/tt&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="mako-templates"&gt;
&lt;h1&gt;&lt;a class="toc-backref" href="#id10"&gt;Mako (Templates)&lt;/a&gt;&lt;/h1&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Unicode&lt;ul&gt;
&lt;li&gt;Inicie todos os templates com : &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;#&lt;/span&gt; &lt;span class="pre"&gt;-*-&lt;/span&gt; &lt;span class="pre"&gt;coding:&lt;/span&gt; &lt;span class="pre"&gt;utf-8&lt;/span&gt; &lt;span class="pre"&gt;-*-&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Comandos (não esqueça o ':' final)&lt;ul&gt;
&lt;li&gt;% for / % endfor&lt;/li&gt;
&lt;li&gt;% if / % elif / % else / % endif&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Impressão de variáveis&lt;ul&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;${&lt;/span&gt; &lt;span class="pre"&gt;c.variablename&lt;/span&gt; &lt;span class="pre"&gt;}&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;The closing bracket must not be used alone on a line&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="section" id="template-base-de-exemplo-a-ser-herdado"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id11"&gt;Template base de exemplo (a ser herdado)&lt;/a&gt;&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;# -*- coding: utf-8 -*-
&lt;span class="cp"&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Strict//EN&amp;quot;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&amp;quot;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;en&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;xml:lang=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;en&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;...........&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    ${ h.stylesheet_link_tag( &amp;#39;/style1.css&amp;#39;, &amp;#39;/style2.css&amp;#39;) }
    ${ h.javascript_include_tag( &amp;#39;jquery.js&amp;#39;, &amp;#39;jquery.debug.js&amp;#39;) }
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;shortcut icon&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;image/x-icon&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/favicon.ico&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;My application&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    ${ next.body() }
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="herdando-de-um-template-base"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id12"&gt;Herdando de um template base&lt;/a&gt;&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;# -*- coding: utf-8 -*-
&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;%inherit file=&amp;quot;master.mako&amp;quot;/&amp;gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Hello World&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="acesso-a-banco-de-dados"&gt;
&lt;h1&gt;&lt;a class="toc-backref" href="#id13"&gt;Acesso a banco de dados&lt;/a&gt;&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;Links de documentação&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.sqlalchemy.org/docs/"&gt;SQLAlchemy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.postgresql.org"&gt;PostgreSQL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://dev.mysql.com/doc/"&gt;MySQL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.sqlite.org/docs.html"&gt;SQLite&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;em&gt;pylons.database&lt;/em&gt; e &lt;em&gt;SAContext&lt;/em&gt; estão obsoletos!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Veja &lt;a class="reference external" href="http://wiki.pylonshq.com/display/pylonsdocs/Using+SQLAlchemy+with+Pylons"&gt;Usando SQLAlchemy com Pylons&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;development.ini:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
sqlalchemy.url = sqlite:///%(here)s/phonebook.db
sqlalchemy.url = mysql://username:password&amp;#64;host:3306/database
sqlalchemy.url = postgres://username:password&amp;#64;host:5432/database
sqlalchemy.url = oracle://username:password&amp;#64;host:1521/sidname
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Opções&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;O MySQL fecha conexões inativas automaticamente. Você precisará:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
sqlalchemy.pool_recycle = 3600
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Imprimir todas as consultas para o console (você pode usar também o módulo 'logging'
como descrito na &lt;a class="reference external" href="http://www.sqlalchemy.org/docs/04/dbengine.html#dbengine_logging"&gt;Documentação do SQLAlchemy&lt;/a&gt;):&lt;/p&gt;
&lt;pre class="literal-block"&gt;
sqlalchemy.echo = True
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;a class="reference external" href="http://www.sqlalchemy.org/docs/04/dbengine.html#dbengine_options"&gt;Lista completa de opções suportadas&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Definindo tabelas e &lt;a class="reference external" href="http://www.sqlalchemy.org/docs/04/ormtutorial.html"&gt;mapeamento objeto relacional&lt;/a&gt; in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;model/__init__.py&lt;/span&gt;&lt;/tt&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Mapeamento simples:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sqlalchemy&lt;/span&gt; &lt;span class="kn"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;sql&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sqlalchemy.orm&lt;/span&gt; &lt;span class="kn"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;orm&lt;/span&gt;

&lt;span class="c"&gt;# Define table&lt;/span&gt;
&lt;span class="n"&gt;my_table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;tablename&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;primary_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;    &lt;span class="c"&gt;# gets a sequence/serial assigned&lt;/span&gt;
    &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;othertable_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;othertable.id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unicode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;80&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Define object class for ORM-mapping (every object matches one row)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__repr__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;MyModel (&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;)&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;

&lt;span class="n"&gt;orm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MyModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;my_table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Teste o acesso aos modelos mapeados confortavelmente usando &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;paster&lt;/span&gt; &lt;span class="pre"&gt;shell&lt;/span&gt;&lt;/tt&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Criando consultas e obtendo resultados&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;Obter uma linha com certo conteúdo em um campo (levanta uma exceção se mais linhas são retornadas):
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;model.Session.query(model.MyModel).filter_by(name='John').one()&lt;/span&gt;&lt;/tt&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Obter um iterador de itens combinando com certo critério:
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;model.Session.query(model.MyModel).filter(model.MyModel.name=='John')&lt;/span&gt;&lt;/tt&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Obter um iterador de itens com certo conteúdo:
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;model.Session.query(model.MyModel).filter_by(name='John')&lt;/span&gt;&lt;/tt&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Obter um iterador de itens selecionados por uma condição SQL personalizada:
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;model.Session.query(model.MyModel).filter_by(&amp;quot;id&amp;lt;:value&lt;/span&gt; &lt;span class="pre"&gt;and&lt;/span&gt; &lt;span class="pre"&gt;name=:name&amp;quot;).params(value=224,&lt;/span&gt; &lt;span class="pre"&gt;name='fred')&lt;/span&gt;&lt;/tt&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Obter a linha com a chave primária 42:
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;model.Session.query(model.MyModel).get(42)&lt;/span&gt;&lt;/tt&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Obter todas as linhas:
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;model.Session.query(model.MyModel).all()&lt;/span&gt;&lt;/tt&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Obter a primeira linha:
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;model.Session.query(model.MyModel).first()&lt;/span&gt;&lt;/tt&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Obter a única linha(levanta uma exceção se mais linhas são retornadas):
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;model.Session.query(model.MyModel).one()&lt;/span&gt;&lt;/tt&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Obter um número limitado de linhas (como uma lista)&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;model.Session.query(model.MyModel).offset(50).limit(10).all()&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;model.Session.query(model.MyModel)[10:50].all()&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Ordenar linhas&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;model.Session.query(model.MyModel).order_by(model.MyModel.age).all()&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;model.Session.query(model.MyModel).order_by(model.sql.desc(model.MyModel.age)).all()&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;model.Session.query(model.MyModel).order_by([model.MyModel.age,&lt;/span&gt; &lt;span class="pre"&gt;model.MyModel.city]).all()&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Contar linhas em um resultado:
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;model.Session.query(model.MyModel).count()&lt;/span&gt;&lt;/tt&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Joins (cuidado com produtos cartesianos):
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;model.Session.query(model.Model1,&lt;/span&gt; &lt;span class="pre"&gt;model.Model2).all()&lt;/span&gt;&lt;/tt&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Use &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;filter&lt;/span&gt;&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;filter_by&lt;/span&gt;&lt;/tt&gt; for generative queries:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
query = model.Session.query(model.MyModel)
query = query.filter_by(name='John', type=189)
query = query.filter_by(city='Hamburg')
results = query.all()
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Operadores lógicos&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;AND: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;and_(condition1,&lt;/span&gt; &lt;span class="pre"&gt;condition2,&lt;/span&gt; &lt;span class="pre"&gt;...)&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;AND: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;(condition1&lt;/span&gt; &lt;span class="pre"&gt;&amp;amp;&lt;/span&gt; &lt;span class="pre"&gt;condition2&lt;/span&gt; &lt;span class="pre"&gt;&amp;amp;&lt;/span&gt; &lt;span class="pre"&gt;...)&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;OR: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;or_(condition1,&lt;/span&gt; &lt;span class="pre"&gt;condition2,&lt;/span&gt; &lt;span class="pre"&gt;...)&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;OR: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;(condition1&lt;/span&gt; &lt;span class="pre"&gt;|&lt;/span&gt; &lt;span class="pre"&gt;condition2&lt;/span&gt; &lt;span class="pre"&gt;|&lt;/span&gt; &lt;span class="pre"&gt;...)&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;NOT: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;not_(condition)&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;NOT: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;~(condition)&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Operadores de consulta:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;==&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;&amp;lt;&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;&amp;gt;&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;&amp;lt;=&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;&amp;gt;=&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;startswith('foo')&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;endswith('.com')&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;like('%jean')&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;between(100,500)&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;in('A',&lt;/span&gt; &lt;span class="pre"&gt;'CNAME',&lt;/span&gt; &lt;span class="pre"&gt;'MX')&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;+&lt;/span&gt;&lt;/tt&gt; (concatenation of strings)&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;op('...')&lt;/span&gt;&lt;/tt&gt; (custom operator)&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;==None&lt;/span&gt;&lt;/tt&gt; (NULL comparison)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Funções de consulta:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Sintaxe geral: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;func.FUNCTIONNAME(...)&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;e.g. &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;func.count()&lt;/span&gt;&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;func.now()&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Criando novos objetos:&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;new_object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MyModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;new_object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Jane&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;new_object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Tokio&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;# not needed if set autoflush=True&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;# needed because SQLAlchemy 0.4 uses transactions everywhere&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Alterando objetos:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;old_object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MyModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;old_object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Paris&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;# not needed if set autoflush=True&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;# needed because SQLAlchemy 0.4 uses transactions everywhere&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Removendo objetos (não use &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;del(...)&lt;/span&gt;&lt;/tt&gt;):&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;old_object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sac&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MyModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old_object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;# not needed if set autoflush=True&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;# needed because SQLAlchemy 0.4 uses transactions everywhere&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Seleções arbitrárias (retorna o que você especifica ao invés de linhas completas):&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;select&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MyModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;some_column&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;# or .fetchmany() or .fetchone()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.sqlalchemy.org/docs/types.html"&gt;Tipos&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;String(length=None) [&lt;em&gt;é melhor usar Unicode&lt;/em&gt;]&lt;/li&gt;
&lt;li&gt;Integer&lt;/li&gt;
&lt;li&gt;SmallInteger&lt;/li&gt;
&lt;li&gt;Numeric(precision=10, length=2)&lt;/li&gt;
&lt;li&gt;Float(precision=10)&lt;/li&gt;
&lt;li&gt;DateTime [corresponds to datetime.datetime]&lt;/li&gt;
&lt;li&gt;Date [corresponds to datetime.date]&lt;/li&gt;
&lt;li&gt;Time [corresponds to datetime.time]&lt;/li&gt;
&lt;li&gt;Binary(length=None)&lt;/li&gt;
&lt;li&gt;Boolean&lt;/li&gt;
&lt;li&gt;Unicode(length=None)&lt;/li&gt;
&lt;li&gt;PickleType&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- ::

Removed temporarily because 0.3 is stable but buggy while
0.4 is rumored to be good but not documented.
===========================
Autenticação (Authkit)
===========================

- Change your ``config/middleware.py`` where it says ``if asbool(full_stack)`` to::

    if asbool(full_stack):
        import authkit.authenticate
        app = authkit.authenticate.middleware(app, app_conf=app_conf, global_conf=global_conf)

- development.ini

  - authkit.enable = true
  - authkit.setup.method = forward
  - authkit.forward.internalpath = /start/login
  - authkit.cookie.secret   = qwertzuiop
  - authkit.cookie.name = dnsdhcp_auth
  - authkit.cookie.params = max-age:86400
  - authkit.cookie.signout  = /start/logout
  - authkit.cookie.includeip = True

- O ``authkit.cookie.name`` deve ser diferente de ``beaker.session.key``

- Habilitar logging (em ``config/middleware.py`` logo após o authkit.authenticate.middleware)::

    import logging
    log_StreamHandler = logging.StreamHandler() # points to stderr
    authkit.authenticate.log.addHandler(log_StreamHandler)
    authkit.authenticate.log.setLevel(logging.DEBUG) --&gt;
&lt;/div&gt;
&lt;div class="section" id="sess-es-baseadas-em-cookies"&gt;
&lt;h1&gt;&lt;a class="toc-backref" href="#id14"&gt;Sessões baseadas em cookies&lt;/a&gt;&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;a class="reference external" href="http://beaker.groovie.org/"&gt;Página do Beaker&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;development.ini&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;beaker.session.key&lt;/span&gt; &lt;span class="pre"&gt;=&lt;/span&gt; &lt;span class="pre"&gt;myproject_session&lt;/span&gt;&lt;/tt&gt;
(nome do cookie de sessão sendo enviado ao navegador)&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;beaker.session.secret&lt;/span&gt; &lt;span class="pre"&gt;=&lt;/span&gt; &lt;span class="pre"&gt;somesecret&lt;/span&gt;&lt;/tt&gt; (random string that the cookie
string sent to the user is signed with)&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;beaker.session.cookie_expires&lt;/span&gt; &lt;span class="pre"&gt;=&lt;/span&gt; &lt;span class="pre"&gt;True&lt;/span&gt;&lt;/tt&gt;
(whether the cookie is a session cookie that expires when the browser
is closed)&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;beaker.session.timeout&lt;/span&gt; &lt;span class="pre"&gt;=&lt;/span&gt; &lt;span class="pre"&gt;...&lt;/span&gt;&lt;/tt&gt; (time in seconds until the session
times out)&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;beaker.session.data_dir&lt;/span&gt; &lt;span class="pre"&gt;=&lt;/span&gt; &lt;span class="pre"&gt;...&lt;/span&gt;&lt;/tt&gt; (path where the 'sessions' directory
is located storing the session data)&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;beaker.session.type&lt;/span&gt; &lt;span class="pre"&gt;=&lt;/span&gt; &lt;span class="pre"&gt;...&lt;/span&gt;&lt;/tt&gt; (Storage type for session information.)&lt;ul&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;dbm&lt;/span&gt;&lt;/tt&gt; stores sessions in files on disk&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;file&lt;/span&gt;&lt;/tt&gt; stores sessions in files on disk&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;memory&lt;/span&gt;&lt;/tt&gt; stores sessions in RAM&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ext:memcached&lt;/span&gt;&lt;/tt&gt; stores sessions on
&lt;a class="reference external" href="http://www.danga.com/memcached/"&gt;memcached&lt;/a&gt; servers.
Memcache servers are configured as &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;beaker.session.url&lt;/span&gt; &lt;span class="pre"&gt;=&lt;/span&gt;
&lt;span class="pre"&gt;server1,&lt;/span&gt; &lt;span class="pre"&gt;server2,&lt;/span&gt; &lt;span class="pre"&gt;..&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;A variavel &lt;em&gt;session&lt;/em&gt; vem de &lt;em&gt;pylons.session&lt;/em&gt; e é importada
em seu &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;lib/base.py&lt;/span&gt;&lt;/tt&gt;. Ela se comporta como um dicionário.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Carregando um valor da sessão:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
value = session['whatever']
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Salvando um valor na sessão:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
session['whatever'] = value
session.save()
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Removendo uma chave da sessão:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
del session['whatever']
session.save()
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Limpando a sessão:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="formencode"&gt;
&lt;h1&gt;&lt;a class="toc-backref" href="#id15"&gt;Formencode&lt;/a&gt;&lt;/h1&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Documentação&lt;ul&gt;
&lt;li&gt;&lt;a class="reference external" href="http://formencode.org"&gt;Sítio do Formencode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Formulários são validados através de subclasses de
&lt;a class="reference external" href="http://formencode.org/module-formencode.schema.html"&gt;formencode.schema&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Validadores customizados são normalmente subclasses de
&lt;a class="reference external" href="http://formencode.org/class-formencode.validators.FancyValidator.html"&gt;formencode.FancyValidator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://formencode.org/Validator.html#writing-your-own-validator"&gt;Escrevendo validadores customizados&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Usando formencode:&lt;ul&gt;
&lt;li&gt;Defina um schema de validação&lt;/li&gt;
&lt;li&gt;Decore o método/ação do seu controlador (&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;&amp;#64;formencode.validate(MySchema)&lt;/span&gt;&lt;/tt&gt;)&lt;/li&gt;
&lt;li&gt;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
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;form&lt;/span&gt;&lt;/tt&gt;) como em &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;&amp;#64;formencode.validate(MySchema,&lt;/span&gt; &lt;span class="pre"&gt;form='anotheraction')&lt;/span&gt;&lt;/tt&gt;
e mostrar as mensagens de erro bem acima dos inputs com uma classe CSS
chamada '.error-message'&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Exemplo de classe de validação:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c"&gt;# My form schema&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MySchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;formencode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;allow_extra_fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="n"&gt;filter_extra_fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="n"&gt;days&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;formencode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validators&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IntRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;min&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;365&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;not_empty&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;formencode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validators&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MaxLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# MySchema schema without the &amp;#39;days&amp;#39; field&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MySchema2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MySchema&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;days&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;   &lt;span class="c"&gt;# (removes days field from the superclass)&lt;/span&gt;

&lt;span class="c"&gt;# MySchema schema with additional &amp;#39;comment&amp;#39; field&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MySchema3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MySchema&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;comment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;min&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Subclass variables:&lt;ul&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;allow_extra_fields&lt;/span&gt; &lt;span class="pre"&gt;=&lt;/span&gt; &lt;span class="pre"&gt;False&lt;/span&gt;&lt;/tt&gt; (whether to display an error if fields are
found in the POST request that are not mentioned in this subclass)&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;filter_extra_fields&lt;/span&gt; &lt;span class="pre"&gt;=&lt;/span&gt; &lt;span class="pre"&gt;False&lt;/span&gt;&lt;/tt&gt; (whether to remove fields that are not
mentioned in this subclass)
are missing in the POST request but defined in this subclass)&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ignore_key_missing&lt;/span&gt; &lt;span class="pre"&gt;=&lt;/span&gt; &lt;span class="pre"&gt;False&lt;/span&gt;&lt;/tt&gt; (whether to display an error if fields
are missing in the POST request but defined in this subclass)&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;if_key_missing&lt;/span&gt;&lt;/tt&gt; (fields that are missing in the POST request will get
this value assigned)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="bibliotecas-javascript"&gt;
&lt;h1&gt;&lt;a class="toc-backref" href="#id16"&gt;Bibliotecas Javascript&lt;/a&gt;&lt;/h1&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;jQuery (pequna bibloteca Javascript com uma porção de plugins)&lt;ul&gt;
&lt;li&gt;&lt;a class="reference external" href="http://jquery.com/"&gt;Home&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://jquery.com/plugins/"&gt;Plugins&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Prototype (Biblioteca Javascript)&lt;ul&gt;
&lt;li&gt;&lt;a class="reference external" href="http://wiki.script.aculo.us/scriptaculous/show/PrototypeOverview"&gt;Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://blogs.ebusiness-apps.com/jordan/pages/Prototype%20Library%20Info.htm"&gt;Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://particletree.com/features/quick-guide-to-prototype"&gt;Guia Rápido&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.sergiopereira.com/articles/prototype.js.html"&gt;Usando Prototype&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;script.aculo.us (Javascript effects library built on top of Prototype)
- &lt;a class="reference external" href="http://script.aculo.us/"&gt;Home&lt;/a&gt;
- &lt;a class="reference external" href="http://wiki.script.aculo.us/scriptaculous/show/Demos"&gt;Demos&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="webhelpers"&gt;
&lt;h1&gt;&lt;a class="toc-backref" href="#id17"&gt;Webhelpers&lt;/a&gt;&lt;/h1&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;As funções de &lt;em&gt;webhelpers.rails&lt;/em&gt; são importadas automaticamente&lt;/li&gt;
&lt;li&gt;Controllers and templates can access the webhelpers module by the 'h' (helper) name&lt;/li&gt;
&lt;li&gt;Links:&lt;ul&gt;
&lt;li&gt;&lt;a class="reference external" href="http://pylonshq.com/WebHelpers/module-index.html"&gt;Referência&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="internacionaliza-o-i18n"&gt;
&lt;h1&gt;&lt;a class="toc-backref" href="#id18"&gt;Internacionalização (i18n)&lt;/a&gt;&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;Instale o  &lt;em&gt;Babel&lt;/em&gt;:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Debian/Ubuntu: aptitude install python-pybabel  (anteriormente, acidentalmente chamado de &amp;quot;python-babel&amp;quot;mas foi renomeado depois)&lt;/li&gt;
&lt;li&gt;Outros sistemas operacionais: easy_install Babel&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;a class="reference external" href="http://wiki.pylonshq.com/display/pylonsdocs/Internationalization+and+Localization"&gt;Artigo do wiki&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Edite o seu &lt;cite&gt;setup.py&lt;/cite&gt; e habilite a seção &lt;cite&gt;message_extractors&lt;/cite&gt; para interpretar strings gettext nos templates.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Use a função &amp;quot;_&amp;quot; (um alias para a função gettext) sempre que você precisar de strings normais.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Controladores: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;_('Hello&lt;/span&gt; &lt;span class="pre"&gt;World')&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Templates Mako : &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;${&lt;/span&gt; &lt;span class="pre"&gt;_('Hello&lt;/span&gt; &lt;span class="pre"&gt;World')&lt;/span&gt; &lt;span class="pre"&gt;}&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Primeira vez:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Criar um &lt;em&gt;pot&lt;/em&gt; template no diretório &lt;em&gt;i18n&lt;/em&gt;: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;python&lt;/span&gt; &lt;span class="pre"&gt;setup.py&lt;/span&gt; &lt;span class="pre"&gt;extract_messages&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Criar um arquivo &lt;em&gt;po&lt;/em&gt; para cada idioma (aqui o idioma é 'es'): &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;python&lt;/span&gt; &lt;span class="pre"&gt;setup.py&lt;/span&gt; &lt;span class="pre"&gt;init_catalog&lt;/span&gt; &lt;span class="pre"&gt;-l&lt;/span&gt; &lt;span class="pre"&gt;es&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Editar o arquivo &lt;em&gt;po&lt;/em&gt; em &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;i18n/es/LC_MESSAGES/*.po&lt;/span&gt;&lt;/tt&gt; e adicionar strings traduzidas nos campos &lt;em&gt;msgstr&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Compile the &lt;em&gt;po&lt;/em&gt; files into &lt;em&gt;mo&lt;/em&gt; files: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;python&lt;/span&gt; &lt;span class="pre"&gt;setup.py&lt;/span&gt; &lt;span class="pre"&gt;compile_catalog&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Update:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Overwrite the english &lt;em&gt;pot&lt;/em&gt; template:: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;python&lt;/span&gt; &lt;span class="pre"&gt;setup.py&lt;/span&gt; &lt;span class="pre"&gt;extract_messages&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Update the &lt;em&gt;po&lt;/em&gt; files with new strings (does not overwrite the already translated strings): &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;python&lt;/span&gt; &lt;span class="pre"&gt;setup.py&lt;/span&gt; &lt;span class="pre"&gt;update_catalog&lt;/span&gt; &lt;span class="pre"&gt;-l&lt;/span&gt; &lt;span class="pre"&gt;es&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Compile the &lt;em&gt;po&lt;/em&gt; files into &lt;em&gt;mo&lt;/em&gt; files: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;python&lt;/span&gt; &lt;span class="pre"&gt;setup.py&lt;/span&gt; &lt;span class="pre"&gt;compile_catalog&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Dica: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;paster&lt;/span&gt; &lt;span class="pre"&gt;serve&lt;/span&gt; &lt;span class="pre"&gt;--reload&lt;/span&gt;&lt;/tt&gt; não detecta mudanças na internacionalização. Você irá precisar reiniciar o servidor web.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Para configurar a linguagem do gettext de acordo com a língua do navegador adicione isso ao seu &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;lib/base.py&lt;/span&gt;&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
def __before__(self):
    user_agent_language = request.languages[0][0:2]
    set_lang(user_agent_language)
&lt;/pre&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;request.languages&lt;/span&gt;&lt;/tt&gt; is an array of preferred languages that the users sets in the browser.
I.e. &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;['de',&lt;/span&gt; &lt;span class="pre"&gt;'en',&lt;/span&gt; &lt;span class="pre"&gt;'en-us']&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="testes-unit-rios-nose"&gt;
&lt;h1&gt;&lt;a class="toc-backref" href="#id19"&gt;Testes Unitários (Nose)&lt;/a&gt;&lt;/h1&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://somethingaboutorange.com/mrl/projects/nose/"&gt;Página do Nose&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="comunidade"&gt;
&lt;h1&gt;&lt;a class="toc-backref" href="#id20"&gt;Comunidade&lt;/a&gt;&lt;/h1&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;#pylons &amp;#64; irc.freenode.net&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="add-ons-relacionados"&gt;
&lt;h1&gt;&lt;a class="toc-backref" href="#id21"&gt;Add-ons Relacionados&lt;/a&gt;&lt;/h1&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://code.google.com/p/formalchemy/"&gt;FormAlchemy&lt;/a&gt;:
Cria formulários HTML automaticamente a partir de schemas do SQLAlchemy (classes mapeadas)&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://paginate.workaround.org/"&gt;Paginator&lt;/a&gt;:
Ajuda a dividir um número grande de resultados em páginas e permite que o usuário navegue
entre as páginas&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://code.google.com/p/dbsprockets/"&gt;DBSprockets&lt;/a&gt;:
Automaticamente cria formulários do  Toscawidget e validadores do Formencode para schemas
(classes mapeadas)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;...&lt;/p&gt;
&lt;!-- ::


= Controllers / Actions =

Whether a controller or a static file is served when calling a certain action is defined
by the search path ''Cascade'' in the {{{config/middleware.py}}} file.
''public/index.html'' is always served first unless you delete it.

Actions (methods of the controller) are not publicly visible if their names start
with an underscore (_).

Parameter as defined in config/routing.py that are neither ''action'' nor ''controller''
are passed to the controllers' actions as named parameter. If you are using the default {{{:controller/:action/:id}}} schema and have a URL http://myserver/news/view/10 that will call the controller named NewsController (Pylons looks for a controller called the name in with an uppercase first character plus the word "Controller") with the method/action ''view''. The additional parameter 'id' is passed as an argument to the function:

{{{
class NewsController(BaseController):
    def view(self, id):
        return Response('You wanted to read news article number %s' % id)
}}}

= Templates =

Templates are text files that reside in the templates/ directory in your project.
They consist mainly of text or HTML but any line can also contain special control statements.

 * Python commands can be used on lines beginning with a '%' sign
 (you can can use 'if'/'elif'/'else' or 'for' statements as you would do in Python)
 * You can include other templates: {{{&lt;%include file="header.html"/&gt;}}}
 * You can access attributes from your 'c' object used in the controller: {{{ ${ c.foo } }}}

See also [http://pylonshq.com/docs/template_plugins.html Template Language Plugins]

To use a template from a controller {{{return render_response('/templatefile')}}}.
Fill the attributes of the ''c'' object in your controller like this:

{{{#!python
class PingController(BaseController):
    def action(self):
        c.foo = 'Hello'
        c.bar = 'World'
        return render_response('/template.myt')
}}}

== Myghty templates ==

Mygthy is still the default templating system in Pylons 0.9.5. But as it likely that
it will be replaced by Mako I won't describe it here.

== Mako templates ==

=== Switch from Myghty to Mako ===

In the config/middleware.py you need to change the config.init_app call to:

{{{
config.init_app(global_conf, app_conf, package='yourproject', template_engine='mako')
}}}

In the config/environment.py you need to add these lines to use UTF-8 encoded templates correctly:

{{{
tmpl_options = {}
tmpl_options['mako.input_encoding'] = 'UTF-8'
tmpl_options['mako.output_encoding'] = 'UTF-8'
}}}

=== Differences Myghty -&gt; Mako ===

Variables in Mygthy were printed as {{{&lt;% c.foo %&gt;}}}.
In Mako you use {{{ ${ c.foo } }}} instead.

Python blocks in Mako are written in {{{ &lt;% my python code %&gt; }}}
but no result will be printed. If you want to display a return value
use {{{ ${ c.foo } }}}.

There is no automatic inheritance of the autohandler template. If you
want to use a global template you need to
{{{ &lt;%inherit file="/base.mako"/&gt; }}} it.

== Unicode ==

If your templates are utf-8 encoded you need to start all of your template files
with this line:

{{{
# -*- coding: utf-8 -*-
}}}

You should also config/environment.py's last line to:

{{{
    return pylons.config.Config(tmpl_options, map, paths,
        request_settings = dict(charset='utf-8', errors='replace') )
}}}

Otherwise you may get strange unicode errors. They showed up here when I used Formencode.

(See also: http://pylonshq.com/docs/0.9.5/internationalization.html#request-parameters)

= Sessions =

A session is used to store information of a current user sitting in front
of a web browser. HTTP is stateless so you usually don't know anything
about what the user did before the current HTTP request. With sessions you
are free to store and retrieve session data (variables in a dictionary) on the Pylons
web server. The user is tracked through a ''cookie'' that is associated to the
data dictionary by Pylons. To make that session information persistent the
variables set you set are saved in files under the data/sessions directory
(as specified in your development.ini -&gt; session_data_dir).

Example that increases the session variable ''testvalue'' every time a user
visits this URL:

{{{
from pylons import session

class ExampleController(BaseController):
    def index(self):
        if 'testvalue' in session:
            session['testvalue'] += 1
        else:
            session['testvalue'] = 0

        session.save()

        return Response('Current value is %s' % session['testvalue'])
}}}

= Webhelpers =

Webhelpers are a set of utility functions for Pylons documented at http://pylonshq.com/WebHelpers/module-index.html
You can use them to simplify AJAX calls, create HTML elements, control the script.aculo.us Javascript library and do
other fancy things. Your controllers can access the helpers through the global 'h' object.

&lt;!&gt; Note: The 'webhelpers' package consists of further sub-packages like 'htmlgen', 'util' or 'pagination'. These are not
imported automatically. Extend your lib/helpers.py by a line like "from webhelpers.rails import *" or rather "import webhelpers.rails" (keep your namespace clenaer) if you
want to use them. The 'rails' package is imported automatically.

Form example in a template that will submit the entered data to the same controller with a different action:

{{{
&lt;% h.form( url=h.url(action="dothat"), method='post' ) %&gt;
Please enter your name: &lt;% h.text_field(name='username', size=30) %&gt;
Your password: &lt;% h.password_field(name='password') %&gt;
Any comments? &lt;% h.text_area(name="usercomments", size="25x10") %&gt;
Where do you come from?
&lt;% h.select(name='origin', option_tags=h.options_for_select( [ ['Germany','GER'], ['Australia','AUS'] ] ) ) %&gt;
&lt;% h.submit(value='I am done') %&gt;
}}}

Further documentation:

 * Shell: pydoc webhelpers
 * Web: http://pylonshq.com/WebHelpers/module-index.html
 * Explore them using ipython as follows:

{{{
&gt; import webhelpers
&gt; webhelpers.[TAB]
&gt; webhelpers.url_for?
&gt; webhelpers.url_for??
}}}

 * Explore them in the paster shell... (ipython should be installed, too)

{{{
&gt; h.url_for?
}}}

= Logging/Debugging =

Use {{{h.log()}}} to write messages to the console you run ''paster serve'' on.
Redirect the output using: {{{paster serve - -log-file=yourlogfile development.ini}}}

= Global objects =

== ...in controllers ==

=== h ===

Webhelpers that come from ''lib/helpers.py''. Think 'h' as in ''Helpers''.

== ...in templates ==

=== c ===

Template variables. Think 'c' as in ''content''. Set them in your controller functions ({{{c.foobar = 42}}} and display them in your
templates using {{{&lt;% c.foobar %&gt;}}}.

=== m ===

The runtime context inside of ''Mygthy'' templates (see [http://www.myghty.org/docs/request.myt#request the request object])
Think 'm' as in 'Myghty'. See http://www.myghty.org/docs/request.myt#request

=== request.params ===

A dictionary that contains HTTP parameters.


= AJAX =

AJAX (''asynchronous Javascript and XML'') is a way to let your browser fetch new information from
a web server without reloading the whole page. Either that action is triggered by the click of
a submit button or just a timed action. What happens then is that the Javascript requests a certain
URL from the web server and replaces a defined part of the web page with the new content.

== Prototype / script.aculo.us ==

Pylons has built-in support for Javascript libraries:

 * [http://wiki.script.aculo.us/scriptaculous/show/Prototype Prototype][[BR]]
 Provides a lot of useful Javascript functions to be used in your web page.
 Pylons uses these functions to make using AJAX simpler.
 * http://script.aculo.us/[[BR]]
 Provides some visual effects that you can use to impress your coworkers.
 Not exactly needed to use AJAX but helps you use special form elements
 like a text input field that suggests auto-completion.
 scriptaculous is built upon Prototype and will not work without it.

Pylons even has these libraries bundled so you don't need to download them manually.
Just put

{{{
&lt;% h.javascript_include_tag(builtins=True)  %&gt;
}}}

into the {{{&lt;head&gt;}}} section of your templates/autohandler to load both libraries
on every HTML page.

== AJAX forms ==

One useful example of AJAX are HTML forms. You can alter the form
depending on what the user entered. The Webhelper's ''form_remote_tag''
function creates a {{{&lt;form&gt;}}} tag with some additional Javascript
that works together with Prototype/script.aculo.us.

{{{
&lt;% h.form_remote_tag(
    url=h.url(
        controller="list",
        action="reload_form"),
    id='my_form',
    method='post',
    loading=h.update_element_function(
        "loading",
        content='Loading... please wait...'),
    update='my_form'
    ) %&gt;

&lt;div id="loading"&gt;
&lt;/div&gt;

&lt;/form&gt;
}}}

This will create a {{{&lt;form&gt;}}} tag. The parameters mean:

 * url: the URL this form is submitted to (it calls the
 controller 'list' with the action 'reload_form')
 * id: the unique HTML identifier (id="...") of this HTML tag
 (each element of your HTML page can have one)
 * method: the method to send this form ('GET' or 'POST')
 * loading: a special action to run when the Javascript sent the
 AJAX request and awaits the answer from the web server. It tells
 the Javascript library to put ''"Loading... please wait..."''
 into the HTML element with the ID '''loading''' which is the
 {{{&lt;div&gt;}}} below the form. So while the user is waitint for the
 form to update itself through AJAX this message is shown.
 * update: tells the Javascript library to replace the HTML element
 with the ID ''my_form'' (our form itself) by whatever the web
 server returned

So you mainly need a controller 'list' that sends a HTML form
back. The Javascript receives what your controller prints and
will replace your HTML with that. So obviously your controller
needs to return just the form - not a full page with title
and header. That's why you need to tell the ''render_response''
function (that you use to print HTML based on templates)
to ''fragment'':

{{{#!python
return render_response('just_the_form.myt', fragment=True)
}}}

= SQLAlchemy =

Through the power of assign_mapper you get additional methods on your object classes
(see also http://www.sqlalchemy.org/docs/plugins.html#plugins_assignmapper).
To explore these methods and play with queries the "paster shell" is a good tool.
Install ipython, too, and you can TAB-complete and much more.

== Querying ==

(See also: http://www.sqlalchemy.org/docs/datamapping.html#datamapping_query_callingstyles)

 * model.Mymodel.get(15) [returns the row from the database table with the primary index 15]
 * model.Mymodel.get_by(me='good') [returns exactly one row]
 * model.Mymodel.select(model.Mymodel.c.religion=='atheism') [returns all matching rows as a list]
 * model.Mymodel.select_by(name='John') [returns all matching rows as a list]
 * model.Mymodel.count() [counts all rows of this model]
 * model.Mymodel.count_by(foo=51) [counts all rows matching this query]

The 'c' in .select() refers to the columns of that database table.

Specify the order of the results like this:

 * model.Mymodel.select(order_by=[model.Mymodel.c.name])

=== Incrementally adding criteria for a select request ===


{{{
query = model.DnsRecord.query()
query = query.filter(model.Mymodel.c.name.like('john%'))
query = query.filter(model.Mymodel.c.address.like('main st%'))
results = query.list()
}}}

''The .filter() method is not yet available as a method of the assign_mapper in version 0.3 of SQLAlchemy.
It will get added in version 0.4. Thus the {{{query()}}} in between. Additionally {{{filter_by()}}}
is not available in this context, yet.''


== Inserting new database rows ==

{{{
newmodel = model.Mymodel()
newmodel.name='John'
newmodel.gender='male'
newmodel.flush()
}}}

@@Only works with assign_mapper(). With mapper() either you had to use the SessionContextExt (http://www.sqlalchemy.org/docs/plugins.html#plugins_sessioncontext_sessioncontextext) or call newmodel.save(); newmodel.flush()

== Changing existing database rows ==

{{{
some_person = model.Mymodel.get_by(name='marc')
some_person.phone_number = '+81 576 47195'
some_person.flush()
}}}

== Many-to-many relationships with secondary mappings ==

If you use normalized tables (second or third normal form) you often need extra tables
that map IDs of one table to IDs of another table. Imagine you want to create a
bookmarking service and thus have tables for:

 * Users (ID, Name)
 * Bookmarks (ID, URL)
 * Mapping of User-ID to Bookmark-ID

The connection of users to bookmarks is done through the mapping table.
In SQL you need to join two tables to get all the bookmarks for a certain
user. SQLAlchemy simplifies that if you add a "secondary" property
(as described at
http://www.sqlalchemy.org/docs/datamapping.html#datamapping_manytomany).
This allows you to access the bookmarks linked to a certain user
directly as a property of the user object.
A table/object mapping that adds a secondary property looks like this:

{{{
mapper(User, users_table, properties = {
    'bookmarks':relation(Bookmark, secondary=bookmarks_table)
    } )
}}}

This adds a 'bookmarks' property to your
User objects. The property behaves like a list:

{{{
# Get John from the database
user_john = model.User.get_by(name='john')

# Print John's bookmarks
print user_john.bookmarks   # prints john's bookmarks
}}}

The last line looks up all matching rows in the User-ID-to-Bookmark-ID table
and fetches the bookmarks with the appropriate IDs.

{{{
# Create a new bookmark ("Bookmark" is a mapped class, too)
google = model.Bookmark.get_by(url='http://www.google.com')

# Add this bookmark to John
user_john.bookmarks.append(google)
}}}

The last statement inserts a new row into the User-ID-to-Bookmark-ID table
"connecting" this bookmark to John. Handy, isn't it?

== Unicode ==

=== MySQL ===

You should use two parameters when creating connections to MySQL databases
so that all fields are transmitted as unicode strings:

{{{
sqlalchemy.dburi = mysql://user:pass@localhost:3306/dbname?use_unicode=1&amp;charset=utf8
}}}

=== PostgreSQL ===

Define the fields in your table as ''Unicode'' instead of ''String'' and you should
get unicode strings all the time.

= Formencode =

todo...

Documentation for parameters of schemas: pydoc formencode.schema.Schema

formencode.validators.FancyValidator

prefill with defaults:
formencode.htmlfill.render(render('/hosts/new.mako'), defaults=request.params)


= AuthKit =

todo

Important: use different cookie names in your development.ini file for "session_key" and "authkit.cookie.name"

== Logging ==

In case AuthKit does weird things you can enable debugging by adding these lines to
your environment.py:

{{{
import logging
log_StreamHandler = logging.StreamHandler() # points to stderr
authkit.authenticate.log.addHandler(log_StreamHandler)
authkit.authenticate.log.setLevel(logging.DEBUG)
}}}

- - - - - - - - - - - -
paster shell -&gt; r = app.get('/') -&gt; r.req

http://code.google.com/p/modwsgi/

- - - - - - - - - - - - - - - - - - - -
websetup.py
paster setup-app --&gt;
&lt;/div&gt;
&lt;/div&gt;
</summary><category term="python"></category></entry><entry><title>Introdução ao otimizador de consultas do PostgreSQL</title><link href="http://artigos.waltercruz.com/postgresql/otimizador" rel="alternate"></link><updated>2008-11-16T14:21:47Z</updated><author><name>Walter Cruz</name><uri>http://waltercruz.com</uri></author><id>tag:artigos.waltercruz.com,2008-11-16:/postgresql/otimizador</id><summary type="html">&lt;div class="document" id="introdu-o-ao-otimizador-de-consultas-do-postgresql"&gt;
&lt;h1 class="title"&gt;Introdução ao otimizador de consultas do PostgreSQL&lt;/h1&gt;
&lt;p&gt;Walter Rodrigo de Sá Cruz
Criado em Wed Aug 30 11:48:04 2006&lt;/p&gt;
&lt;div class="section" id="o-caminho-de-uma-consulta"&gt;
&lt;h1&gt;O caminho de uma consulta&lt;/h1&gt;
&lt;p&gt;Antes de entrarmos no assunto da otimização propriamente dito, precisamos rever qual é o caminho de uma consulta no banco de dados.&lt;/p&gt;
&lt;p&gt;(A seção a seguir é adaptada da documentação do PostgreSQL)&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;O programa aplicativo transmite um comando para o servidor, e aguarda para receber de volta os resultados transmitidos pelo servidor.&lt;/li&gt;
&lt;li&gt;O estágio de análise verifica o comando transmitido pelo programa aplicativo com relação à correção da sintaxe, e cria a árvore de comando.&lt;/li&gt;
&lt;li&gt;O sistema de reescrita recebe a árvore de comando criada pelo estágio de análise, e procura por alguma regra(armazenada nos catálogos do sistema) a ser aplicada na árvore de comando. Realiza as transformações especificadas no corpo das regras. Uma das aplicações do sistema de reescrita é a criação de visões. Sempre que é executado um comando em uma visão (ou seja, uma tabela virtual), o sistema de reescrita reescreve o comando do usuário como um comando acessando as tabelas base especificadas na definição da visão, em vez da visão.&lt;/li&gt;
&lt;li&gt;O planejador/otimizador recebe a árvore de comando (reescrita), e cria o plano de comando que será a entrada do executor. Isto é feito criando primeiro todos os caminhos possíveis que levam ao mesmo resultado. Por exemplo, se existe um índice em uma relação a ser varrido, existem dois caminhos para a varredura. Uma possibilidade é uma varredura seqüencial simples, e a outra possibilidade é utilizar o índice. Em seguida é estimado o custo de execução de cada umdos caminhos, e escolhido o mais barato. O caminho mais barato é expandido em um plano completo para que o executor possa utilizá-lo.&lt;/li&gt;
&lt;li&gt;O executor caminha recursivamente através da árvore do plano, e traz as linhas no caminho representado pelo plano. O executor faz uso do sistema de armazenamento ao varrer as relações, realiza classificações e junções, avalia as qualificações e, por fim, envia de volta as linhas derivadas.&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="section" id="o-papel-do-otimizador"&gt;
&lt;h1&gt;O papel do otimizador&lt;/h1&gt;
&lt;p&gt;(Adaptado de &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Query_optimizer"&gt;http://en.wikipedia.org/wiki/Query_optimizer&lt;/a&gt; )&lt;/p&gt;
&lt;p&gt;O otimizador de consultas é o componente do banco de dados que tenta determinar o o modo mais eficiente
de executar uma consulta.&lt;/p&gt;
&lt;p&gt;O PostgreSQL usa algumas estatísticas mantidas em tabelas do sistema para direcionar o otimizador. Se essas estatísticas estiverem
desatualizadas, você provavelmente não obterá o melhor plano para a consulta.&lt;/p&gt;
&lt;p&gt;Para atualizar as estatísticas, devemos executar o comando ANALYZE.&lt;/p&gt;
&lt;p&gt;É possível vermos o plano que o otimizador de consultas do PostgreSQL gerou, usando a cláusula EXPLAIN antes da consulta.&lt;/p&gt;
&lt;p&gt;Adicionando a cláusula EXPLAIN ANALYZE antes da consulta, ela é de fato executada, de forma que possamos ter a medida do tempo.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="exemplos"&gt;
&lt;h1&gt;Exemplos&lt;/h1&gt;
&lt;/div&gt;
&lt;div class="section" id="pagila"&gt;
&lt;h1&gt;Pagila&lt;/h1&gt;
&lt;p&gt;Vamos usar o banco de dados exemplo pagila, disponível em: &lt;a class="reference external" href="http://pgfoundry.org/projects/dbsamples/"&gt;http://pgfoundry.org/projects/dbsamples/&lt;/a&gt;, que é um exemplo de banco de dados de uma locadora.&lt;/p&gt;
&lt;p&gt;Vamos começar com a configuração padrão do otimizador do Postgres. Todas as variáveis no postgresql.conf estão comentadas, o que significa que elas usarão os valores padrão.&lt;/p&gt;
&lt;p&gt;Como teste, criei dois bancos iguais, o pagila e o pagila2, com a diferença que não rodei o ANALYZE no pagila2.&lt;/p&gt;
&lt;p&gt;A configuração do banco é a padrão do Debian.&lt;/p&gt;
&lt;p&gt;A consulta a seguir retorna os filmes e os atores associados a ela:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first_name&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;film&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt;  &lt;span class="n"&gt;film_actor&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;film_actor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;film_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;film_id&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;actor&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;film_actor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;actor_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;actor_id&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Aqui está o plano gerado para essa consulta no pagila2 (o que não recebeu ainda o ANALYZE):&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Merge Join  (cost=708.95..795.88 rows=5462 width=185) (actual time=56.274..85.883 rows=5462 loops=1)
  Merge Cond: (&amp;quot;outer&amp;quot;.film_id = &amp;quot;inner&amp;quot;.film_id)
  -&amp;gt;  Sort  (cost=94.83..97.33 rows=1000 width=149) (actual time=8.489..9.926 rows=1000 loops=1)
        Sort Key: f.film_id
        -&amp;gt;  Seq Scan on film f  (cost=0.00..45.00 rows=1000 width=149) (actual time=0.010..4.517 rows=1000 loops=1)
  -&amp;gt;  Sort  (cost=614.12..627.77 rows=5462 width=42) (actual time=47.775..56.152 rows=5462 loops=1)
        Sort Key: film_actor.film_id
        -&amp;gt;  Merge Join  (cost=0.00..275.06 rows=5462 width=42) (actual time=0.027..33.019 rows=5462 loops=1)
              Merge Cond: (&amp;quot;outer&amp;quot;.actor_id = &amp;quot;inner&amp;quot;.actor_id)
              -&amp;gt;  Index Scan using actor_pkey on actor a  (cost=0.00..12.20 rows=200 width=44) (actual time=0.010..0.450 rows=200 loops=1)
              -&amp;gt;  Index Scan using film_actor_pkey on film_actor  (cost=0.00..194.08 rows=5462 width=4) (actual time=0.006..13.315 rows=5462 loops=1)
Total runtime: 94.576 ms
&lt;/pre&gt;
&lt;img alt="http://waltercruz.com/images/artigos/query1semanalyze.jpg" src="http://waltercruz.com/images/artigos/query1semanalyze.jpg" /&gt;
&lt;p&gt;Sem entrar nos detalhes, vamos entender esse plano. Primeiro, temos que saber olhar de baixo pra cima.&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Foi feito um index scan na tabela film_actor (a última linha)&lt;/li&gt;
&lt;li&gt;Foi feito um index table scan na tabela actor&lt;/li&gt;
&lt;li&gt;Essas duas tabelas foram unidas usando o algoritmo Merge Join&lt;/li&gt;
&lt;li&gt;O resultado foi ordenado pela coluna fil,_actor.film_id&lt;/li&gt;
&lt;li&gt;Foi feito um table scan na tabela film, e o resultado foi ordenado pela coluna film id&lt;/li&gt;
&lt;li&gt;O resultado do scan na tabela film foi juntado com o resultado anterior, usando o algoritmo Merge Join&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;E aqui está o plano gerado pelo pagila (o banco no qual foi rodado o ANALYZE):&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Merge Join  (cost=562.49..697.92 rows=5462 width=27) (actual time=47.691..78.064 rows=5462 loops=1)
  Merge Cond: (&amp;quot;outer&amp;quot;.film_id = &amp;quot;inner&amp;quot;.film_id)
  -&amp;gt;  Index Scan using film_pkey on film f  (cost=0.00..51.00 rows=1000 width=22) (actual time=0.011..2.442 rows=1000 loops=1)
  -&amp;gt;  Sort  (cost=562.49..576.15 rows=5462 width=11) (actual time=47.668..56.011 rows=5462 loops=1)
        Sort Key: film_actor.film_id
        -&amp;gt;  Merge Join  (cost=0.00..223.43 rows=5462 width=11) (actual time=0.025..32.304 rows=5462 loops=1)
              Merge Cond: (&amp;quot;outer&amp;quot;.actor_id = &amp;quot;inner&amp;quot;.actor_id)
              -&amp;gt;  Index Scan using actor_pkey on actor a  (cost=0.00..6.20 rows=200 width=13) (actual time=0.007..0.459 rows=200 loops=1)
              -&amp;gt;  Index Scan using film_actor_pkey on film_actor  (cost=0.00..148.46 rows=5462 width=4) (actual time=0.008..12.557 rows=5462 loops=1)
Total runtime: 86.610 ms
&lt;/pre&gt;
&lt;img alt="http://waltercruz.com/images/artigos/query1comanalyze.jpg" src="http://waltercruz.com/images/artigos/query1comanalyze.jpg" /&gt;
&lt;p&gt;Vamos agora analizar esse plano:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Foi feito um scan na tabela film_actor pelo índice film_actor_pkey (a chave primária da tabela)&lt;/li&gt;
&lt;li&gt;Agora, um scan na tabela actor pelo índice&lt;/li&gt;
&lt;li&gt;As tabelas foram JOINadas pelo actor_id, e o resultado foi ordenado por film_actor.actor_id&lt;/li&gt;
&lt;li&gt;A tabela film foi varrida pelo índice.&lt;/li&gt;
&lt;li&gt;O resultado do JOIN anterior foi juntado com a tabela film.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Procurando clientes e filiais:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Filial &amp;#39;&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;filial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;store_id&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;filial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first_name&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_name&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="n"&gt;filial&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;filial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;store_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;store_id&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Aqui, o explain do banco sem ANALYZE:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Nested Loop  (cost=1.02..17.65 rows=1 width=84) (actual time=0.042..15.491 rows=584 loops=1)
  Join Filter: (&amp;quot;inner&amp;quot;.store_id = &amp;quot;outer&amp;quot;.store_id)
  -&amp;gt;  Seq Scan on customer c  (cost=0.00..16.49 rows=3 width=82) (actual time=0.013..1.621 rows=584 loops=1)
        Filter: (active = 1)
  -&amp;gt;  Materialize  (cost=1.02..1.04 rows=2 width=4) (actual time=0.002..0.006 rows=2 loops=584)
        -&amp;gt;  Seq Scan on store filial  (cost=0.00..1.02 rows=2 width=4) (actual time=0.004..0.011 rows=2 loops=1)
Total runtime: 16.480 ms
&lt;/pre&gt;
&lt;p&gt;Repare que foi usado um materialize = um plano foi salvo num arquivo temporário.&lt;/p&gt;
&lt;p&gt;E agora, o explain do banco com ANALYZE:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Nested Loop  (cost=4.05..46.50 rows=584 width=23) (actual time=0.175..6.097 rows=584 loops=1)
  -&amp;gt;  Seq Scan on store filial  (cost=0.00..1.02 rows=2 width=4) (actual time=0.008..0.015 rows=2 loops=1)
  -&amp;gt;  Bitmap Heap Scan on customer c  (cost=4.05..16.80 rows=300 width=21) (actual time=0.136..1.248 rows=292 loops=2)
        Recheck Cond: (&amp;quot;outer&amp;quot;.store_id = c.store_id)
        Filter: (active = 1)
        -&amp;gt;  Bitmap Index Scan on idx_fk_store_id  (cost=0.00..4.05 rows=300 width=0) (actual time=0.122..0.122 rows=300 loops=2)
              Index Cond: (&amp;quot;outer&amp;quot;.store_id = c.store_id)
Total runtime: 7.160 ms
&lt;/pre&gt;
&lt;p&gt;Nesse caso, como as estatísticas já estavam atualizadas, o banco optou por uma otimização e usou o Bitmap Index Scan e o Bitmap Heap Scan.&lt;/p&gt;
&lt;p&gt;O Bitmap Index Scan é usado em colunas que tem pouca variação (colunas de baixa cardinalidade). No caso, para cada cliente, eu tenho apenas duas opções
de filias às quais ele pode estar associado (1 e 2). Então, o banco cria um mapa de bits para cada um desses valores. Esse mapa é carregado em memória, e é
juntado com o resultado do table scan em store, que tem apenas dois valores possíveis.&lt;/p&gt;
&lt;p&gt;No último select, ainda assim foi feito um Table Scan na tabela store, onde alguém poderia argumentar que seria melhor um index scan.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="random-page-cost"&gt;
&lt;h1&gt;Random Page Cost&lt;/h1&gt;
&lt;p&gt;Existe um parâmetro de configuração, chamado &lt;strong&gt;random_page_cost&lt;/strong&gt; que determina qual o peso que o PostgreSQL dá a leituras não sequencias no disco.
Aumentar esse valor favorece o uso de table scans, abaixá-lo favorece o uso de índices. O valor padrão é 4.0.&lt;/p&gt;
&lt;p&gt;Para um pequeno teste, vamos baixá-lo para 2.0 e executar a query no banco pagila.&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Merge Join  (cost=0.00..42.34 rows=584 width=23) (actual time=0.128..6.012 rows=584 loops=1)
  Merge Cond: (&amp;quot;outer&amp;quot;.store_id = &amp;quot;inner&amp;quot;.store_id)
  -&amp;gt;  Index Scan using store_pkey on store filial  (cost=0.00..3.02 rows=2 width=4) (actual time=0.092..0.098 rows=2 loops=1)
  -&amp;gt;  Index Scan using idx_fk_store_id on customer c  (cost=0.00..27.64 rows=584 width=21) (actual time=0.015..2.150 rows=584 loops=1)
        Filter: (active = 1)
Total runtime: 6.996 ms
&lt;/pre&gt;
&lt;p&gt;Como o custo do índice estava mais baixo, o otimizador preferiu usar o índice do que gerar o índice bitmap em memória.&lt;/p&gt;
&lt;p&gt;Agora, no pagila2, o plano gerado é semelhante ao plano do banco que está com as estatísticas atualizadas:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Nested Loop  (cost=2.01..12.74 rows=1 width=84) (actual time=0.264..5.829 rows=584 loops=1)
  -&amp;gt;  Seq Scan on store filial  (cost=0.00..1.02 rows=2 width=4) (actual time=0.012..0.019 rows=2 loops=1)
  -&amp;gt;  Bitmap Heap Scan on customer c  (cost=2.01..5.82 rows=3 width=82) (actual time=0.182..1.236 rows=292 loops=2)
        Recheck Cond: (&amp;quot;outer&amp;quot;.store_id = c.store_id)
        Filter: (active = 1)
        -&amp;gt;  Bitmap Index Scan on idx_fk_store_id  (cost=0.00..2.01 rows=3 width=0) (actual time=0.165..0.165 rows=300 loops=2)
              Index Cond: (&amp;quot;outer&amp;quot;.store_id = c.store_id)
Total runtime: 6.861 ms
&lt;/pre&gt;
&lt;p&gt;Executada no pagila2, a query gera o mesmo plano que havia sido gerado para random_page_cost = 4.0. E, com random_page_cost = 2.0, a primeira query do nosso teste
passa a usar o índice, mesmo sem o analyze. Mas os custos não são os mesmos - isso porque o pagila2 ainda não teve as estatísticas atualizadas.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="configura-es-do-otimizador"&gt;
&lt;h1&gt;Configurações do otimizador&lt;/h1&gt;
&lt;p&gt;O arquivo de configuração do postgresql (postgresql.conf) contém várias opções que tratam da configuração do otimizador e dos vários métodos de consulta.
São eles:&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="100%" /&gt;
&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;enable_bitmapscan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;enable_hashagg&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;enable_hashjoin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;enable_indexscan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;enable_mergejoin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;enable_nestloop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;enable_seqscan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;enable_sort&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;enable_tidscan&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Todos eles podem ser configurados para on ou off, e por padrão todos vem habilitados. Alguns pontos a serem notados:&lt;/p&gt;
&lt;p&gt;enable_seqscan = off não desabilita de fato o scan sequencial, já que essa pode ser a única forma de executar certas consultas. O que esse parâmetro faz ne verdade
é aumentar o custo de um table scan. O mesmo vale para enable_nestloop (algumas vezes não há forma de resolver uma consulta, exceto por esse tipo de loop) e enable_sort.&lt;/p&gt;
&lt;p&gt;Esses valores podem ser alterados em tempo de execução para uma sessão em particular, usando o comando SET.&lt;/p&gt;
&lt;p&gt;Esse é apenas um resumo. As descrição completa das opções relativas ao otimizador são encontradas em: &lt;a class="reference external" href="http://www.postgresql.org/docs/8.0/static/runtime-config.html"&gt;http://www.postgresql.org/docs/8.0/static/runtime-config.html&lt;/a&gt; .&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="listando-atores-e-filmes"&gt;
&lt;h1&gt;Listando atores e filmes&lt;/h1&gt;
&lt;p&gt;A primeira consulta, embora trouxesse a lista de filmes e atoes, não trazia o resultado agrupado pelos filmes, com uma lista de todos os atores.&lt;/p&gt;
&lt;p&gt;Vamor fazer essa consulta então.&lt;/p&gt;
&lt;p&gt;Existem duas sintaxes que podem ser usadas. A primeira:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;select&lt;/span&gt;
     &lt;span class="n"&gt;film&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="n"&gt;array_to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;array_accum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first_name&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39; &amp;#39;&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;,&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;actors&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt;
     &lt;span class="n"&gt;film&lt;/span&gt;
     &lt;span class="k"&gt;inner&lt;/span&gt; &lt;span class="k"&gt;join&lt;/span&gt; &lt;span class="n"&gt;film_actor&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;film&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;film_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;film_actor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;film_id&lt;/span&gt;
     &lt;span class="k"&gt;inner&lt;/span&gt; &lt;span class="k"&gt;join&lt;/span&gt; &lt;span class="n"&gt;actor&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;film_actor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;actor_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;actor_id&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;film&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;film&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;E seu explain:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Sort  (cost=768.81..771.31 rows=1000 width=37) (actual time=151.996..153.650 rows=997 loops=1)
  Sort Key: film.title
  -&amp;gt;  HashAggregate  (cost=698.98..718.98 rows=1000 width=37) (actual time=132.534..142.902 rows=997 loops=1)
        -&amp;gt;  Merge Join  (cost=536.24..671.67 rows=5462 width=37) (actual time=53.739..97.676 rows=5462 loops=1)
              Merge Cond: (&amp;quot;outer&amp;quot;.film_id = &amp;quot;inner&amp;quot;.film_id)
              -&amp;gt;  Index Scan using film_pkey on film  (cost=0.00..51.00 rows=1000 width=22) (actual time=0.012..3.780 rows=1000 loops=1)
              -&amp;gt;  Sort  (cost=536.24..549.90 rows=5462 width=21) (actual time=53.716..63.214 rows=5462 loops=1)
                    Sort Key: film_actor.film_id
                    -&amp;gt;  Merge Join  (cost=0.00..197.18 rows=5462 width=21) (actual time=0.026..36.636 rows=5462 loops=1)
                          Merge Cond: (&amp;quot;outer&amp;quot;.actor_id = &amp;quot;inner&amp;quot;.actor_id)
                          -&amp;gt;  Index Scan using actor_pkey on actor  (cost=0.00..6.20 rows=200 width=23) (actual time=0.007..0.536 rows=200 loops=1)
                          -&amp;gt;  Index Scan using film_actor_pkey on film_actor  (cost=0.00..122.21 rows=5462 width=4) (actual time=0.009..14.883 rows=5462 loops=1)
Total runtime: 159.766 ms
&lt;/pre&gt;
&lt;p&gt;Uma outra forma possível de fazer essa consulta é essa:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;array_to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;first_name&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;film_actor&lt;/span&gt; &lt;span class="n"&gt;fa&lt;/span&gt;
&lt;span class="k"&gt;join&lt;/span&gt; &lt;span class="n"&gt;actor&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;fa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;actor_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;actor_id&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;fa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;film_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;film_id&lt;/span&gt;
&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;, &amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;film_actor&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;film&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Será que essa forma, além de ser menos compacta, é mais rápida também?&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Seq Scan on film f  (cost=0.00..16382.69 rows=1000 width=22) (actual time=1.214..712.919 rows=1000 loops=1)
  SubPlan
    -&amp;gt;  Merge Join  (cost=9.56..16.34 rows=5 width=9) (actual time=0.199..0.687 rows=5 loops=1000)
          Merge Cond: (&amp;quot;outer&amp;quot;.actor_id = &amp;quot;inner&amp;quot;.actor_id)
          -&amp;gt;  Index Scan using actor_pkey on actor a  (cost=0.00..6.20 rows=200 width=13) (actual time=0.005..0.347 rows=165 loops=1000)
          -&amp;gt;  Sort  (cost=9.56..9.57 rows=5 width=2) (actual time=0.056..0.069 rows=5 loops=1000)
                Sort Key: fa.actor_id
                -&amp;gt;  Bitmap Heap Scan on film_actor fa  (cost=2.02..9.50 rows=5 width=2) (actual time=0.020..0.033 rows=5 loops=1000)
                      Recheck Cond: (film_id = $0)
                      -&amp;gt;  Bitmap Index Scan on idx_fk_film_id  (cost=0.00..2.02 rows=5 width=0) (actual time=0.013..0.013 rows=5 loops=1000)
                            Index Cond: (film_id = $0)
Total runtime: 714.655 ms
&lt;/pre&gt;
&lt;img alt="http://waltercruz.com/images/artigos/filmes_atores_explain_1.jpg" src="http://waltercruz.com/images/artigos/filmes_atores_explain_1.jpg" /&gt;
&lt;p&gt;Parece que não.&lt;/p&gt;
&lt;p&gt;Vemos que na consulta, o maior tempo gasto está no merge join (de 0.199 até 0.687). Vamos desabilitá-lo então. Isso é feito com o comando:
set enable_mergejoin = off.&lt;/p&gt;
&lt;p&gt;Vejamos o novo plano:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Seq Scan on film f  (cost=0.00..24679.64 rows=1000 width=22) (actual time=0.368..162.552 rows=1000 loops=1)
  SubPlan
    -&amp;gt;  Nested Loop  (cost=2.02..24.63 rows=5 width=9) (actual time=0.037..0.136 rows=5 loops=1000)
          -&amp;gt;  Bitmap Heap Scan on film_actor fa  (cost=2.02..9.50 rows=5 width=2) (actual time=0.020..0.039 rows=5 loops=1000)
                Recheck Cond: (film_id = $0)
                -&amp;gt;  Bitmap Index Scan on idx_fk_film_id  (cost=0.00..2.02 rows=5 width=0) (actual time=0.014..0.014 rows=5 loops=1000)
                      Index Cond: (film_id = $0)
          -&amp;gt;  Index Scan using actor_pkey on actor a  (cost=0.00..3.01 rows=1 width=13) (actual time=0.007..0.009 rows=1 loops=5462)
                Index Cond: (&amp;quot;outer&amp;quot;.actor_id = a.actor_id)
Total runtime: 164.212 ms
&lt;/pre&gt;
&lt;img alt="http://waltercruz.com/images/artigos/filmes_atores_explain_2.jpg" src="http://waltercruz.com/images/artigos/filmes_atores_explain_2.jpg" /&gt;
&lt;p&gt;Fiz um pequeno teste com o beta do PostgreSQL 8.2, e para minha surpresa, o plano gerado para essa consulta é o mesmo que é gerado no
8.1 usando enable_mergejoin = off (com random_page_cost = 2.0).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="entendendo-os-cones-usados-no-pgadmin"&gt;
&lt;h1&gt;Entendendo os ícones usados no pgadmin&lt;/h1&gt;
&lt;p&gt;Escrevi uma pequena descrição para os ícones usados no pgadmin, que está disponível em: &lt;a class="reference external" href="http://artigos.waltercruz.com/postgresql/explain"&gt;http://artigos.waltercruz.com/postgresql/explain&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Outra coisa interessante pra se notar é que a grossura das setas é proporcional ao custo das operações.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
</summary><category term="postgresql"></category></entry><entry><title>Tags com o SQLAlchemy</title><link href="http://artigos.waltercruz.com/tags-com-sqlalchemy" rel="alternate"></link><updated>2008-10-20T14:14:58Z</updated><author><name>Walter Cruz</name><uri>http://waltercruz.com</uri></author><id>tag:artigos.waltercruz.com,2008-10-20:/tags-com-sqlalchemy</id><summary type="html">&lt;div class="document"&gt;
&lt;p&gt;Tradução de: &lt;a class="reference external" href="http://pieceofpy.com/index.php/2008/10/09/tags-with-sqlalchemy/"&gt;http://pieceofpy.com/index.php/2008/10/09/tags-with-sqlalchemy/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Existem diversos artigos da Net sobre SQLalchemy. Implementação de um blog, um wiki, mesmo outros artigos sobre a implementação de tags. Alguns são bons, outros bem pobres, e alguns apenas estão desatualizados. Após alguma pesquisa sobre as melhores práticas sobre como implementar um sistema de Tags com o SQLalchemy eu cheguei à solução que você está prestes a ler.&lt;/p&gt;
&lt;p&gt;Eu extraí esse exemplos de um código real em produção. Apenas renomeei e resumi um pouco para esse post. Peguei a convenção de nomes do exemplo SimpleSite do Pylons. Aqui está o layout das tabelas. Simples. Uma página, tag, e tabela de relação.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;page_table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;page&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;page_seq_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;optional&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;primary_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;sa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unicode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;tag_table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;tag&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;taq_seq_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;optional&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;primary_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;sa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unicode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;pagetag_table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;pagetag&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;pagetag_seq_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;optional&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;primary_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;sa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;pageid&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;page.id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;sa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;tagid&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;tag.id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Agora, a parte importente, o mapeamento. O mapeador é o que diz ao sqlalchemy o que você está tentando fazer e como relacionar essas ForeignKeys. Ele faz o trabalho pesado por você.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;

&lt;span class="n"&gt;orm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tag_table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;orm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;page_table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;properties&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;tags&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;orm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;secondary&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pagetag_table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cascade&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;all,delete-orphan&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Isso faz duas coisas. Configura o relacionamento e também usa a regra built-in de cascata do SQLalchemy para garantir que não existam tags órfãs no banco de dados.&lt;/p&gt;
&lt;p&gt;Agora podemos usar o modelo que configuramos. Aqui, eu apenas iniciei meu paster shell para que eu pudesse trabalhar alguns exemplos de uso.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Example Page&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;tag&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;tag_q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tag_q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# filter pages by tag(s)&lt;/span&gt;
&lt;span class="n"&gt;page_q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;pages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page_q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;tags&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;tag&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c"&gt;# delete-orphans does the work for us here...&lt;/span&gt;
&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tag_q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# tag cloud anyone?&lt;/span&gt;
&lt;span class="c"&gt;# see the source code linked below for a properly weighted tag cloud.&lt;/span&gt;
&lt;span class="n"&gt;tag_q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;*&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;tagcount&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tag_r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tag_q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tag&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pagetag_table&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tagid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tag&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c"&gt;# what about pages with related tags?&lt;/span&gt;
&lt;span class="n"&gt;page_q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;taglist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;tag1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;tag2&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;tagcount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;taglist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;page_q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tag&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;in_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;taglist&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;\
&lt;span class="n"&gt;group_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;having&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;tagcount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ok, agora a parte divertida, e sobre todas as tags relacionadas? Uma intersecção entre um número arbitrário de relacionamentos muitos para muitos? Para isso eu adicionei um método estático a minha classe. Algo assim:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nd"&gt;@staticmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_related&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt;
        &lt;span class="n"&gt;tag_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;inner_q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;select&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;pagetag_table&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pageid&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;inner_w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inner_q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;and_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pagetag_table&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tagid&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;Tag&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;Tag&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;in_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pagetag_table&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pageid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;having&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pagetag_table&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pageid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;tag_count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;correlate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;outer_q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;select&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;Tag&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Tag&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pagetag_table&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shipid&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
        &lt;span class="n"&gt;outer_w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;outer_q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;and_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pagetag_table&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pageid&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;in_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inner_w&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;not_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Tag&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;in_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
            &lt;span class="n"&gt;Tag&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;pagetag_table&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tagid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pagetag_table&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tagid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;related_tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outer_w&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;related_tags&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Um grande agradecimento ao &lt;a class="reference external" href="http://cakephp.org/"&gt;CakePHP&lt;/a&gt; e ao &lt;a class="reference external" href="http://tagschema.com/"&gt;TagSchema&lt;/a&gt; pelas idéias, conceitos e exemplos de implementação.&lt;/p&gt;
&lt;p&gt;Você pode encontrar o código real no qual esse texto foi baseado em:
&lt;a class="reference external" href="http://trac.pieceofpy.com/pieceofpy/browser/tags-sqlalchemy"&gt;http://trac.pieceofpy.com/pieceofpy/browser/tags-sqlalchemy&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
</summary><category term="banco de dados"></category></entry></feed>