.. .. META INFORMATION OF TRANSLATION .. .. $TranslationStatus: Done, waiting for revision $ .. $OriginalRevision: 11332 $ .. $TranslationAuthors: Robson Mendonça $ .. .. INFO OF THIS FILE (DO NOT EDIT! UPDATED BY SUBVERSION) .. .. $HeadURL$ .. $LastChangedRevision$ .. $LastChangedBy$ .. $LastChangedDate$ .. ========================================= Tags e filtros de template personalizados ========================================= Introdução ========== O sistema de template do Django vem com uma larga variedade de :doc:`tags e filtros embutidos ` projetados para direcionar a lógida da apresentação que sua aplicação. Todavia, você pode se encontrar precisando de funcionalidades que não são cobertas pelo conjunto de primitivas de template. Você pode extender o motor de template definindo tags e filtros personalizados usando Python, e então torná-los disponíveis aos seus templates usando a tag ``{% load %}``. Layout do código ---------------- Tags e filtros de template customizados devem estar dentro de uma Django app. Se elas estão relacionadas a uma app existente, faz mais sentido estarem empacotados na app; caso contrário, você deveria criar uma nova app para armazená-los. A app deve conter um diretório ``templatetags``, no mesmo nível do ``models.py``, ``views.py``, etc. Se ele não existe ainda, é só criá-lo - não esqueça o arquivo ``__init__.py`` para assegurar que o diretório seja tratado como um pacote Python. Suas tags e filtros personalizados ficaram nesse módulo dentro do diretório ``templatetags``. O nome do arquivo do módulo é o nome que você usará para carregar as tags depois, então seja cuidadoso ao criar um nome que não colida com as tags e filtros de outras apps. Por exemplo, se sua tags/filtros estão num arquivo chamado ``poll_extras.py``, o layout de sua app deve parecer com isso:: polls/ models.py templatetags/ __init__.py poll_extras.py views.py E nos seus templates você poderia usar o seguinte: .. code-block:: html+django {% load poll_extras %} A app que contém as tags personalizadas deve estar no :setting:`INSTALLED_APPS` para que a tag ``{% load %}`` funcione. Esta é uma medida de segunrança: Ela permite que você armazene código Python para muitas bibliotecas de template num único servidor, sem permitir o acesso a todas elas para toda instalação do Django. Não há limites de quantos módulos você pode colocar no pácote ``templatetags``. Só tenha em mente que uma declaração ``{% load %}`` carregará tags/filtros para uma dado nome de módulo Python, não o nome da app. Para uma biblioteca de tag ser válida, o módulo deve conter uma variável chamada ``register`` que é uma instância do ``templates.Library``, na qual todas as tags e filtros são registrados. Então, no topo de seu módulo, coloque o seguinte:: from django import template register = template.Library() .. admonition:: Por trás das cenas Para uma tonelada de exemplos, leia o código font dos filtros e tags padrão do Django. Eles estão em ``django/templates/defaultfilters.py`` e ``django/template/defaulttags.py``, respectivamente. Escrevendo filtros de template personalizados --------------------------------------------- Filtros personalizados são como funções Python que recebem um ou dois argumentos: * O valor de uma variável (input) -- não necessariamente uma string. * O valor de um argumento -- este pode ter um valor padrão, ou ser deixado de fora. Por exemplo, no filtro ``{{ var|foo:"bar"}}``, ao filtro ``foo`` seria passado a variável ``var`` e o argumento ``"bar"``. Funções filtro devem sem retornar algo. Elas não lançam exceções. Elas falham silenciosamente. No caso de um erro, elas devem retornar a entrada original ou uma string vazia -- o que fizer mais sentido. Aqui temos um exemplo de definição de filtro:: def cut(value, arg): "Remove todos os valores do arg da string fornecida." return value.replace(arg, '') E aqui tem um exemplo de como este filtro poderia ser usado: .. code-block:: html+django {{ somevariable|cut:"0" }} A maioria dos filtros não recebem argumentos. Neste caso, é só deixar o argumento fora de sua função. Exemplo:: def lower(value): # Somente um argumento. "Converte uma string para minúsculo" return value.lower() Filtros de template que esperam strings ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Se você estiver escrevendo um filtro de template que somente expera uma string como o primeiro argumento, você deve usar o decorador ``stringfilter``. Este converterá um objeto para seu valor em string antes de ser passado para a sua função:: from django.template.defaultfilters import stringfilter @stringfilter def lower(value): return value.lower() Desta forma, você será poderá passar, digo, um inteiro para este filtro, e ele não causará um ``AttributeError`` (pois inteiros não possuem métodos ``lower()``). Registrando filtros personalizados ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Uma vez que tenha escrito sua definição de filtro, você precisa registrá-lo na sua instância ``Library``, para torná-lo disponível na linguagem de template do Django:: register.filter('cut', cut) register.filter('lower', lower) O método ``Library.filter()`` recebe dois argumentos: 1. O nome do filtro -- uma string. 2. A função de compilação -- uma função Python (não o nome da função como uma string). Você pode usar ``register.filter()`` como um decorador, no entanto:: @register.filter(name='cut') @stringfilter def cut(value, arg): return value.replace(arg, '') @register.filter @stringfilter def lower(value): return value.lower() Se você deixa desligado o argumento ``name``, como no segundo exemplo acima, o Django usará o nome da função como o nome do filtro. Filtros e auto-escaping ~~~~~~~~~~~~~~~~~~~~~~~ Quando esiver escrevendo um filtro personalizado, pense um pouco em como o filtro irá interagir com o comportamento de auto-escaping do Django. Note que há três tipos de strings que podem ser passadas por dentro de um código de template: * **Strings puras** são tipos nativos do Python ``str`` ou ``unicode``. Na saída, elas são escapadas se o auto-escaping estiver em efeito e apresenta-se inalterado. * **Strings seguras** são strings que foram marcadas como seguras por um escape fora de tempo. Qualquer escape necessário já foi feito. Elas são comumente usados para a saída que contém HTML puro que destina-se a ser interpretado, assim como está, no lado do cliente. Internamente, estas strings são do typo ``SafeString`` ou ``SafeUnicode``. Elas compartilham uma classe básica comum de ``SafeData``, então você pode testá-las usando um código como este:: if isinstance(value, SafeData): # Faça algo com a string "safe". * **Strings marcadas como "precisando escapar"** são *sempre* escapadas na saída, indiferente se elas estão num bloco ``autoescape`` ou não. Essas strings são somente escapadas uma vez, contudo, mesmo se o auto-escaping se aplica. Internamente, estas strings são do tipo ``EscapeString`` ou ``EscapeUnicode``. Geralmente você não tem de se preocupar com eles; eles existem para implementação do filtro ``escape``. Código de filtros de template caem em uma de duas situações: 1. Seu filtro não introduz quaisquer caracteres HTML não seguros (``<``, ``>``, ``'``, ``"`` or ``&``) no resultado que ainda não foi aprensentado. Neste caso, você pode deixar o Django se preocupar com todo o auto-escaping por você. Tudo que você precisa fazer é colocar o atributo ``is_safe`` sobre o sua função filtro e setá-lo como ``True``, assim:: @register.filter def myfilter(value): return value myfilter.is_safe = True Este atributo diz ao Django que se uma string "safe" é passada para o seu filtro, o resultado será mantido "safe" e se uma string não-safe é passada, o Django automaticamente a escapará, se necessário. Você pode pensar que nisso como significando "este filtro é seguro -- ele não introduz qualquer possibilidade de HTML não seguro." A razão de ``is_safe`` ser necessário é porque há abundância de operadores de string normais que tornarão um objeto ``SafeData`` uma string normal ``str`` ou ``unicode`` e, ao invés de tentar pegá-los todos, o que será muito difícil, o Django repara o dano depois que o filtro foi completado. Por exemplo, suponhamos que você tem um filtro que adiciona uma string ``xx`` no final de uma entrada. Já que isso não introduz caracteres HTML perigosos ao resultado (com exceção de alguns que já estavam presentes), você deve marcar seu filtro com ``is_safe``:: @register.filter def add_xx(value): return '%sxx' % value add_xx.is_safe = True Quando este filtro é usado num template onde auto-escaping está habilitado, o Django escapa a saída sempre que a entrada já não estiver marcada como "safe". Por padrão, ``is_safe`` é ``False``, e você pode omiti-lo de qualquer filtro onde ele não é requerido. Seja cuidadoso quando decidir se seu filtro realmente deixa strings safe como safe. Se estiver *removendo* caracteres, você pode inadvertidamente deixa tabs HTML desbalanceadas ou entidades no resultado. Por exemplo, removendo um ``>`` de uma entrada por tornar um ```` em ``A hora é {% current_time "%Y-%m-%d %I:%M %p" %}.

