Este documento explica o sistema de template do Django a partir de uma perspectiva técnica – como ela funciona e como estendê-la. Se você só está procurando por referência sobre a sintaxe da linguagem, veja A linguagem de template do Django.
Se você estiver procurando usar o sistema de template do Django como parte de outra aplicação – i.e., sem o resto do framework – esteja certo de ler a seção configuração mais adiante neste documento.
Um template é um documento de texto, ou uma string do Python normal, que é marcado usando a linguagem de template do Django. Um template pode conter tags de bloco ou variáveis.
Uma tag de bloco é um símbolo dentro de um template que faz algo.
Esta definição deliberadamente vaga. Por exemplo, uma tag de bloco pode gerar conteúdo, serve como um controle de strutura (uma declaração “if” ou laço “for”), apanha conteúdo do banco de dados ou habilita acesso para outras tags de template.
Tags de bloco são envolvidas por "{%}" e "%}".
Exemplo de template exemplo com tags de bloco:
{% if is_logged_in %}Thanks for logging in!{% else %}Please log in.{% endif %}
Uma variável é um símbolo dentro de um template que gera um valor.
Tags de variáveis são envolvidas por "{{" e "}}".
Exemplo de template com variáveis:
My first name is {{ first_name }}. My last name is {{ last_name }}.
A context is a "variable name" -> "variable value" mapping that is passed to a template.
Um template renderiza um contexto substituindo a variável "holes" com os valores do contexto e executando todas tags de bloco.
Usar o sistema de templates no Python é um processo de dois passos:
A forma mais fácil de criar um objeto de Template é instanciando-o diretamente. A classe fica em django.template.Template. O construtor recebe um argumento -- o código do template puro:
>>> from django.template import Template
>>> t = Template("My name is {{ my_name }}.")
>>> print t
<django.template.Template instance>
Por trás das cenas
O sistema parseia somente o código do template puro -- quando você cria o objeto Template. A partir de entnao, é armazenado internamente como um "nodo" da estrutura por questões de performance.
Mesmo o parseamento em si é bem rápido. A maioria do parseamento acontece através de uma única chamada para uma única, e curta, expressão regular.
Uma vez que você tenha compilado o objeto Template, você pode renderizar um contexto -- ou vários contextos -- como ele. A classe Context fica em django.template.Context, e o seu construtor recebe dois argumentos (opcionais):
- Um dicionário que mapeia nomes de variáveis para valores de variáveis.
- The name of the current application. This application name is used to help resolve namespaced URLs. If you're not using namespaced URLs, you can ignore this argument.
Chame o método render() dos objetos Template com o contexto para "preencher" o template:
>>> from django.template import Context, Template
>>> t = Template("My name is {{ my_name }}.")
>>> c = Context({"my_name": "Adrian"})
>>> t.render(c)
"My name is Adrian."
>>> c = Context({"my_name": "Dolores"})
>>> t.render(c)
"My name is Dolores."
Nomes de variáveis devem consistir de uma letra (A-Z), qualquer dígito (0-9), e um sublinhado ou ponto.
Pontos possuem um significado especial na renderização do template. Um ponto no nome da variável significa acesso. Especificamente, quando o sistema de template enconra um ponto no nome de uma variável, ele tenta seguir a seguinte pesquisa, nesta ordem:
O sistema de template usa o primeiro tipo de acesso que funcionar. É um lógica de curto-circuito. Aqui tem alguns exemplos:
>>> from django.template import Context, Template
>>> t = Template("My name is {{ person.first_name }}.")
>>> d = {"person": {"first_name": "Joe", "last_name": "Johnson"}}
>>> t.render(Context(d))
"My name is Joe."
>>> class PersonClass: pass
>>> p = PersonClass()
>>> p.first_name = "Ron"
>>> p.last_name = "Nasty"
>>> t.render(Context({"person": p}))
"My name is Ron."
>>> t = Template("The first stooge in the list is {{ stooges.0 }}.")
>>> c = Context({"stooges": ["Larry", "Curly", "Moe"]})
>>> t.render(c)
"The first stooge in the list is Larry."
Acesso a métodos são ligeiramente mais complexos em relação aos outros tipos de acessos. Exemplo:
>>> class PersonClass2:
... def first_name(self):
... return "Samantha"
>>> p = PersonClass2()
>>> t.render(Context({"person": p}))
"My name is Samantha."
Callable variables are slightly more complex than variables which only require straight lookups. Here are some things to keep in mind:
Se durante o acesso ao método, um método lançar uma exceção, a exceção será propagada, a menos que a exceção tenha um atributo silent_variable_failure cujo valor seja True. Se a exceção tem um atributo silent_variable_failure, a variável será renderizada como uma string vazia. Exemplo:
>>> t = Template("My name is {{ person.first_name }}.")
>>> class PersonClass3:
... def first_name(self):
... raise AssertionError, "foo"
>>> p = PersonClass3()
>>> t.render(Context({"person": p}))
Traceback (most recent call last):
...
AssertionError: foo
>>> class SilentAssertionError(Exception):
... silent_variable_failure = True
>>> class PersonClass4:
... def first_name(self):
... raise SilentAssertionError
>>> p = PersonClass4()
>>> t.render(Context({"person": p}))
"My name is ."
Perceba que django.core.exceptions.ObjectDoesNotExist, que é a classe base para exceção DoesNotExist de toda API de banco de dados do Django, possui silent_variable_failure = True. Então se você estiver usando templates do Django com objetos de model, qualquer exceção DoesNotExist falhará silenciosamente.
Uma chamada de método somente funcionará se o método não tiver argumentos obrigatórios. Otherwise, the system will return an empty string.
Obviamente, alguns métodos tem um efeito colateral, e seria tolo ou representaria uma falha de segurança permitir que o sistema de template acesse-os.
Um bom exemplo é o método delete() em cada objeto de model do Django possui. Ao sistema de template não e permitido fazer algo deste tipo:
Eu irei agora deletar esse dado valioso. {{ data.delete }}
Para previnir isso, configure um atributo de função alters_data no método. O sistema de template não executará um método se o mesmo tem o alters_data=True configurado. Os métodos dinamicamente gerados delete() e save() nos objetos de model do Django obtêm alters_data=True automaticamente. Exemplo:
def sensitive_function(self):
self.database_record.delete()
sensitive_function.alters_data = True
Geralmente, se uma variável não existe, o sistema de template insere o valor da configuração TEMPLATE_STRING_IF_INVALID, que é configurado como '' (uma string vazia) por padrão.
Filtros que são aplicado numa variável inválida somente serão aplicados se TEMPLATE_STRING_IF_INVALID for configurado como '' (uma string vazia). Se TEMPLATE_STRING_IF_INVALID for configurado com qualquer outro valor, a variável filtro serão ignorados.
Este comportamento é ligeiramente diferente das tags de template if, for e regroup. Se uma variável inválida é fornecida para uma dessas tags de template, a variável será interpretada como None. Os filtros são sempre aplicados em variáveis inválidas dentro destas tags de template.
Se TEMPLATE_STRING_IF_INVALID contém um '%s', o formato do marcador será substituido com o nome da variável inválida.
Somente para fins de depuração!
Enquanto TEMPLATE_STRING_IF_INVALID pode ser uma ferramenta de depuração útil, também pode ser uma péssima idéia ligá-la como um 'padrão de desenvolvimento'.
Muitos templates, incluíndo os do site Admin, confiam no silêncio do sistema de template quando uma variável não existene é encontrada. Se você atribui um valor diferente de '' para TEMPLATE_STRING_IF_INVALID, você experimentará problemas de renderização com estes templates e sites.
Geralmente, TEMPLATE_STRING_IF_INVALID somente pode ser habilitado para fins de depuraçnao num problema específico de um template, uma vez que esteja concluída a depuração ele deve ser limpo.
Na maior parte do tempo, você instanciará objetos Context passando num dicionário completamente populado ao Context(). Mas você pode adicionar e deletar ítens de um objeto Context uma vez que o tenha instanciado, também, usando a sintaxe padrão do dicionário:
>>> c = Context({"foo": "bar"})
>>> c['foo']
'bar'
>>> del c['foo']
>>> c['foo']
''
>>> c['newvariable'] = 'hello'
>>> c['newvariable']
'hello'
Um objeto Context é uma pilha. Isto é, você pode executar push() e pop(). Se você executar o pop() insistentemente, ele lançará um django.template.ContextPopException:
>>> c = Context()
>>> c['foo'] = 'first level'
>>> c.push()
>>> c['foo'] = 'second level'
>>> c['foo']
'second level'
>>> c.pop()
>>> c['foo']
'first level'
>>> c['foo'] = 'overwritten'
>>> c['foo']
'overwritten'
>>> c.pop()
Traceback (most recent call last):
...
django.template.ContextPopException
In addition to push() and pop(), the Context object also defines an update() method. This works like push() but takes a dictionary as an argument and pushes that dictionary onto the stack instead of an empty one.
>>> c = Context()
>>> c['foo'] = 'first level'
>>> c.update({'foo': 'updated'})
{'foo': 'updated'}
>>> c['foo']
'updated'
>>> c.pop()
{'foo': 'updated'}
>>> c['foo']
'first level'
Usando um Context como uma pilha vem a calhar em algumas tags de template, como você verá abaixo.
O Django vem com uma classe Context especial django.template.RequestContext, que age ligeiramente diferente da classe normal django.template.Context. A primeira difereça é que ele recebe um HttpRequest como o seu primeiro argumento. Por exemplo:
c = RequestContext(request, {
'foo': 'bar',
})
A segunda diferença é que ele automaticamente popula o contexto com umas poucas variáveis, de acordo com a sua configuração TEMPLATE_CONTEXT_PROCESSORS.
A configuração TEMPLATE_CONTEXT_PROCESSORS é uma tupla de chamáveis -- chamado processador de contexto -- que recebe um objeto de requisição como seu argumento e retorna um dicionário para ser mesclado dentro do contexto. Por padrão, TEMPLATE_CONTEXT_PROCESSORS é configurado como:
("django.contrib.auth.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media",
"django.core.context_processors.static",
"django.contrib.messages.context_processors.messages")
Cada processador é aplicado na ordem. Isso significa que um processador que adiciona uma variável ao contexto e um segundo processador adiciona uma variável com o mesmo nome, a segunda sobrescreverá o primeiro. O processador padrão será explicado abaixo.
When context processors are applied
When you use RequestContext, the variables you supply directly are added first, followed any variables supplied by context processors. This means that a context processor may overwrite a variable you've supplied, so take care to avoid variable names which overlap with those supplied by your context processors.
Também, você pode fornecer ao RequestContext uma lista de processadores adicionais, usando o terceiro argumento (opcional), processors. Neste exemplo, a instância RequestContext recebe uma variável ip_address:
def ip_address_processor(request):
return {'ip_address': request.META['REMOTE_ADDR']}
def some_view(request):
# ...
c = RequestContext(request, {
'foo': 'bar',
}, [ip_address_processor])
return HttpResponse(t.render(c))
Note
Se você estiver usando o atalho render_to_response() do Django para popular um template com os conteúdos de um dicionário, ao seu template será passado uma instância de Context por padrão (não um RequestContext). Para usar um RequestContext na renderização do seu template, passe o terceiro parâmetro opcional para o render_to_response(): uma instância do RequestContext. Seu código pode parecer com isso:
def some_view(request):
# ...
return render_to_response('my_template.html',
my_data_dictionary,
context_instance=RequestContext(request))
Aqui temos o que cada processador padrão faz:
Se TEMPLATE_CONTEXT_PROCESSORS conter seu processador, todo RequestContext conterá estas três variáveis:
Se TEMPLATE_CONTEXT_PROCESSORS contém este processador, cada RequestContext conterá estas duas variáveis -- mas somente se sua configuração DEBUG configurada como True e o endereço IP da requisição (request.META['REMOTE_ADDR']) estiver na configuração INTERNAL_IPS:
Se TEMPLATE_CONTEXT_PROCESSORS contém este processador, todo RequestContext conterá estas duas variáveis:
Veja Internationalization and localization para mais.
Se TEMPLATE_CONTEXT_PROCESSORS contém este processador, cada RequestContext conterá uma variável MEDIA_URL, fornecendo o valor da configuração MEDIA_URL.
If TEMPLATE_CONTEXT_PROCESSORS contains this processor, every RequestContext will contain a variable STATIC_URL, providing the value of the STATIC_URL setting.
This processor adds a token that is needed by the csrf_token template tag for protection against Cross Site Request Forgeries.
Se TEMPLATE_CONTEXT_PROCESSORS contém este processador, cada RequestContext conterá uma variável request, que é o atual HttpRequest. Note que este processaodr não está habilitado por padrão; você precisa ativá-lo.
If TEMPLATE_CONTEXT_PROCESSORS contains this processor, every RequestContext will contain a single additional variable:
Um processador de contexto tem uma interface muito simples: É somente uma função Python que recebe um argumento, um objeto HttpRequest, e retorna um dicionário que será adicionado ao contexto do template´. Cada processador de contexto deve retornar um dicionário.
Processadores de contexto podem ficar em qualquer parte de sua base de código. Tudo com o que o Django se preocupa é que seu processador de contexto seja apontado pela a sua configuração TEMPLATE_CONTEXT_PROCESSORS.
Geralmente, você armazenará templates em arquivos no seu sistema de arquivos ao invés de usar a API de Template de baixo nível em si. Guarde os templates num diretório especificado com um diretório de template.
O Django procura por diretórios de templates em alguns lugares, dependendo da sua configuração do carregador de template (veja "Tipos de carregadores" abaixo), mas a forma mais básica de especificar diretórios de templates é usando a configuração TEMPLATE_DIRS.
Diz ao Django aonde seus diretórios de templates estão usando a configuração TEMPLATE_DIRS no seu arquivo de configuração. Este deve ser configurado como uma lista ou tupla de strings que contenham caminhos completos dos seus templates. Exemplo:
TEMPLATE_DIRS = (
"/home/html/templates/lawrence.com",
"/home/html/templates/default",
)
Seus template podem fica em qualquer lugar, desde que seus diretórios de templates possam ser lidos pelo servidor Web. Eles podem ter qualquer extensão que você quiser, tais como .html ou .txt, ou eles podem nem mesmo ter qualquer extensão.
Perceba que estes caminhos devem ser no estilo Unix com barras, mesmo no Windows.
O Django tem duas formas de carregar templates dos arquivos:
get_template retorna o template compilado (um objeto Template) para o template com o dado nome. se o template não existir, ele lançará um django.template.TemplateDoesNotExist.
select_template assim como get_template, exceto por receber uma lista de nomes de template. Com base na lista, ele retorna o primeiro template que existir.
Por exemplo, se você chama get_template('story_detail.html') e tem a configuração acima, o Django procurará por estes arquivo, nesta ordem:
Se você chamar select_template(['story_253_detail.html', 'story_detail.html']), aqui tá pelo que o Django irá procurar:
Quando o Django encontra um template que existe, ele para de procurar.
Dica
Você pode usar select_template() para tornar os templates super flexíveis. Por exemplo, se você estiver escrevendo um sistema de notícias e quiser que algumas tenham um template personalizado, use algo desse tipo select_template(['story_%s_detail.html' % story.id, 'story_detail.html']). Isso vai permitir que você use um modelo personalizado para uma notícia individual, com um template de reserva para notícias que não tenham templates personalizados.
É possível -- e preferível -- organizar os templates em sub diretórios dentro do diretório de template. A convenção é fazar um sub diretório para cada aplicação Django, com sub diretórios dentro deles se necessário.
Faça isso para sua própria sanidade. Armaezenar todos os templates no mesmo nível de diretório vira uma bagunça.
Para carregar um template que esteja dentro de um sub diretório, é só usar uma barra, desta forma:
get_template('news/story_detail.html')
Usando a mesma configuração acima TEMPLATE_DIRS, este exemplo de chamada para get_template() tentará carregar os seguintes templates:
Por padrão, o Django usa um carregador de template baseado no sistema de arquivos, mas o Django vem com alguns outros tipos de carregador de templates, que sabem como carregar templates de outras fontes.
Alguns desses outros carregadores são desabilitador por padrão, mas você pode ativá-los editando sua configuração TEMPLATE_LOADERS. TEMPLATE_LOADERS devem ser uma tupla de strings, onde cada string representa um carregador de template. Aqui temos os carregadors de template que acompanham o Django:
Carrega os template das aplicações Django no sistema de arquivos. Para cada applicação no INSTALLED_APPS, o carregador procura pelo diretório tempaltes. Se o diretório existe, o Django procura pelos templates lá dentro.
Isso significa que você pode armazenar templates dentro de suas aplicações individualmente. Isso também torna fácil distribuir suas aplicações Django com templates padrão.
Por exemplo, para esta configuração:
INSTALLED_APPS = ('myproject.polls', 'myproject.music')
...então get_template('foo.html') procurará pelos templates nestes diretórios, nesta ordem:
Percebe-se que o carregador realiza uma otimização quando é importada pela primeira vez: Ele cachea a lista de pacotes INSTALLED_APPS que possuem um sub diretório templates.
Este carregador é habilitado por padrão.
Igual ao app_directories acima, mas ele procura por templates no Python eggs ao invés do sistema de arquivos.
Esse carregador é desabilitado por padrão.
By default, the templating system will read and compile your templates every time they need to be rendered. While the Django templating system is quite fast, the overhead from reading and compiling templates can add up.
The cached template loader is a class-based loader that you configure with a list of other loaders that it should wrap. The wrapped loaders are used to locate unknown templates when they are first encountered. The cached loader then stores the compiled Template in memory. The cached Template instance is returned for subsequent requests to load the same template.
For example, to enable template caching with the filesystem and app_directories template loaders you might use the following settings:
TEMPLATE_LOADERS = (
('django.template.loaders.cached.Loader', (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
)),
)
Note
All of the built-in Django template tags are safe to use with the cached loader, but if you're using custom template tags that come from third party packages, or that you wrote yourself, you should ensure that the Node implementation for each tag is thread-safe. For more information, see template tag thread safety considerations.
This loader is disabled by default.
O Django usa os carregadores de templates na ordem de acordo com a configuração TEMPLATE_LOADERS. Ele usa cada carregador até que o carregador encontre uma combinação.
Para reduzir a repetição naturla de carregar e renderizar templates, o Django fornece uma função de atalho que automatiza amplamente o processo: render_to_string() no django.template.loader, que carrega um template, o renderiza e retorna a string resultante:
from django.template.loader import render_to_string
rendered = render_to_string('my_template.html', { 'foo': 'bar' })
O atalho render_to_string recebe um argumento obrigatório -- template_name, que deve ser o nome do template a ser carregado e renderizado -- e dois argumentos opcionais:
Veja também o atalho render_to_response(), que chama render_to_string e alimenta o resultado dentro de um HttpResponse adequado para retornar diretamente a uma view.
Note
Esta seção somente é interessante para pessoas que estejam tentando usar o sistema de template como um componente de saída em outra aplicação. Se você estiver usando o sitema de template como parte de uma aplicação do Django, nada aqui se aplica a você.
Normalmente, o Django carregará todas as informações de configurações que ele precisa para seu próprio arquivo de configuração padrão, combinado com as configurações no módulo fornecido na variável de ambiente DJANGO_SETTINGS_MODULE. Mas se você estiver usando o sistema de template independentemente do resto do Django, a abordagem de variável de ambiente não é muito conveniente, pois você provavelmente deseja configurar o sistema de template alinhado com o resto de sua aplicação ao invês de lidar com arquivos de configuração e apontá-los via variáveis de ambiente.
Para resolver este problema, você precisa usar a opção de configuração manual descrita em Usando o settings sem a configuração DJANGO_SETTINGS_MODULE. Simplesmente importando as partes apropriadas do sistema de template e então, antes de você chamar qualquer função de templates, chame django.conf.settings.configure() com qualquer configuração que você deseja especificar. Você pode querer considerar configurar pelo menos TEMPLATE_DIRS (se você estiver usando carregadores de template), DEFAULT_CHARSET (embora o padrão seja utf-8 seja legal) e TEMPLATE_DEBUG. Toda variável de configuração está descrita no documentação de configurações, e qualquer configuração que comece com TEMPLATE_ é de interesse óbvio.
The Django Template and Loader classes implement a simple API for loading and rendering templates. By providing some simple wrapper classes that implement this API we can use third party template systems like Jinja2 or Cheetah. This allows us to use third-party template libraries without giving up useful Django features like the Django Context object and handy shortcuts like render_to_response().
The core component of the Django templating system is the Template class. This class has a very simple interface: it has a constructor that takes a single positional argument specifying the template string, and a render() method that takes a Context object and returns a string containing the rendered response.
Suppose we're using a template language that defines a Template object with a render() method that takes a dictionary rather than a Context object. We can write a simple wrapper that implements the Django Template interface:
import some_template_language
class Template(some_template_language.Template):
def render(self, context):
# flatten the Django Context into a single dictionary.
context_dict = {}
for d in context.dicts:
context_dict.update(d)
return super(Template, self).render(context_dict)
That's all that's required to make our fictional Template class compatible with the Django loading and rendering system!
The next step is to write a Loader class that returns instances of our custom template class instead of the default Template. Custom Loader classes should inherit from django.template.loader.BaseLoader and override the load_template_source() method, which takes a template_name argument, loads the template from disk (or elsewhere), and returns a tuple: (template_string, template_origin).
The load_template() method of the Loader class retrieves the template string by calling load_template_source(), instantiates a Template from the template source, and returns a tuple: (template, template_origin). Since this is the method that actually instantiates the Template, we'll need to override it to use our custom template class instead. We can inherit from the builtin django.template.loaders.app_directories.Loader to take advantage of the load_template_source() method implemented there:
from django.template.loaders import app_directories
class Loader(app_directories.Loader):
is_usable = True
def load_template(self, template_name, template_dirs=None):
source, origin = self.load_template_source(template_name, template_dirs)
template = Template(source)
return template, origin
Finally, we need to modify our project settings, telling Django to use our custom loader. Now we can write all of our templates in our alternative template language while continuing to use the rest of the Django templating system.
Dec 26, 2011