Django 템플릿 언어

이 문서에 대하여

이 문서는 Django 템플릿 시스템의 언어 구문을 설명합니다. 만약에 보다 기술적인 관점에서 작동원리와 확장 방법을 알고 싶다면, The Django template language: For Python programmers를 살펴보기 바랍니다.

Django의 템플릿 언어는 강력함과 용이성 사이의 균형을 잡고자 설계되었습니다. HTML 작업을 하는 동안 편안함을 느낄 수 있도록 합니다. SmartyCheetahTemplate등의 텍스트 기반 템플릿 언어를 접해본 적이 있다면, Django의 템플릿이 편안하게 느껴질 것입니다.

철학

프로그래밍에 대한 배경지식이 있거나, PHP와 같이 프로그램 코드를 HTML과 직접 뒤섞는 언어를 사용해왔다면, Django 템플릿 시스템은 단순히 HTML에 파이썬을 심어놓은 것이 아니라는 점을 유념하시기 바랍니다. 템플릿 시스템은 프로그램 로직이 아닌, 프리젠테이션을 표현하기 위하여 설계된 것이기 때문입니다.

Django 템플릿 시스템은 프로그래밍 구조와 유사한 태그를 제공합니다. – if 태그는 부울 테스트에 사용되며, for 태그는 루핑에 사용됩니다. – 그러나 이런 것들은 대응되는 파이썬 코드로서 수행되는 것이 아니며, 템플릿 시스템은 임의의 파이썬 표현을 실행시키지 않습니다. 아래에 나열한 태그, 필터 및 구문만 기본적으로 지원됩니다(단, 필요에 따라 스스로 맞춤 템플릿 태그를 추가할 수도 있습니다).

템플릿

템플릿은 단순한 텍스트 파일입니다. 템플릿으로 어떠한 텍스트 기반 형식이든 만들어낼 수 있습니다(HTML, XML, CSV 등).

템플릿에 있는 변수는 템플릿이 평가될 때 값으로 대치되며, 태그는 템플릿의 로직을 제어합니다.

다음은 몇 가지 기초를 설명하기 위한 간단한 템플릿입니다. 각 요소에 대해서는 이 문서에서 설명할 것입니다.

{% extends "base_generic.html" %}

{% block title %}{{ section.title }}{% endblock %}

{% block content %}
<h1>{{ section.title }}</h1>

{% for story in story_list %}
<h2>
  <a href="{{ story.get_absolute_url }}">
    {{ story.headline|upper }}
  </a>
</h2>
<p>{{ story.tease|truncatewords:"100" }}</p>
{% endfor %}
{% endblock %}

철학

왜 (Zope의 TAL과 같은) XML 기반의 템플릿이 아닌 텍스트 기반의 템플릿을 사용할까요? 우리는 Django의 템플릿 언어가 XML/HTML 템플릿보다 더욱 usable하기를 원했습니다. 월드 온라인에서 우리는 이메일, 자바스크립트, CSV를 사용하였습니다. 어떠한 텍스트 기반의 형식을 위해서든지 템플릿 언어를 사용할 수 있습니다.

아, 한 가지 더. 인간으로 하여금 XML을 작성하도록 하는 것은 가혹해요!

변수

변수는 {{ 변수 }}와 같이 생겼습니다. 템플릿 엔진이 변수를 만나면, 평가하여 그 결과로 치환합니다. 변수명은 영문자와 밑줄("_")로 구성됩니다. 점(".")에는 특별한 의미가 있습니다. 중요한 점으로, 변수명에는 공백이나 특수문자를 사용할 수 없습니다.

점(.)은 변수의 속성에 접근할 때에 사용합니다.

배후에서 일어나는 일

기술적으로, 템플릿 시스템이 점을 만나면, 다음과 같은 조회를 차례대로 시도합니다.

  • 사전 조회
  • 속성 조회
  • 메소드 호출
  • 리스트-인덱스 조회

위의 예제에서, {{ section.title }}section 개체의 title 속성으로 치환됩니다.

변수가 존재하지 않을 경우, 템플릿 시스템은 TEMPLATE_STRING_IF_INVALID 설정값을 삽입하는데, 그 기본값은 ''(공백 문자)입니다.

필터

필터를 사용함으로써 변수의 표시에 변화를 줄 수 있습니다.

필터는 {{ name|lower }}와 같이 생겼습니다. 이것은 {{ name }} 변수에 lower 필터를 적용시켜서, 텍스트를 소문자로 변경시킨 결과를 표출합니다. 파이프(|)를 사용하여 필터를 적용합니다.