.. _`sintaxe do strftime`: http://docs.python.org/library/time.html#time.strftime O parser para esta função deve abarrar o parâmetro e criar um objeto ``Node``:: from django import template def do_current_time(parser, token): try: # split_contents() sabe que não é para dividir strings entre aspas. tag_name, format_string = token.split_contents() except ValueError: raise template.TemplateSyntaxError, "%r tag requires a single argument" % token.contents.split()[0] if not (format_string[0] == format_string[-1] and format_string[0] in ('"', "'")): raise template.TemplateSyntaxError, "%r tag's argument should be in quotes" % tag_name return CurrentTimeNode(format_string[1:-1]) Notas: * O ``parser`` é o objeto parser de template. Não não precisamos dele nesse exemplo. * O ``token.contents`` é uma string de conteúdos puros da tag. No nosso exemplo, é ``'current_time "%Y-%m-%d %I:%M %p"'``. * O método ``token.split_contents()`` quebra os argumentos nos espaços mantendo as strings entre aspas agrupadas. O mais simples ``token.contents.split()`` não seria tão robusto, e dividiria ingenuamente *todos* os espaços, incluindo aqueles dentro de strings entre aspas agrupadas. É uma boa idéia sempre usar ``token.split_contents()``. * Essa função é responsável por lançar ``django.template.TemplateSyntaxError``, com mensagens úteis, para qualquer erro de sintaxe. * As exceções ``TemplateSystemError`` usam a variável ``tag_name``. Não embuta o nome da tag nas suas mensagens de erro, porque eles casam com o nome de sua função. O ``token.contents.split()[0]`` ''sempre'' será o nome da sua tag -- mesmo quando a tag não tenha argumentos. * A função retorna um ``CurrentTimeNode`` com tudo o que o nodo precisa saber sobre esta tag. Neste caso, é só passar o argumento -- ``"%Y-%m-%d %I:%M %p"``. As aspas, no início e no final, da template tag são removidas no ``format_string['1:-1']``. * O parseamento é de muito baixo nível. Os desenvolvedores do Django têm experimentado escrever pequenos frameworks sobre o estes sistema de parse, usando técnicas como gramáticas EBNF, mas estes experimentos tornam o motor de template muito lento. Ele é de baixo nível porque é mais rápido. Escrevendo o renderizador ~~~~~~~~~~~~~~~~~~~~~~~~~ O segundo passo em escrever tags personalizadas é definir uma subclasse ``Node`` que tem um método ``render()``. Continuando o exemplo acima, nós precisamos definir ``CurrentTimeNode``:: from django import template import datetime class CurrentTimeNode(template.Node): def __init__(self, format_string): self.format_string = format_string def render(self, context): return datetime.datetime.now().strftime(self.format_string) Notas: * ``__init__()`` recebe o ``format_string`` do ``do_current_time()``. Sempre passe quaisquer opções/parâmetros/argumentos para um ``Node`` via seu ``__init__()``. * O método ``render()`` é onde o trabalho realmente acontece. * O ``render()`` nunca deve lançar um ``TemplateSyntaxError`` ou qualquer outra exceção. Ele deve falhar silenciosamente, assim como os filtros de template o fazem. Ultimamente, essa dissociação de compilação e renderização resulta num sistema de template eficiente, pois um template pode renderizar vários contextos sem precisar ser parsiado múltiplas vezes. Considerações do Auto-escaping ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A saída de uma template tag *não* é automaticamente executada através de filtros de auto-escaping. Entretanto, ainda há algumas coisas que você deve ter em mente quando estiver escrevendo uma template tag. Se a função ``render()`` de seu template armazena o resultado numa variável de contexto (ao invés de retornar o resultado numa string), ela deverá se preocupar em chamar ``mark_safe()`` se for apropriado. Quando a variável é finalmente renderizada, ela será afetada pela configuração auto-escape em efeito nessa hora, assim o conteúdo que deveria ser seguro além de escapado precisa ser marcado como tal. Também, se sua template tag cria um novo contexto para executar algumas sub-renderizações, configure o atributo auto-escape para o valor do contexto atual. O método ``__init__`` para a classe ``Context`` recebe um parâmetro chamado ``autoescape`` que você pode usar para este propósito. Por exemplo:: def render(self, context): # ... new_context = Context({'var': obj}, autoescape=context.autoescape) # ... Faça algo com new_context ... Isso não é uma situação muito comum, mas é útil se você estiver renderizando um template você mesmo. Por exemplo:: def render(self, context): t = template.loader.get_template('small_fragment.html') return t.render(Context({'var': obj}, autoescape=context.autoescape)) Se nós tivéssemos esquecido de passar o valor atual ``context.autoescape`` para nosso novo ``Context`` neste exemplo, os resultados teriam *sempre* de ser automaticamente escapados, o que pode não ser um comportamento desejável se a template tag é usada dentro de um bloco ``{% autoescape off %}``. .. _template_tag_thread_safety: Thread-safety considerations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. versionadded:: 1.2 Once a node is parsed, its ``render`` method may be called any number of times. Since Django is sometimes run in multi-threaded environments, a single node may be simultaneously rendering with different contexts in response to two separate requests. Therefore, it's important to make sure your template tags are thread safe. To make sure your template tags are thread safe, you should never store state information on the node itself. For example, Django provides a builtin ``cycle`` template tag that cycles among a list of given strings each time it's rendered:: {% for o in some_list %} Este post foi atualizado em {% format_time blog_entry.date_updated "%Y-%m-%d %I:%M %p" %}.

