一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

Django框架模板从基础入门到进阶

时间:2015-07-25 编辑:简简单单 来源:一聚教程网

Django框架模板基础


模板使用
模板基本由两个部分组成,一是HTML代码,二是逻辑控制代码。
逻辑控制的实现又基本由三个部分组成:
1. 变量的使用
{{ person_name }} #使用双大括号来引用变量
2. tag的使用
{% if ordered_warranty %} #使用大括号和百分号的组成来表示使用Django提供的
template tag
{% for item in item_list %}

  • {{ item }}


  • {% endfor %}


  • 3. filter的使用


  • {{ ship_date|date:"F j, Y" }},ship_date变量传给data过滤器,data过滤器通过使用


  • "F j, Y"这几个参数来格式化日期数据。"|"代表类似Unix命令中的管道操作。



  • Template system不仅仅可以和view进行合作,也可以自己独立使用。


  • 最基本的使用方法是:


  • 1. 使用模板代码字符串作为参数,创建一个Template类


  • 2. 创建模板代码中所需要的上下文Context对象,包含所需要的各个引用参数的值


  • 3. 调用Template.render()方法,把template渲染成一个完整的字符串。


  • >>> from django import template


  • >>> t = template.Template('My name is {{ name }}.')


  • >>> c = template.Context({'name': 'Adrian'})


  • >>> print t.render(c)


  • >>> My name is Adrian.



  • 还可以在template代码中使用dict索引,然后在context传入所需要的dict


  • >>> from django.template import Template, Context


  • >>> person = {'name': 'Sally', 'age': '43'}


  • >>> t = Template('{{ person.name }} is {{ person.age }} years old.')


  • >>> c = Context({'person': person})


  • >>> t.render(c)


  • u'Sally is 43 years old.'



  • 还可以使用函数,不过只能使用无参数的函数


  • >>> from django.template import Template, Context


  • >>> t = Template('{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}')


  • >>> t.render(Context({'var': 'hello'}))


  • u'hello -- HELLO -- False'



  • 还可以使用列表索引,但是item.-1是不被允许的


  • >>> from django.template import Template, Context


  • >>> t = Template('Item 2 is {{ items.2 }}.')


  • >>> c = Context({'items': ['apples', 'bananas', 'carrots']})


  • >>> t.render(c)


  • u'Item 2 is carrots.'



  • 以上的使用方法被称为Dot Lookup方法。



  • 使用dot lookup的访问函数功能时,需要注意的问题:


  • 1. 当在模板代码中执行的函数抛出异常时,会一直向上层传播,除非这个异常中有一个参数


  • silent_variable_failure=True; 这样的话,这个出错的函数信息就会被渲染成空字符串。


  • >>> class SilentAssertionError(AssertionError):


  • ... silent_variable_failure = True


  • >>> class PersonClass4:


  • ... def first_name(self):


  • ... raise SilentAssertionError


  • >>> p = PersonClass4()


  • >>> t.render(Context({"person": p}))


  • u'My name is .'



  • 2. 很明显,调用函数会产生一些不好的结果,安全漏洞之类的,如果你有一个BankAccout,


  • 然后在模板中写成{{ account.delete }}, 这样在渲染的时候,你的账号就被删除了。。。。


  • 所以要在修改一下你的delete函数


  • def delete(self):


  • # Delete the account


  • delete.alters_data = True#缩进没有问题,把delete看成一个对象,设置它的alters_data属性。


  • 这样在渲染的时候,就会变成failed silent。



  • 当在渲染的时候,简单的key值没有找到时,会failed silent,变成空字符串,而不是大动干戈的


  • 报错。


  • >>> from django.template import Template, Context


  • >>> t = Template('Your name is {{ name }}.')


  • >>> t.render(Context())


  • u'Your name is .'


  • >>> t.render(Context({'var': 'hello'}))


  • u'Your name is .'



  • Context对象也可以进行增删改值的操作。


  • >>> from django.template import Context


  • >>> c = Context({"foo": "bar"})


  • >>> c['foo']


  • 'bar'


  • >>> c['newvariable'] = 'hello'


  • >>> del c['foo']


  • >>> c['foo']



  • 使用python manage.py shell启动python交互式命令行窗口与一般直接启动python自带的


  • 交互式命令行窗口的区别是前者会通过找一个DJANGO_SETTINGS_MODULE环境变量,


  • 告诉Django导入settings.py的配置信息。



  • 基本的tag和filter的用法


  • tag:



  •     {% if %}的使用



  • 可以使用and, or, not来组织你的逻辑。但不允许and和or同时出现的条件语句中。


  • 没有{% elif %}这样的用法,只能用嵌套来实现多重if语句。


  • {% if athlete_list %}


  • Here are the athletes: {{ athlete_list }}.


  • {% else %}


  • No athletes are available.


  • {% if not coach_list %}


  • Here are the coaches: {{ coach_list }}.


  • {% endif %}


  • {% endif %}



  •     {% for %} 的使用



  • 用来循环一个list,还可以使用resersed关键字来进行倒序遍历,一般可以用if语句来先


  • 判断一下列表是否为空,再进行遍历;还可以使用empty关键字来进行为空时候的跳转。


  • {% for athlete in athlete_list resersed %}


  • {{ athlete.name }}


  • {% empty %}


  • There are no athletes. Only computer programmers.


  • {% endfor %}



  • for tag还提供了一些内置参数来提供模板循环的信息。


  • 1. forloop.counter 当前循环计数,从1开始


  • {% for item in todo_list %}


  • {{ forloop.counter }}: {{ item }}


  • {% endfor %}


  • 2. forloop.counter0 当前循环计数,从0开始,标准索引方式


  • 3. forloop.revcounter 当前循环的倒数计数,从列表长度开始


  • 4. forloop.revcounter0 当前循环的倒数计数,从列表长度减1开始,标准


  • 5. forloop.first bool值,判断是不是循环的第一个元素


  • 6. forloop.last 同上,判断是不是循环的最后一个元素


  • 7. forloop.parentloop 用在嵌套循环中,得到parent循环的引用,然后可以使用以上的参数


  • {% for country in countries %}



  • {% for city in country.city_list %}






  • {% endfor %}

  • Country #{{ forloop.parentloop.counter }}City #{{ forloop.counter }}{{ city }}

  • {% endfor %}



  •     ifequal和ifnotequal,一看就是直接比较值的tag,需要两个参数,用法比较有限,


  •     只限于字符串,整数,小数的比较,什么列表,字典,元组不支持。



  • {% ifequal user currentuser %}


  • Welcome!


  • {% ifequal section "community" %}


  • Community


  • {% endifequal %}


  • {% endifequal %}



  •     {# #},模板中注释的用法,只能用在一行


  •     如果要使用多行注释,要使用{% comment %}



  • {# This is a comment #}


  • {% comment %}


  • This is a


  • multi-line comment.


  • {% endcomment %}


  • filter:


  • filter用于变量在显示之前的一些简单的处理。使用类似管道的操作符"|",也可以进行链式操作


  • {{ name|lower }}


  • {{ my_list|first|upper }}


  • {{ bio|truncatewords:"30" }}


  • 介绍几个重要的filter:



  •     addslashes :给任何的反斜线,单引号,双引号,再加一个反斜线。在文本中含有

  • javascript
  • 字符串的时候有用。


  •     date :用来对data和datatime对象的字符串信息进行格式化。


  •     {{ pub_date|date:"F j, Y" }}


  •     length :返回变量的长度。



  • 在view中使用template:


  • 首先在settings.py中配置模板文件的路径。


  • TEMPLATE_DIRS = (


  • '/home/django/mysite/templates',


  • )


  • 记住一个路径的时候要使用逗号,这样是来区分是一个tuple还是一个block expression


  • 也可以在设置的时候使用python文件路径操作代码:


  • import os.path



  • TEMPLATE_DIRS = (


  • os.path.join(os.path.dirname(__file__), 'templates').replace('\\','/'),


  • )



  • 然后,可以在view中使用模板


  • from django.template.loader import get_template


  • from django.template import Context


  • from django.http import HttpResponse


  • import datetime



  • def current_datetime(

  • request
  • ):


  • now = datetime.datetime.now()


  • t = get_template('current_datetime.html')


  • html = t.render(Context({'current_date': now}))


  • return HttpResponse(html)



  • 大多数情况下,你会使用一种shortcut方法,render_to_response()去完成以上的工作。


  • from django.shortcuts import render_to_response


  • import datetime



  • def current_datetime(request):


  • now = datetime.datetime.now()


  • return render_to_response('current_datetime.html', {'current_date': now})



  • locals()的小技巧


  • 如果你有很多变量要传给render,一个一个构造dict元素很麻烦。直接把变量名改成模板中所需要的变量名,


  • 再使用locasl()函数,轻松搞定


  • def current_datetime(request):


  • current_date = datetime.datetime.now()


  • return render_to_response('current_datetime.html', locals())


  • locals()会返回局部空间中变量信息的dict,直接可以传给render,但有一点需要注意,它会返回把有的局部变量


  • 信息,有一些可能不需要用到,如request变量。



  • {%

  • include
  • %}的使用


  • {% include 'nav.html' %},用来引入其它模板的内容,减少重复的模板代码


  • {% include template_name %},还可以使用变量名


  • 如果include的模板文件没有找到,当DEBUG为真时,会报错TemplateDoesNotExist,当为假时,页面那一块为


  • 空白。



  • 诚然,include可以有效减少模板的重复代码。但一种更优雅的方式是:


  • template inheritance.


  • 首先,创建base.html






  • {% block title %}{% endblock %}


    My helpful timestamp site


  • {% block content %}{% endblock %}

  • {% block footer %}



  • Thanks for visiting my site.


  • {% endblock %}



  •  

  • 我们使用一个新的tag,{% block %}用来告诉template engine,这个部分会被子模板

  • 来实现。如果子模板没有实现这些部分,就会默认使用父模板的代码。

  •  

  • 再看看,子模板要怎么写:

  • {% extends "base.html" %}

  •  

  • {% block title %}The current time{% endblock %}

  •  

  • {% block content %}

  • It is now {{ current_date }}.


  • {% endblock %}

  • 只需要先使用{% extends %}继承父模板,再把相应需要实现的部分写上所需要的内容。

  •  

  • {% extends template_name %}也可以使用变量名来实现动态。

  • 模板继承的三层继承策略:

  • 1. 创建一个base.html,用来设置外观

  • 2. 为网站的每一个部分,创建base_SECTION.html,比如base_phote.html, base_forum.html

  • 3. 为每一个页面创建自己的模板。




  • Django框架模板高级进阶

    1. RequestContext函数和context处理器

  •   首先,我们回顾模板的视图函数如何书写:


  • from django.shortcuts import render_to_response


  • def diary(request):

  •     return render_to_response('diary.html', {'name': 'qiqi'})


  •   为了说明方便,我们同时给出另一种写法:


from django.http import HttpResponse
from django.template import loader, Context
def diary(request):
    t = loader.get_template('diary.html')
    c = Context({'name': 'qiqi'})
    return HttpResponse(t.render(c))


  或许还是有同学看不懂,那我就再给出第三种最笨的等价写法,大家大可略过直接往后看:

from django.http import HttpResponse
from django.template import Template, Context
def diary(request):
    tin = open('./templates/diary.html')
    html = tin.read()
    tin.close()
    inf = {'name': 'qiqi'}
    t = Template(html)
    c = Context(inf)
    return HttpResponse(t.render(c))



  在上述代码中,我们是用Context函数进行渲染的。

  而与django.template.Context函数差不多的,还有一个django.template.RequestContext函数,它默认在模板中加了一些变量,比如HttpRequest对象、当前登录用户的相关信息……

  来看下面这段代码:

from django.http import HttpResponse
from django.template import loader, Context
def view_1(request):
    # ...
    t = loader.get_template('template1.html')
    c = Context({
        'app': 'My app',
        'user': request.user,
        'ip_address': request.META['REMOTE_ADDR'],
        'message': 'I am view 1.'
    })
    return HttpResponse(t.render(c))
def view_2(request):
    # ...
    t = loader.get_template('template2.html')
    c = Context({
        'app': 'My app',
        'user': request.user,
        'ip_address': request.META['REMOTE_ADDR'],
        'message': 'I am the second view.'
    })
    return HttpResponse(t.render(c))


  其中app、user、ip_address这三个变量,可能需要在很多模板的渲染中重复写入,很麻烦。而利用RequestContext函数,我们便可以将之简化为这样:

from django.http import HttpResponse
from django.template import loader, RequestContext
def custom_proc(request):
    return {
        'app': 'My app',
        'user': request.user,
        'ip_address': request.META['REMOTE_ADDR']
    }
def view_1(request):
    # ...
    t = loader.get_template('template1.html')
    c = RequestContext(request, {'message': 'I am view 1.'}, [custom_proc])
    return HttpResponse(t.render(c))
def view_2(request):
    # ...
    t = loader.get_template('template2.html')
    c = RequestContext(request, {'message': 'I am the second view.'}, [custom_proc])
    return HttpResponse(t.render(c))


  其中,我们写的函数custom_proc,便称为context处理器。用术语来说,我们利用RequestContext函数和context处理器,简化了使用模板的代码。

  而在此基础上使用render_to_response,我们还可以将之简化成:

from django.shortcuts import render_to_response
from django.template import RequestContext
def custom_proc(request):
    return {
        'app': 'My app',
        'user': request.user,
        'ip_address': request.META['REMOTE_ADDR']
    }
def view_1(request):
    # ...
    return render_to_response('template1.html',
        {'message': 'I am view 1.'},
        context_instance=RequestContext(request, [custom_proc]))
def view_2(request):
    # ...
    return render_to_response('template2.html',
        {'message': 'I am the second view.'},
        context_instance=RequestContext(request, [custom_proc]))


  此时,还是有一点美中不足的――渲染语句中,每次都需要手动为processors赋值。

  Django提供了对全局context处理器的支持――可以让你在全局变量中列出一些context处理器,Django在你每次调用RequestContext函数时都会自动加入这些处理器的。这个全局变量在TEMPLATES参数中可以找到,其默认值是:

# TEMPLATES参数中,'OPTIONS'字典中的,'context_processors'列表
[
    'django.template.context_processors.debug',
    'django.template.context_processors.request',
    'django.contrib.auth.context_processors.auth',
    'django.contrib.messages.context_processors.messages',
]



   这四个值不多说,可以去查书和手册。此时,我们可以进一步简化代码如下:

from django.shortcuts import render_to_response
from django.template import RequestContext
def view_1(request):
    # ...
    return render_to_response('template1.html',
        {'message': 'I am view 1.'},
        context_instance=RequestContext(request))
def view_2(request):
    # ...
    return render_to_response('template2.html',
        {'message': 'I am the second view.'},
        context_instance=RequestContext(request))


  至于context处理器,则应当写在单独的一份代码中,推荐命名为context_processors.py。

  只要把这份代码放在Python搜索路径中,Django就可以找到。推荐把它放在project或者app的目录下。

# context_processors.py
def custom_proc(request):
    return {
        'app': 'My app',
        'user': request.user,
        'ip_address': request.META['REMOTE_ADDR']
    }



  最后,修改全局变量即可:

[
    'context_processors.custom_proc',
]

 

2. html自动转义

  从模板生成html时,总会有一个风险――变量包含了会影响html的字符。

  书中举了小例子,这里不加赘述,直接说解决方法:

    1. 确保每个不被信任的变量都用escape过滤器过滤一遍,把潜在的有害html字符转换为无害的。这是初几年Django的处理策略。

    2. 利用Django的html自动转义。下面介绍这种方法。

  只要你使用Django模板,那么变量标签中的以下五个字符都会被自动转义:
原字符     转义结果
<     <
>     >
'     '
"     "
&     &

  当然,有时候你会自己手动用模板变量写一段html代码,那这时候你就需要关闭自动转义了,关闭方法如下:

    1. 变量级别,用safe过滤器: This will not be escaped: {{ data|safe }}

    2. 模板级别,用autoescape标签: {% autoescape off %} ... {% endautoescape %}

        这个标签有两种状态:off、on。

        这个标签是可以嵌套的,例如你可以在一个off的标签内嵌套on的标签。

        当你使用模板继承的时候,显而易见,这个标签依旧会持续生效的。

    3. 站点级别,这个书中只说有方法,却没写,暂时存疑。

  注意:

    1. 过滤多次和过滤一次效果完全一样。

    2. 模板中的字符串常量(例如过滤器中包含的常量)会逃过自动转义,而变量中的字符串则不会。

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

   存疑:

    1. escape过滤器是什么?根据文档中所写,好像是专指autoescape标签?

3. Django如何加载模板

  3.1 什么是Engine

    Engine,简单来说就是关于模板的一份设置,具体定义这里暂不介绍,大家可以先去文章开头所给的文档中查看,以后有时间会补上。

    平时加载模板,使用的就是Django中默认的一个Engine对象,其内容就是咱们在settings.py中那个TEMPLATES参数。因此,你用默认的方式加载模板,也就等同于你用Django中这个默认的Engine对象来加载模板。这个对象是: django.template.engines['django']

    而如果你想要实例化另一个Engine对象,则需要用这个定义: django.template.Engine()

  3.2 加载模板的语句

    Django加载模板的语句有三种:

      1. Engine.from_string(template_code)

        按照Engine对象的设置,编译所给代码生成模板,返回一个Template对象。


# 方法一,使用默认的Engine

from django.template import engines
template = engines['django'].from_string(template_code)

# 方法二,使用一个空的Engine(没有context_processors之类的东西)

from django.template import Engine
template = Engine().from_string(template_code)

       2. Engine.get_template(template_name)

        按照Engine对象的设置,根据所给名称找到模板,在内部进行编译,最后返回一个Template对象。

        如果模板不存在,则返回一个TemplateDoesNotExist的异常。

      3. Engine.select_template(self, template_name_list)

        按照Engine对象的设置,根据所给列表中的名称,顺次寻找模板,把找到的第一个同样处理,返回一个Template对象。

        如果全都不存在,则返回一个TemplateDoesNoeExist的异常。

  3.3 模板加载器

    我们一直说在加载模板,但是TEMPLATES参数中却并没有加载器的设置,此时我们所用的一直是Django中默认的加载器。

    下面,我们就一一介绍Django中的模板加载器:

      1. django.template.loaders.filesystem.Loader(默认)

        从文件系统中加载模板。

        路径:TEMPLATE参数中'DIRS'列表。

      2. django.template.loaders.app_directories.Loader

        从文件系统中加载模板。

        路径:各app,即INSTALLED_APPS参数每个app目录下的templates文件夹。

        开启方式:将TEMPLATES参数中'APP_DIRS'设置为True。

      3. django.template.loaders.eggs.Loader

        从Python egg中加载模板。

        路径:各app。

        开启方式:写出类似代码(未经测试,仅供参考)――

TEMPLATES = [{
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'DIRS': [os.path.join(BASE_DIR, 'templates')],
    'OPTIONS': {
        'loaders': [
            ('django.template.loaders.eggs.Loader'),
        ],
    },
}]


       4. django.template.loaders.cached.Loader

        加载模板时,第一次调用各加载器加载模板并存入缓存,以后则从缓存中直接加载模板。

        路径:取决于调用的各加载器。

        注意:实际加载的各模板应该保证其结点(Node)是进程安全(thread-safe)的,详见文档。

        开启方式:写出类似代码(源自文档)――


TEMPLATES = [{
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'DIRS': [os.path.join(BASE_DIR, 'templates')],
    'OPTIONS': {
        'loaders': [
            ('django.template.loaders.cached.Loader', [
                'django.template.loaders.filesystem.Loader',
                'django.template.loaders.app_directories.Loader',
            ]),
        ],
    },
}]


       5. django.template.loaders.locmem.Loader

        从Python字典中加载模板,常用于测试。

        存疑:实际效果未测试过,不是很能理解。

        开启方式:写出类似代码(源自文档)――

TEMPLATES = [{
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'OPTIONS': {
        'loaders': [
            ('django.template.loaders.locmem.Loader', {
                'index.html': 'content here',
            }),
        ],
    },
}]


实际上,书和文档中还提到了如何写自己的模板、模板中用于debug的两个类、如何写独立模型下的模板……

但由于暂时还用不到,所以先不写了,等以后用到再补充。

热门栏目