필터는 “연쇄적으로” 사용하여, 필터의 결과가 그 다음 필터에 적용되도록 할 수 있습니다. {{ text|escape|linebreaks }}는 텍스트 컨텐츠를 이스케이프한 다음, 행 바꿈을 <p> 태그로 바꾸기 위해 종종 사용되곤 합니다.

어떤 필터는 인자를 취합니다. 필터 인자는 {{bio|truncatewords:30 }}과 같이 사용합니다. 이것은 bio 변수의 처음 30 단어를 보여줍니다.

필터 인자에 공백이 포함된 경우에는 반드시 따옴표로 둘러싸야 합니다. 예를 들어 목록을 쉼표와 공백으로 join하는 경우 {{ list|join:", " }}과 같이 사용합니다.

Django는 서른 개 정도의 내장 템플릿 필터를 제공합니다. 내장 필터 레퍼런스를 참고하기 바랍니다. 어떤 것을 사용할 수 있는지 맛보기를 위해, 자주 사용되는 템플릿 필터들을 소개합니다.

default

변수가 false 또는 비어 있는 경우, 지정된 defalut를 사용합니다. 그렇지 않으면 변수의 값을 사용합니다.

예:

{{ value|default:"nothing" }}

value가 제공되지 않았거나 비어 있는 경우, 위에서는 “nothing“을 출력합니다.

length

값의 길이를 반환합니다. 문자열과 목록에 대하여 사용할 수 있습니다. 예:

{{ value|length }}

value['a', 'b', 'c', 'd']라면, 결과는 4가 됩니다.

striptags

S모든 [X]HTML 태그를 제거합니다. 예:

{{ value|striptags }}

value"<b>Joel</b> <button>is</button> a <span>slug</span>"인 경우, 결과는 "Joel is a slug"가 됩니다.

일부의 예를 들었을 뿐이므로, 내장 필터 레퍼런스에서 전체 목록을 보시기 바랍니다.

템플릿 필터를 직접 만들 수도 있습니다. Custom template tags and filters를 읽어보세요.

See also

Django의 관리자 인터페이스는 해당 사이트에서 사용 가능한 모든 템플릿 태그와 필터의 전체 레퍼런스를 포함할 수 있습니다. The Django admin documentation generator를 참고하세요.

태그

태그는 {% tag %}와 같이 생겼습니다. 태그는 변수에 비해 더 복잡합니다. 어떤 것은 텍스트를 생성하고, 어떤 것은 루프 또는 로직을 수행하며 외부적인 정보를 나중에 변수에 의해 사용될 템플릿에 적재합니다.

어떤 태그는 시작과 끝 태그를 필요로 합니다(예: {% tag %} ... 태그 내용 ... {% endtag %}).

Django에는 스무 개가 넘는 템플릿 태그가 내장되어 있습니다. 내장 태그 레퍼런스에서 읽어볼 수 있습니다. 어떤 것이 가능한지 맛보기를 위해, 자주 사용되는 태그의 예를 들어보겠습니다.

for
배열의 각 원소에 대하여 루프. 예를 들어, athlete_list에 들어 있는 선수의 목록을 출력하기 위하여 다음과 같이 할 수 있습니다.
<ul>
{% for athlete in athlete_list %}
    <li>{{ athlete.name }}</li>
{% endfor %}
</ul>
if and else

변수를 평가하여, 변수가 “true”이면 블록의 컨텐츠를 표시:

{% if athlete_list %}
    Number of athletes: {{ athlete_list|length }}
{% else %}
    No athletes.
{% endif %}

위에서는, athlete_list가 비어있지 않다면, {{ athlete_list|length }} 변수에 의하여 선수의 숫자가 출력됩니다.

또한 if 태그 내에 필터 및 각종 연산자를 사용할 수 있습니다.

{% if athlete_list|length > 1 %}
   Team: {% for athlete in athlete_list %} ... {% endfor %}
{% else %}
   Athlete: {{ athlete_list.0.name }}
{% endif %}
blockextends
템플릿 상속 (아래 참조), 템플릿의 “보일러플레이트”를 줄여주는 강력한 방법입니다.

위에서 소개한 것은 일부에 불과하므로, 내장 태그 레퍼런스에서 전체 목록을 살펴보시기 바랍니다.

또한 맞춤 템플릿 태그를 만들 수도 있습니다. Custom template tags and filters를 참고하세요.

See also

Django의 관리자 인터페이스는 해당 사이트에서 사용 가능한 모든 템플릿 태그와 필터의 전체 레퍼런스를 포함할 수 있습니다. The Django admin documentation generator를 참고하세요.