Inicialmente, ``token.split_contents()`` retornará três valores: 1. O nome da tag ``format_time``. 2. A string "blog_entry.date_updated" (sem as aspas em volta). 3. A string de formatação "%Y-%m-%d %I:%M %p". O valor retornado de ``split_contents()`` incluirá as aspas em strings literais como esta. Agora sua tag deve começar a parecer como isso:: from django import template def do_format_time(parser, token): try: # split_contents() não sabe como separar strings com aspas. tag_name, date_to_be_formatted, format_string = token.split_contents() except ValueError: raise template.TemplateSyntaxError, "%r tag requires exactly two arguments" % token.contents.split()[0] if not (format_string[0] == format_string[-1] and format_string[0] in ('"', "'")): raise template.TemplateSyntaxError, "%r tag's argument should be in quotes" % tag_name return FormatTimeNode(date_to_be_formatted, format_string[1:-1]) Você também tem de mudar o renderizador para receber os conteúdos atuais da propriedade ``date_updated`` de um objeto ``blog_entry``. Isso pode ser realizado usando a classe ``Variable()`` em ``django.template``. Para usar a classe ``Variable``, simplesmente instancie-o com o nome da variável a ser resolvida, e então chame ``variable.resolve(context)``. Assim, por exemplo:: class FormatTimeNode(template.Node): def __init__(self, date_to_be_formatted, format_string): self.date_to_be_formatted = template.Variable(date_to_be_formatted) self.format_string = format_string def render(self, context): try: actual_date = self.date_to_be_formatted.resolve(context) return actual_date.strftime(self.format_string) except template.VariableDoesNotExist: return '' A resolução de variável lançará uma exceção ``VariableDoesNotExist`` se ela não conserguir resolver a string passada no atual contexto da página. Atalho para simple tags ~~~~~~~~~~~~~~~~~~~~~~~ Muitas templates tags recebe vários argumentos -- strings ou variáveis de template -- e retornam uma string depois de fazer algum processamento baseado unicamente no argumento de entrada e algumas informações externas. Por exemplo, a tag ``current_time`` que nós escrevemos acima é desta variedade: nós fornecemos a ela uma string de formatação, e ela retorna a hora como uma string. Para facilitar a criação de tipos de tags, o Django fornece uma função helper, ``simple_tag``. Esta função, que é um método de ``django.template.Library``, recebe uma função que aceita qualquer quantidade de argumentos, a envolve com uma função ``render`` e de outras coisas necessárias mencionadas acima e a registra no sistema de template. Nossa função ``current_time`` poderia, assim, ser escrita desta forma:: def current_time(format_string): return datetime.datetime.now().strftime(format_string) register.simple_tag(current_time) No Python 2.4, a sintaxe de decorador também funciona:: @register.simple_tag def current_time(format_string): ... Uma das coisas que você deve tomar nota sobre o helper ``simple_tag``: * Verificar o número de argumentos requeridos, etc., já é feito na hora que nossa função é chamada, então nós não precisamos fazer isso. * As aspas em volta dos argumentos (se tive alguma) já foram retiradas, desta forma nós só recebemos uma string. * Se o argumento for uma variável de template, à nossa função é passada o valor atual da variável, não a própria variável. Quando sua template tag não precisa acessar o contexto atual, escrever uma função para trabalhar com os valores de entrada e usar o helper ``simple_tag`` é a forma mais fácil de criar uma nova tag. .. versionadded:: 1.3 If your template tag needs to access the current context, you can use the ``takes_context`` argument when registering your tag:: # The first argument *must* be called "context" here. def current_time(context, format_string): timezone = context['timezone'] return your_get_current_time_method(timezone, format_string) register.simple_tag(takes_context=True)(current_time) Or, using decorator syntax:: @register.simple_tag(takes_context=True) def current_time(context, format_string): timezone = context['timezone'] return your_get_current_time_method(timezone, format_string) For more information on how the ``takes_context`` option works, see the section on `inclusion tags`_. .. _howto-custom-template-tags-inclusion-tags: Inclusion tags ~~~~~~~~~~~~~~ Outro tipo comum de template tag é o tipo que mostra algum dados renderizando *outro* template. Por exemplo, a inteface de administração do Django usa templates personalizados para mostrar botões na base das páginas de formulário "adicionar/atualizar". Estes botões sempre são os mesmos, mas o alvo do link muda dependendo do objeto que estiver sendo editado -- então eles são o caso perfeito para se usar um template que é preenchido com detalhes do objeto atual. (No caso do admin, isso é a tag ``submit_row``.) Esses tipos de tags são chamados "inclusion tags". Escrever inclusion tags é provavelmente melhor demonstrado com exemplo. Vamos escrever uma tag que mostra uma lista de escolhas para um dado objeto ``Poll``, como a que foi criada no :ref:`tutorial `. Nós usaremos a tag desta forma: .. code-block:: html+django {% show_results poll %} ...e a saída será algo assim: .. code-block:: html
  • Primeira escolha
  • Segunda escolha
  • Terceira escolha
