【Django】简析Django的模板系统发展史

本文探讨了Django模板系统的发展,从最初的HTML硬编码到使用Template和Context,再到get_template()和render_to_response()的引入。文章详细介绍了如何通过设置TEMPLATE_DIRS加载模板,利用locals()简化视图,以及如何利用include模板标签和模板继承实现代码复用。通过模板继承,可以有效地管理头部、底部和不同中间内容的差异化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

任何系统都是在不断的优化不断的发展,现在我以个人经验总结下Django模板系统的来龙去脉

未使用模板时的html硬编码

尽管这种技术便于解释视图是如何工作的,但直接将HTML硬编码到视图里却并不是一个好主意。

from django.http import HttpResponse
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    html = "<html><body>It is now %s.</body></html>" % now
    return HttpResponse(html)


开始使用模板系统Template,Context

  模板是一个文本,用于分离文档的表现形式和内容,模板定义了占位符以及各种用于规范文档该如何显示的各部分基本逻辑(模板标签)。模板通常用于产生HTML,但是Django的模板也能产生任何基于文本格式的文档。

  模板系统是一个Python库,可以在任何地方使用它,而不仅仅是在Django视图中。

在Python代码中使用Django模板的最基本方式如下:

  可以用原始的模板代码字符串创建一个Template对象,Django同样支持用指定模板文件路径的方式来创建Template对象。
  
  调用模板对象的render方法,并且传入一套变量context。它将返回一个基于模板的展现字符串,模板中的变量和标签会被context值替换。

代码如下:

>>> from django import template
>>> t = template.Template('My name is {{ name }}.')
>>> c = template.Context({'name': 'Adrian'})
>>> print t.render(c)
My name is Adrian.

  必须指出的一点是,t.render(c)返回的值是一个Unicode对象,不是普通的Python字符串,可以通过字符串前的u来区分。在框架中,Django会一直使用Unicode对象而不是普通的字符串。如果明白这样做会带来多大便利,就可知道Django在后台有条不紊地做这些工作;如果不明白从中获益了什么,只需要知道Django对Unicode的支持,将让应用程序轻松地处理各式各样的字符集,而不仅仅是基本的A~Z英文字符。

from django.template import Template, Context
from django.http import HttpResponse
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    t = Template("<html><body>It is now {{ current_date }}.</body></html>")
    html = t.render(Context({'current_date': now}))
    return HttpResponse(html)


通过get_template()加载模板

  没错,上面确实使用了模板系统,但是并没有解决在本节开头所指出的问题。也就是说,模板仍然嵌入在Python代码中,并未真正地实现数据与表现的分离。接下来将模板置于一个单独的文件中,并且让视图加载该文件来解决此问题。

  可能首先考虑把模板保存在文件系统的某个位置并用Python内建的文件操作函数来读取文件内容。假设文件保存在/home/djangouser/templates/mytemplate.html中,代码就会像下面这样:

from django.template import Template, Context
from django.http import HttpResponse
import datetime

def current_datetime(request):
    nwo = datetime.datetime.now()
    fp = open('/home/djangouser/templates/mytemplate.html')
    t = Template(fp.read())
    fp.close()
    html = t.render(Context({'current_date': now}))
    return HttpResponse(html)

然而,基于以下几个原因,该方法还算不上简洁。

1)它没有对文件丢失的情况作出处理。如果文件mytemplate.html不存在或者不可读,open()函数调用将会引发IOError异常。

2)这里对模板文件的位置进行了硬编码。如果在每个视图函数都用该技术,就要不断复制这些模板的位置,更不用说还要带来大量的输入工作。

3)它包含了大量令人生厌的重复代码。与其在每次加载模板时都调用open()、fp.read()和fp.close(),还不如作出更佳选择。

为了解决这些问题,采用模板自加载与模板目录的技巧。

模板加载

  为了减少模板加载调用过程及模板本身的冗余代码,Django提供了一种使用方便且功能强大的API,用于从磁盘中加载模板,要使用此模板加载API,首先必须将模板的保存位置告诉框架。设置的保存文件就是setting.py。

  该设置告诉Django的模板加载机制在哪里查找模板。选择一个目录用于存放模板并将其添加到 TEMPLATE_DIRS :

TEMPLATE_DIRS = (   
    '/home/django/mysite/templates',  // 注意:Python要求单元素元祖中必须使用逗号,以此消除与圆括号表达式之间的起义
)

当然,最省事的方式是使用绝对路径

import os.path

TEMPLATE_DIRS = (
    os.path.join(os.path.dirname(__file__), 'templates').replace('\\','/'),
)
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)


使用render_to_response()代替get_template、Template、Context和HttpResponse

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()技巧

下面思考一下对current_datetime的最后一次赋值:

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})

  很多时候,就像在这个示例中那样,发现自己一直在计算某个变量,保存结果到变量中(如前面代码中的now),然后将这些变量发送给模板。尤其喜欢偷懒的程序员应该注意到了,不断地为临时变量和临时模板命名不仅多余,而且需要额外的输入。如果是喜欢偷懒的程序员并想让代码看起来更加简明,可以利用Python的内建函数locals()。它返回的字典对所有局部变量的名称与值进行映射。因此,前面的视图可以重写为:

from django.shortcuts import render_to_response
import datetime

def current_datetime(request):
    current_date = datetime.datetime.now()
    return render_to_response('current_datetime.html', locals())

  在此,没有像之前那样手工指定context字典,而是传入了locals()的值,它囊括了函数执行到该时间点时所定义的一切变量。因此,将now变量重命名为current_date,因为那才是模板所预期的变量名称。在本例中,locals()并没有带来多大的改进,但是如果有多个模板变量需要界定而程序员又想偷懒,这种技术可以减少一些键盘输入。

  使用locals()时需要注意的是它将包括所有的局部变量,它们可能比程序员想让模板访问的要多。在前例中,locals()还包含了request,对此如何取舍取决于程序员的应用程序。

include模板标签

  每当在多个模板中出现相同的代码时,就应该考虑是否要使用{% include %}来减少重复。一般用于头部和底部的包含。

模板继承

  对基于include的策略,头部和底部的包含很简单,麻烦的是中间部分。Django的模板继承系统解决了这些问题,可以将其视为服务器端include的逆向思维版本。可以对那些不同的代码段进行定义,而不是共同代码段。

注意:如果在模板中使用{% extends %},必须保证其为模板中的第一个模板标记;否则,模板继承将不起作用。

例如:

// base.html
<!DOCTYPE html>
<html lang="en">
<head>
    <title>{% block title %}{% endblock %}</title>
</head>
<body>
    <h1>My timestamp site</h1>
    {% block content %}{% endblock %}
    {% block footer %}
    <hr>
    <p>Thanks for visiting my site.</p>
    {% endblock %}
</body>
</html>
// current_datetime.html
{% extends "base.html" %}
{% block title %}The current time{% endblock %}
{% block content %}
    <p>It is now {{ current_date }}.</p>
{% endblock %}
// hours_ahead.html
{% extends "base.html" %}
{% block title %}Future time{% endblock %}
{% block content %}
    <p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p>
{% endblock %}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值