주석

템플릿에서 한 행을 주석 처리하기 위한 주석 구문은 {# #}입니다.

예를 들어, 이 템플릿은 'hello'로 렌더됩니다.

{# greeting #}hello

주석은 다음과 같이 템플릿 코드가 유효하든 아니든 간에 포함시킬 수 있습니다.

{# {% if foo %}bar{% else %} #}

이 구문은 단일 행 주석에만 사용할 수 있습니다({##} 분리자 사이에 개행은 허용되지 않습니다). 템플릿에서 여러 줄을 주석으로 처리하고자 한다면, comment 태그를 살펴보세요.

템플릿 상속

Django의 템플릿 엔진에 있어서 가장 강력하고도 가장 복잡한 부분이 템플릿 상속입니다. 템플릿 상속을 통해 여러분의 사이트에서 공통적인 부분을 모두 갖고 있는 기초 “골격” 템플릿을 만들고 자식 템플릿이 덮어쓸 수 있는 **블록(block)**을 정의할 수 있도록 해줍니다.

예제를 통해 살펴보는 것이 이해하기 쉽습니다.

<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="stylesheet" href="style.css" />
    <title>{% block title %}My amazing site{% endblock %}</title>
</head>

<body>
    <div id="sidebar">
        {% block sidebar %}
        <ul>
            <li><a href="/">Home</a></li>
            <li><a href="/blog/">Blog</a></li>
        </ul>
        {% endblock %}
    </div>

    <div id="content">
        {% block content %}{% endblock %}
    </div>
</body>
</html>

우리가 ``base.html``이라고 부를 위의 템플릿은, 두 컬럼으로 된 페이지에 사용할 수 있는 간단한 HTML 골격 문서를 정의합니다. 비어있는 블록의 내용을 채우는 것은 “자식” 템플릿이 할 일입니다.

이 예제에서, block 태그는 자식 템플릿이 채울 수 있는 세 개의 블록을 정의합니다. block 태그가 하는 일은 템플릿 엔진에게 자식 템플릿이 그 부분을 덮어쓸 것이라고 알려주는 것입니다.

자식 템플릿은 다음과 같습니다.

{% extends "base.html" %}

{% block title %}My amazing blog{% endblock %}

{% block content %}
{% for entry in blog_entries %}
    <h2>{{ entry.title }}</h2>
    <p>{{ entry.body }}</p>
{% endfor %}
{% endblock %}

extends 태그가 핵심입니다. 템플릿 엔진에게 이 템플릿은 다른 템플릿을 “확장(extends)”한다고 알려줍니다. 템플릿 시스템이 이 템플릿을 평가할 때에는 부모를 – 이 경우, “base.html” –우선 찾습니다.

그때, 템플릿 엔진은 base.html에 있는 세 개의 block 태그를 알아차리고 자식 템플릿의 내용으로 치환합니다. blog_entries의 값에 따라서, 결과는 다음과 같이 보일 것입니다.

<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="stylesheet" href="style.css" />
    <title>My amazing blog</title>
</head>

<body>
    <div id="sidebar">
        <ul>
            <li><a href="/">Home</a></li>
            <li><a href="/blog/">Blog</a></li>
        </ul>
    </div>

    <div id="content">
        <h2>Entry one</h2>
        <p>This is my first entry.</p>

        <h2>Entry two</h2>
        <p>This is my second entry.</p>
    </div>
</body>
</html>

자식 템플릿은 sidebar 블록을 정의하지 않으므로, 부모 템플릿으로부터 온 값을 사용함에 유의하십시오. 부모 템플릿의 {% block %} 태그 내의 내용이 예비되는 것입니다.

원하는 만큼 여러 단계의 상속이 가능합니다. 일반적으로는 다음과 같은 세 단계의 접근방법을 취합니다.

  • 여러분의 사이트에서 주된 룩앤필을 갖는 base.html 템플릿을 작성합니다.
  • 여러분의 사이트에 각 “섹션”을 위한 base_섹션이름.html 템플릿을, base_news.html, base_sports.html와 같이 작성합니다. 이러한 템플릿은 base.html을 확장하면서 섹션에 맞는 스타일 및 디자인을 포함합니다.
  • 뉴스 기사나 블로그 엔트리와 같이, 각 페이지 유형에 맞는 템플릿을 작성합니다. 이러한 템플릿들은 적합한 섹션 템플릿을 확장합니다.

이러한 접근 방법을 통하여 코드의 재사용을 극대화하며, 섹션 내의 네비게이션과 같이 공유된 컨텐츠 부분에 항목을 쉽게 추가할 수 있습니다.

상속을 잘 활용할 수 있는 몇 가지 팁이 있습니다.

  • 템플릿에서 {% extends %}를 사용한다면, 그 템플릿에서 첫 템플릿 태그여야 합니다. 그렇지 않으면, 템플릿 상속이 작동하지 않을 것입니다.
  • 기본 템플릿에서 {% block %} 태그를 많이 쓸 수록 좋습니다. 자식 템플릿에서는 부모 블록을 모두 정의할 필요가 없으므로, 여러 블록에 납득할 만한 기본값을 채울 수 있으며, 나중에 필요한 것들만 정의할 수 있습니다.훅을 적게 갖고 있는 것보다는 많이 갖고 있는 것이 낫습니다.
  • 여러 템플릿에 중복되는 내용을 발견하였다면, 이는 그러한 내용을 부모 템플릿의 {% block %}으로 이동하여야 함을 뜻하는 것입니다.
  • 부모 템플릿으로부터 내용을 얻을 필요가 있다면, {{ block.super }} 변수를 사용하는 방법이 있습니다. 부모 블록의 내용을 완전히 덮어쓰지 않고 일부만 추가하고자 할 때 유용합니다. {{ block.super }}를 사용하여 삽입한 데이터는 자동으로 이스케이프되지 않는데(다음 섹션 참조), 필요하다면 부모 템플릿에서 이미 이스케이프되어 있을 것이기 때문입니다.
  • 가독성을 높이기 위해, 아래와 같이 {% endblock %} 태그에 *이름*을 붙일 수 있습니다.
    {% block content %}
    ...
    {% endblock content %}

큰 템플릿에서는, 이렇게 해두면 어느 ``{% block %}`` 태그가 닫혔는지 찾기가 수월합니다.

끝으로, 동일한 템플릿으로 같은 이름으로 여러 개의 block 태그를 정의할 수 없습니다. 이러한 제한이 있는 이유는 블록 태그가 “양” 방향으로 작동하기 때문입니다. 이는, 블록 태그가 단순히 채움 구멍만을 제공하는 데에 그치는 것이 아니라, 부모에서 구멍을 채울 내용도 정의하기 때문입니다. 템플릿에 비슷한 이름의 block 태그가 있다면, 부모는 어느 블록의 내용을 사용해야 할 지 알 수 없을 것입니다.

Automatic HTML escaping

When generating HTML from templates, there’s always a risk that a variable will include characters that affect the resulting HTML. For example, consider this template fragment:

Hello, {{ name }}.

At first, this seems like a harmless way to display a user’s name, but consider what would happen if the user entered his name as this:

<script>alert('hello')</script>

With this name value, the template would be rendered as:

Hello, <script>alert('hello')</script>

...which means the browser would pop-up a JavaScript alert box!

Similarly, what if the name contained a '<' symbol, like this?

<b>username

That would result in a rendered template like this:

Hello, <b>username

...which, in turn, would result in the remainder of the Web page being bolded!

Clearly, user-submitted data shouldn’t be trusted blindly and inserted directly into your Web pages, because a malicious user could use this kind of hole to do potentially bad things. This type of security exploit is called a Cross Site Scripting (XSS) attack.

To avoid this problem, you have two options:

  • One, you can make sure to run each untrusted variable through the escape filter (documented below), which converts potentially harmful HTML characters to unharmful ones. This was the default solution in Django for its first few years, but the problem is that it puts the onus on you, the developer / template author, to ensure you’re escaping everything. It’s easy to forget to escape data.
  • Two, you can take advantage of Django’s automatic HTML escaping. The remainder of this section describes how auto-escaping works.

By default in Django, every template automatically escapes the output of every variable tag. Specifically, these five characters are escaped:

  • < is converted to &lt;
  • > is converted to &gt;
  • ' (single quote) is converted to &#39;
  • " (double quote) is converted to &quot;
  • & is converted to &amp;

Again, we stress that this behavior is on by default. If you’re using Django’s template system, you’re protected.

How to turn it off

If you don’t want data to be auto-escaped, on a per-site, per-template level or per-variable level, you can turn it off in several ways.

Why would you want to turn it off? Because sometimes, template variables contain data that you intend to be rendered as raw HTML, in which case you don’t want their contents to be escaped. For example, you might store a blob of HTML in your database and want to embed that directly into your template. Or, you might be using Django’s template system to produce text that is not HTML – like an email message, for instance.

For individual variables

To disable auto-escaping for an individual variable, use the safe filter:

This will be escaped: {{ data }}
This will not be escaped: {{ data|safe }}

Think of safe as shorthand for safe from further escaping or can be safely interpreted as HTML. In this example, if data contains '<b>', the output will be:

This will be escaped: &lt;b&gt;
This will not be escaped: <b>

For template blocks

To control auto-escaping for a template, wrap the template (or just a particular section of the template) in the autoescape tag, like so:

{% autoescape off %}
    Hello {{ name }}
{% endautoescape %}

The autoescape tag takes either on or off as its argument. At times, you might want to force auto-escaping when it would otherwise be disabled. Here is an example template:

Auto-escaping is on by default. Hello {{ name }}

{% autoescape off %}
    This will not be auto-escaped: {{ data }}.

    Nor this: {{ other_data }}
    {% autoescape on %}
        Auto-escaping applies again: {{ name }}
    {% endautoescape %}
{% endautoescape %}

The auto-escaping tag passes its effect onto templates that extend the current one as well as templates included via the include tag, just like all block tags. For example:

# base.html

{% autoescape off %}
<h1>{% block title %}{% endblock %}</h1>
{% block content %}
{% endblock %}
{% endautoescape %}


# child.html

{% extends "base.html" %}
{% block title %}This & that{% endblock %}
{% block content %}{{ greeting }}{% endblock %}

Because auto-escaping is turned off in the base template, it will also be turned off in the child template, resulting in the following rendered HTML when the greeting variable contains the string <b>Hello!</b>:

<h1>This & that</h1>
<b>Hello!</b>

Notes

Generally, template authors don’t need to worry about auto-escaping very much. Developers on the Python side (people writing views and custom filters) need to think about the cases in which data shouldn’t be escaped, and mark data appropriately, so things Just Work in the template.

If you’re creating a template that might be used in situations where you’re not sure whether auto-escaping is enabled, then add an escape filter to any variable that needs escaping. When auto-escaping is on, there’s no danger of the escape filter double-escaping data – the escape filter does not affect auto-escaped variables.

String literals and automatic escaping

As we mentioned earlier, filter arguments can be strings:

{{ data|default:"This is a string literal." }}

All string literals are inserted without any automatic escaping into the template – they act as if they were all passed through the safe filter. The reasoning behind this is that the template author is in control of what goes into the string literal, so they can make sure the text is correctly escaped when the template is written.

This means you would write

{{ data|default:"3 &lt; 2" }}

...rather than

{{ data|default:"3 < 2" }}  <-- Bad! Don't do this.

This doesn’t affect what happens to data coming from the variable itself. The variable’s contents are still automatically escaped, if necessary, because they’re beyond the control of the template author.

Accessing method calls

Most method calls attached to objects are also available from within templates. This means that templates have access to much more than just class attributes (like field names) and variables passed in from views. For example, the Django ORM provides the “entry_set” syntax for finding a collection of objects related on a foreign key. Therefore, given a model called “comment” with a foreign key relationship to a model called “task” you can loop through all comments attached to a given task like this:

{% for comment in task.comment_set.all %}
    {{ comment }}
{% endfor %}

Similarly, QuerySets provide a count() method to count the number of objects they contain. Therefore, you can obtain a count of all comments related to the current task with:

{{ task.comment_set.all.count }}

And of course you can easily access methods you’ve explicitly defined on your own models:

# In model
class Task(models.Model):
    def foo(self):
        return "bar"

# In template
{{ task.foo }}

Because Django intentionally limits the amount of logic processing available in the template language, it is not possible to pass arguments to method calls accessed from within templates. Data should be calculated in views, then passed to templates for display.

Custom tag and filter libraries

Certain applications provide custom tag and filter libraries. To access them in a template, use the load tag:

{% load comments %}

{% comment_form for blogs.entries entry.id with is_public yes %}

In the above, the load tag loads the comments tag library, which then makes the comment_form tag available for use. Consult the documentation area in your admin to find the list of custom libraries in your installation.

The load tag can take multiple library names, separated by spaces. Example:

{% load comments i18n %}

See Custom template tags and filters for information on writing your own custom template libraries.

Custom libraries and template inheritance

When you load a custom tag or filter library, the tags/filters are only made available to the current template – not any parent or child templates along the template-inheritance path.

For example, if a template foo.html has {% load comments %}, a child template (e.g., one that has {% extends "foo.html" %}) will not have access to the comments template tags and filters. The child template is responsible for its own {% load comments %}.

This is a feature for the sake of maintainability and sanity.