Primeiro, definir a função que recebe o argumento e produz um dicionário de dados para o resultado. O ponto importante aqui é nós somente precisarmos retornar um dicionário, nada mais complexo. Isso será usado como um contexto de template para o template fragmentado. Exemplo:: def show_results(poll): choices = poll.choice_set.all() return {'choices': choices} Próximo, criar o template usado para renderizar a saída da tag. Este template é uma funcionalidade fixa da tag: o escritor da tag o especifica, não o designer de template. Seguindo nosso exemplo, o template é muito simples: .. code-block:: html+django
    {% for choice in choices %}
  • {{ choice }}
  • {% endfor %}
Agora, criar e registrar a inclusion tag chamando o método ``inclusion_tag()`` de um objeto ``Library``. Seguindo nosso exemplo, se o template acima estiver num arquivo chamado ``results.html`` num diretório que é procurado pelo carregador de template, nós registrariámos a tag desta forma:: # Aqui, register é uma instância do django.template.Library, como antes register.inclusion_tag('results.html')(show_results) Como sempre, a sintaxe de decorador do Python 2.4 também funciona, então poderiámos ter escrito:: @register.inclusion_tag('results.html') def show_results(poll): ... ...ao criar a primeira função. Algumas vezes, suas inclusion tags podem requerer um número grande de argumentos, causando dor aos autores de template ao passar-lhes todos os argumentos e lembrar de suas ordens. Para resolver isso, o Django fornece uma opção ``takes_context`` para inclusion_tags. Se você especificar ``takes_context`` ao criar uma template tag, a tag não terá argumentos requeridos, e a função Python subjacente terá um argumento -- o contexto do template a partir de quando a tag foi chamada. Por exemplo, digamos que você esteja escrevendo uma inclusion tag que sempre será usada no contexto que contém as variáveis ``home_link`` e ``home_title`` que apontam de volta a página principal. Aqui está como a função Python poderia parecer:: # O primeiro argumento *deve* ser chamado "context" aqui. def jump_link(context): return { 'link': context['home_link'], 'title': context['home_title'], } # Registra a tag personalizada como uma inclusion tag com takes_context=True. register.inclusion_tag('link.html', takes_context=True)(jump_link) (Note que o primeiro parâmetro para a função *deve* ser chamado ``context``.) Nessa linha ``registar.inclusion_tag()``, nós especificamos ``takes_context=True`` e o nome do template. Aqui temos como o template ``link.html`` pode parecer: .. code-block:: html+django Pule direto para
{{ title }}. Então, em qualquer hora que você desejar usar essa tag personalizada, carregue sua biblioteca e chame-a sem quaisquer argumentos, dessa forma: .. code-block:: html+django {% jump_link %} Note que quando você estiver usando ``takes_context=True``, não há necessidade de passar argumentos para a template tag. Ela automaticamente tem acesso ao contexto. O parâmetro ``takes_context`` é por padrão ``False``. Quando ele é configurado para *True*, à tag é passada o objeto de contexto, como nesse exemplo. Essa é a única diferença entre este caso e o exemplo anterior da ``inclusion_tag``. Configurando uma variável no contexto ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ O exemplo acima simplesmente mostra um valor. Geralmente, é mais flexível se suas template tags configurarem variáveis ou invés de mostrar valores. Desta forma os autores de templates podem reusar os valores que suas templates tags criam. Para setar uma variável no contexto, é só usar atribuição de dicionário no contexto de objeto no método ``render()``. Aqui temos uma versão atualizada do ``CurrentTimeNode`` que configura uma variável de template ``current_time`` ao invés de mostrá-la:: class CurrentTimeNode2(template.Node): def __init__(self, format_string): self.format_string = format_string def render(self, context): context['current_time'] = datetime.datetime.now().strftime(self.format_string) return '' Note que ``render()`` retorna uma string vazia. O ``render()`` deve sempre retornar uma string. Se tudo o que a template tag faz é setar uma variável, o ``render()`` deve retornar uma string vazia. Aqui temos como poderiámos usar esta nova versão de tag: .. code-block:: html+django {% current_time "%Y-%M-%d %I:%M %p" %}

The time is {{ current_time }}.

Porém, há um problema com ``CurrentTimeNode2``: O nome da variável ``current_time`` é nativo. Isso significa que você precisará assegurar-se de que seu template não use ``{{ current_time }}`` em nenhum lugar mais, por que o ``{% current_time %}`` será cegamente sobrescrito pelo valor desta variável. Uma solução clara é fazer uma template tag especificar o nome da variável de saída, desta forma:: .. code-block:: html+django {% get_current_time "%Y-%M-%d %I:%M %p" as my_current_time %}

The current time is {{ my_current_time }}.

Para fazer isso, você precisar refatorar ambos funções de compilação e classe ``Node``, tipo:: class CurrentTimeNode3(template.Node): def __init__(self, format_string, var_name): self.format_string = format_string self.var_name = var_name def render(self, context): context[self.var_name] = datetime.datetime.now().strftime(self.format_string) return '' import re def do_current_time(parser, token): # Essa versão usa uma expressão regular para parsear a o conteúdo da tag. try: # Separando None == separando por espaços. tag_name, arg = token.contents.split(None, 1) except ValueError: raise template.TemplateSyntaxError, "%r tag requires arguments" % token.contents.split()[0] m = re.search(r'(.*?) as (\w+)', arg) if not m: raise template.TemplateSyntaxError, "%r tag had invalid arguments" % tag_name format_string, var_name = m.groups() if not (format_string[0] == format_string[-1] and format_string[0] in ('"', "'")): raise template.TemplateSyntaxError, "%r tag's argument should be in quotes" % tag_name return CurrentTimeNode3(format_string[1:-1], var_name) A diferença aqui é que o ``do_current_time()`` pega a string de formato e o nome da variável, passando ambos ao ``CurrenttimeNode3``. Parseando até outro tag de bloco ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Template tags podem funcionar em conjunto. Por exemplo, a tag padrão ``{% comment %}`` esconde tudo até o ``{% endcomment %}``. Para criar uma template tag como esta, use ``parser.parse()`` na sua função de compilação. Aqui temos como a tag padrão ``{% comment %}`` é implementado:: def do_comment(parser, token): nodelist = parser.parse(('endcomment',)) parser.delete_first_token() return CommentNode() class CommentNode(template.Node): def render(self, context): return '' O ``parser.parse()`` recebe uma tupla de nomes de tags de bloco ''para parsear até ela''. Ela retorna uma instância do ``django.template.NodeList``, que é uma lista de todos objetos ``Node`` que o parser encontrar ''antes'' de encontrar quaisquer tags chamadas na tupla. No ``"nodelist = parser.parse(('endcomment',))"`` do exemplo acima, ``nodelist`` é uma lista de todos os nodos entre o ``{% comment %}`` e ``{% endcomment %}``, não contando ``{% comment %}`` e ``{% endcomment %}``. Depois que ``parser.parse()`` é chamado, o parser ainda não "consumiu" a tag ``{% endcomment %}``, assim o código precisa explicitamente chamar ``parser.delete_first_token()``. O ``CommentNode.render()`` simplesmente retorna uma string vazia. Qualquer coisa entre ``{% comment %}`` e ``{% endcomment %}`` é ignorado. Parseando até outra tag de bloco, e salvando conteúdos ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ No exemplo anterior, ``do_comment()`` tudo descartado entre ``{% comment %}`` e ``{% endcomment %}``. Ao invés de fazer isso, é possível fazer algo com o código entre as tags de bloco. Por exemplo, há uma template tag personalizada, ``{% upper %}``, que capitaliza tudo entre ela mesma e ``{% endupper %}``. Uso: .. code-block:: html+django {% upper %}Isso irá aparecer em maiúsuclas, {{ your_name }}.{% endupper %} Como no exemplo anterior, nós usaremos ``parser.parse()``. Mas dessa vez, nós passamos o resultado ``nodelist`` para o ``Node``:: def do_upper(parser, token): nodelist = parser.parse(('endupper',)) parser.delete_first_token() return UpperNode(nodelist) class UpperNode(template.Node): def __init__(self, nodelist): self.nodelist = nodelist def render(self, context): output = self.nodelist.render(context) return output.upper() O único conceito novo aqui é o ``self.nodelist.render(context)`` no ``UpperNode.render()``. Para mais exemplos de complexidade de renderização, vej o código fonte ``{% if %}``, ``{% for %}``, ``{% ifequal %}`` e ``{% ifchanged %}``. Eles estão em ``django/template/defaulttags.py``.