第九章 模板高级进阶

hekun 08-05 01:54

这一章有什么用。一路看下来,感觉这一章是难点,但是不是重点。

一只梨 07-06 07:50

要是能有导航就好了,导航可以帮助我分清层次。

mihello 08-29 07:58

这章涵盖 context 处理器,转义,模板库,自定义filter/tag。 自定义filter较容易,但是自定义tag就不那么简单了。总的来说可以看,但不要死磕,到你日后真正要自定义filter/tag 再来看或者看官方文档。 总的来说,重要但不急,即使跳过也可以。

mihello 08-29 08:00

等下,escape(转义)部分最好要看,这个一定用到的,自定义那些反而不急。

Ally Li 01-13 03:57

过于啰嗦和理论话,应该切实的从头开始,带领完成一个小项目。

匿名读者 07-18 09:16

这就是类似官方文档的理论讲解,ls要是想跟着做小项目,那你还真是看错书了

对标题的评论会显示在这里

虽然大多数和Django模板语言的交互都是模板作者的工作,但你可能想定制和扩展模板引擎,让它做一些它不能做的事情,或者是以其他方式让你的工作更轻松。

pengpeng 11-19 10:07

其实,看到第八章就感觉枯燥了,要是能有个例子带着大伙儿做做就好了

Dunboa 11-22 01:43

跟楼上的一样。呵呵。所以才说是从第八章开始进阶啦!枯燥点就认了吧。我现在还是反复的看着第八章。不懂的再重复

leo 12-11 15:59

我最开始看的时候也很无聊.运行几个简单的例子很无聊.应该从现在开始把自己要实现的功能.列一个表格.然后一点一点实现.遇到什么问题就找找书中的例子看看.你的网站开发到一半的时候再回头从头看一遍教程.越来越有意思了.

宁方明 04-22 07:06

同意楼上,网站开发到一半的时候再回头从头看一遍教程.越来越有意思了.

ctycheer 05-05 03:11

先自己做过django的一个小网站后,或者去github,看些代码, 然后再Django Book 这样才有体会

melzg 03-24 09:37

每个人的学习方法不同。略懂 + 网站开发实践 + 再学习理解 + 网站代码重构(返工)模式。或者:粗看了解都有什么 + 网站开发实践 + 遇到问题?再学习查找,解决问题 + 良好网站结果。

melzg 03-25 06:17

看累了?还是这章翻译得不好?还是这张根本就没写明白?啃了半天,没怎么明白。需要再反过头来理解,或参照其他书来看了。

Chris 06-28 13:19

受教了,我先來寫一些自己的應用吧~~刻出個大概再來讀~

树不开叉 08-01 14:14

这一章怎么回事,直接看得云里雾里的!

lgy 11-14 12:53

该理解的东西太多。。记不住啊。

sanyang 01-03 15:37

同感呀,在图书馆里看了四天,看到了这里,今天最痛苦了,昏昏欲睡的,以为自己意志薄弱了~~

Harry 01-15 06:23

@ls,我也是,第8章对我这种编程刚入门的人来说看得云里雾里的,如果有实际的例子能练练手或者会好一些。

Harry 01-15 06:25

@leo,你的建议非常好!我觉得也应该要把实现的功能写下来列一个list,遇到哪些问题去教材中找对应的解决方案。

Timor 08-31 08:36

一楼正解,看那个url的时候,有点瞌睡了

Sean 05-07 07:33

看来大家基本都是同样的感觉啊,我第八章反复看了好几遍了,看了中文版再看英文版,就是搞不懂,还很枯燥,想着作者估计要亮牌了,所以先给大家打个预防针,不理解没关系,先跟着下边的做,之后回来再看,可能会有新的理解。

devil 08-27 13:27

前8章对我来说非常简单,一点也不难,然而这章,实在是看不下去了,可能是现在晚上了,头脑不清醒,明天再看看吧

猪头哥哥 12-12 02:08

前七章就是一个简单demo了,后面的内容一次看下来确实有点累人,应该在实际做的时候碰到问题来找找。事实上看一遍记不住东西,不过如果有个东西不知道怎么解决,大概可以知道可以查询的位置

对这一段的评论会显示在这里

本章深入探讨Django的模板系统。 如果你想扩展模板系统或者只是对它的工作原理感觉到好奇,本章涉及了你需要了解的东西。 它也包含一个自动转意特征,如果你继续使用django,随着时间的推移你一定会注意这个安全考虑。

wangyue 10-12 08:43

>>> 这里纠正一个翻译,应该是"自动转义",而不是"自动转意". >>> 自动转义会对http请求中文本里的特征符号转义,即将html标签语言中的转义字符,类似'&', '<', '>'的符号替换成'&quot;', '&lt;', '&gt;'的形式,在浏览器中还是显示'&', '<', '>'这种形式,但实际内容是'&quot;', '&lt;', '&gt;'。 >>> 自动转义的目的是防止xss攻击,一些攻击者会恶意的在请求文本中加入攻击脚本,而自动转义会把html标签的特征符号替换成转义符号,防止脚本自动加载。 >>> 但是自动转义有一些缺陷,比如网站想对文本渲染成markdown模式文本,其中markdown的引用符号是">",请求中使用了引用符号,会被django自动转义(默认情况下),那么markdown的引用符号相当于是无效的. >>> 目前个人已知的解决方式是写一个脚本,把innerTXT替换成innerHTML,但仍有缺陷,或者直接用NodeJS写网站:)

wangyue 10-12 09:45

>>> 最后说的有误,是将文本里有关">"符号被转义的替换成">",这样可以被正常渲染mardown文本了

123 02-20 08:37

123

对这一段的评论会显示在这里

如果你想把Django的模版系统作为另外一个应用程序的一部分(就是说,仅使用Django的模板系统而不使用Django框架的其他部分),那你一定要读一下“配置独立模式下的模版系统”这一节。

对这一段的评论会显示在这里

模板语言回顾

对这一段的评论会显示在这里

首先,让我们快速回顾一下第四章介绍的若干专业术语:

对这一段的评论会显示在这里

模板 是一个纯文本文件,或是一个用Django模板语言标记过的普通的Python字符串。 模板可以包含模板标签和变量。

对这一段的评论会显示在这里

模板标签 是在一个模板里面起作用的的标记。 这个定义故意搞得模糊不清。 例如,一个模版标签能够产生作为控制结构的内容(一个 if语句或for 循环), 可以获取数据库内容,或者访问其他的模板标签。

施洲博 11-23 07:12

内容重复(英文和中文)

sai 04-28 08:59

模版 -> 模板

匿名读者 12-31 02:59

“这个定义故意搞得模糊不清” 我疯了

对这一段的评论会显示在这里

区块标签被 {%%} 包围:

对这一段的评论会显示在这里
{% if is_logged_in %}
    Thanks for logging in!
{% else %}
    Please log in.
{% endif %}
jwfy 02-23 02:33

这个if 能不能写成和C语言一样的呢 如 if a==5 之类的?

tianya 02-27 10:28

@jwfy,目前还不能,可以使用 ifequal。

对这一段的评论会显示在这里

变量 是一个在模板里用来输出值的标记。

对这一段的评论会显示在这里

变量标签被 {{}} 包围:

melzg 03-24 09:45

复习、明确下概念,不错。

对这一段的评论会显示在这里
My first name is {{ first_name }}. My last name is {{ last_name }}.
对这一段的评论会显示在这里

context 是一个传递给模板的名称到值的映射(类似Python字典)。

mac 12-12 07:43

这个context从哪里冒出来的。

ip 06-15 17:27

context是用来给模版变量赋值的

-。- 07-09 08:49

一楼你那里开始看的。。。

对这一段的评论会显示在这里

模板 渲染 就是是通过从context获取值来替换模板中变量并执行所有的模板标签。

对这一段的评论会显示在这里

关于这些基本概念更详细的内容,请参考第四章。

对这一段的评论会显示在这里

本章的其余部分讨论了扩展模板引擎的方法。 首先,我们快速的看一下第四章遗留的内容。

对这一段的评论会显示在这里

RequestContext和Context处理器

对这一段的评论会显示在这里

你需要一段context来解析模板。 一般情况下,这是一个 django.template.Context 的实例,不过在Django中还可以用一个特殊的子类, django.template.RequestContext ,这个用起来稍微有些不同。 RequestContext 默认地在模板context中加入了一些变量,如 HttpRequest 对象或当前登录用户的相关信息。

小鱼 07-17 12:31

上次做东西的时候遇到了一个关于登陆验证的问题,在当前页面登陆成功重定向后又显示状态为未登陆,最后才发现加上RequestContext就解决问题了,可能就是里面HttpRequest等变量的原因吧

小鱼 07-17 12:41

在使用render_to_response,render_to_string等shortcut的时候,可以附带context_instance参数来使用RequestContext return render_to_response('my_template.html', 03.my_data_dictionary, context_instance=RequestContext(request))

匿名读者 05-17 09:14

在django中,上下文处理器的开启使用render(request)来开启 from django.shortcuts import render return render(request,template_name,c)

对这一段的评论会显示在这里

当你不想在一系例模板中都明确指定一些相同的变量时,你应该使用 RequestContext 。 例如,考虑这两个视图:

kimi 01-12 05:54

读到这里愣了一下,应该意为“隐式指定变量”比较好,“不想…明确指定…”像是不指定

对这一段的评论会显示在这里
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 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 t.render(c)
Lnag 09-09 07:30

应该是return HttpResponse(t.render(c))还有开头要导入模块from django.http import HttpResponse

roger 11-23 07:15

你错了,这里是先得到模板对象,再渲染,与render_to_response是一样的

Python_Newbie 05-10 02:16

你需要一段context来解析模板。 是否应该把解析改为“渲染”或者“填充”

charlie 07-03 09:26

一楼正确

mr.liu 08-12 09:50

作者这里没有打算使用HttpResponse返回给用户,只是说解决重复变量问题。

q2z 02-17 08:08

一楼正解!

longdd 07-05 08:31

一楼棒棒哒

horizonshd 09-18 05:18

在Django-1.11.3,在 t.render(c) 处产生错误:1.11.3 TypeError:context must be a dict rather than Context.

horizonshd 09-18 05:18

在Django-1.11.3,在 t.render(c) 处产生错误: TypeError:context must be a dict rather than Context.

匿名读者 05-17 05:38

django2.0 (render接受一个字典,而不是一个Context对象 t = get_template(template_name) html = t.render({'time':Person(offset,dt,all_time_list,model.__name__.lower())}) return HttpResponse(html)

对这一段的评论会显示在这里

(注意,在这些例子中,我们故意 使用 render_to_response() 这个快捷方法,而选择手动载入模板,手动构造context对象然后渲染模板。 是为了能够清晰的说明所有步骤。)

对这一段的评论会显示在这里

每个视图都给模板传入了三个相同的变量:appuserip_address。 如果我们把这些冗余去掉会不会更好?

对这一段的评论会显示在这里

创建 RequestContextcontext处理器 就是为了解决这个问题。 Context处理器允许你设置一些变量,它们会在每个context中自动被设置好,而不必每次调用 render_to_response() 时都指定。 要点就是,当你渲染模板时,你要用 RequestContext 而不是 Context

宁方明 04-22 07:23

不理解,谁能帮忙分析一下,这段话是什么意思?

ode2free 05-09 14:52

Context处理器 -- Context processor

zxw 07-27 00:06

Context processor允许你设置一些变量,它们会在每个context中自动被设置好 ,而不必每次在调用render_to_response()时候指定,要想让利用context自动设好的值 ,你必须在渲染模板的时候用RequestContext而不是Context示例

debug 08-19 16:55

原文其实说的是render方法,这里翻译改成了render_to_response。。。

对这一段的评论会显示在这里

最直接的做法是用context处理器来创建一些处理器并传递给 RequestContext 。上面的例子可以用context processors改写如下:

对这一段的评论会显示在这里
from django.template import loader, RequestContext

def custom_proc(request):
    "A context processor that provides 'app', 'user' and 'ip_address'."
    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.'},
            processors=[custom_proc])
    return t.render(c)

def view_2(request):
    # ...
    t = loader.get_template('template2.html')
    c = RequestContext(request, {'message': 'I am the second view.'},
            processors=[custom_proc])
    return t.render(c)
ode2free 05-09 15:36

是不是应该返回 return HttpResponse(t.render(c))

Black Glory 06-25 03:04

这里只是把模板渲染,原文中的代码就可以了

keroro 09-09 13:04

我这里一定要写成 "return HttpResponse(t.render(c))",不知道为什么?用原文的代码会出现“'SafeUnicode' object has no attribute 'get'的错误。

lisa 12-07 09:25

为什么我的custom的方法如文中那样的返回结果报语法错误?????

avyou 07-22 08:09

我的也是用: return HttpResponse(t.render(c)) 这样才可以,直接 t.render(c) 就报错!

zzm88 08-12 06:37

我也是!如ls各位所言

jwfy 09-30 12:26

我也出现这种情况。。。。不知道如何解决

熬夜 03-10 16:22

'SafeText' object has no attribute 'get' 我这个一直是这个 说loader没有get_template属性

jstech 07-01 09:26

这里在struts2中对应值栈和HttpRequestContext。 值栈对应本文context,其内容只能在一个视图和模板中被使用。 HttpRequestContext对应本文RequestContext,其内容在可以在途经的所有视图和模板中,使用。 即:view_1 -->{'app':'my app',,,}-->t_1.html; -->view_2 -->t_2.html

mihello 08-20 14:13

参考第四章,如LS所说还得加上HttpResponse(t.render(c))

匿名读者 11-12 13:51

楼上几位你们都是怎么让这程序运行起来的?

mr.liu 08-12 09:58

python manage.py shell

YANG WANG 09-14 01:41

直接拿一个字典进行渲染而不用context创建字典也可以 你可以试试 直接定义一个函数 def custom_proc(request): return { 'app':'myapp', 'user':request.user, #'ip_address':request.META['REMOTE_ADDR'] } 然后def view_1(request): t = get_template('template1.html') c = custom_proc(request) c['message']='im view 1' return render_to_response('template1.html',c) 一样管用

walkmiao 01-18 12:02

django2.0中如果使用t.render(c) 这么写是可以的 with open(os.path.join(BASEDIR, 'template/demo.html'),'rt') as f: template_string = f.read() t = Template(template_string) c = RequestContext(request,processors=[lambda request:{"first":1,"last":2}]) return HttpResponse(t.render(c)) 根本原因:loader.get_template(template_name)返回的对象并不是 django.template.Template 对象。

对这一段的评论会显示在这里

我们来通读一下代码:

对这一段的评论会显示在这里
  • 首先,我们定义一个函数 custom_proc 。这是一个context处理器,它接收一个 HttpRequest 对象,然后返回一个字典,这个字典中包含了可以在模板context中使用的变量。 它就做了这么多。
  • 我们在这两个视图函数中用 RequestContext 代替了 Context 。在context对象的构建上有两个不同点。 一, RequestContext 的第一个参数需要传递一个 HttpRequest 对象,就是传递给视图函数的第一个参数( request )。二, RequestContext 有一个可选的参数 processors ,这是一个包含context处理器函数的列表或者元组。 在这里,我们传递了我们之前定义的处理器函数 curstom_proc
  • 每个视图的context结构里不再包含 appuserip_address 等变量,因为这些由 custom_proc 函数提供了。
  • 每个视图 仍然 具有很大的灵活性,可以引入我们需要的任何模板变量。 在这个例子中, message 模板变量在每个视图中都不一样。
徐云东 11-29 09:14

custom_proc 吧。。

匿名读者 08-01 06:47

fuck

对这一段的评论会显示在这里

在第四章,我们介绍了 render_to_response() 这个快捷方式,它可以简化调用 loader.get_template() ,然后创建一个 Context 对象,最后再调用模板对象的 render()过程。 为了讲解context处理器底层是如何工作的,在上面的例子中我们没有使用 render_to_response() 。但是建议选择 render_to_response() 作为context的处理器。这就需要用到context_instance参数:

mac 12-12 07:51

翻译的什么鬼

对这一段的评论会显示在这里
from django.shortcuts import render_to_response
from django.template import RequestContext

def custom_proc(request):
    "A context processor that provides 'app', 'user' and 'ip_address'."
    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, processors=[custom_proc]))

def view_2(request):
    # ...
    return render_to_response('template2.html',
        {'message': 'I am the second view.'},
        context_instance=RequestContext(request, processors=[custom_proc]))
soda 08-13 03:34

return render_to_response('text', {'message':'this a leave'}, RequestContext(request,processors=[custom_poc]) ) context_instance在dj1.4中可有可无

charlie 07-03 09:45

1.6中也可有可无

YANG WANG 09-14 05:32

1。8中context_instance报错 去掉可以运行

django1.10版本之后 03-05 12:00

1.10 之后的版本不用使用render_to_response(),使用它csrf的验证token填充不到模板表单。

andrew 07-13 02:10

django2.0版本中这样可以实现: # 自定义Context处理器 def custom_proc(request): return { 'app': 'My app', 'user': 'jack', 'ip_address': request.META['REMOTE_ADDR'], } def view_2(request): c = csrf(request) c.update(custom_proc(request)) c.update({'message': 'I am view 2.'}) return render(request, 'webtest/view2.html', context=c)

对这一段的评论会显示在这里

在这,我们将每个视图的模板渲染代码写成了一个单行。

对这一段的评论会显示在这里

虽然这是一种改进,但是,请考虑一下这段代码的简洁性,我们现在不得不承认的是在 另外 一方面有些过分了。 我们以代码冗余(在 processors 调用中)的代价消除了数据上的冗余(我们的模板变量)。 由于你不得不一直键入 processors ,所以使用context处理器并没有减少太多的输入量。

对这一段的评论会显示在这里

Django因此提供对 全局 context处理器的支持。 TEMPLATE_CONTEXT_PROCESSORS 指定了哪些context processors总是默认被使用。这样就省去了每次使用 RequestContext 都指定 processors 的麻烦。

xmmilk 07-17 14:52

没有全局context使用的例子。。上面一段不是说了:一直键入processors,所以django提供了全局context支持。这也没看到如何使用全局context呀!

匿名读者 08-24 17:43

估计是把自己的处理器路径加在TEMPLATE_CONTEXT_PROCESSORS里面吧,也没觉得能节约多少代码量。。。

hekun 08-02 02:25

对啊,这个“全局context处理器”应该怎么玩。

xiaoge 12-02 10:41

例子:http://361031315.blog.51cto.com/4780267/1335057

helpme 05-24 13:59

ls的例子已看“vim context_processors.py (在settings.py同级目录下创建) def auston_proc(request): return { 'app': 'My app', 'user': request.user, 'ip_address': request.META['REMOTE_ADDR'] }” 在setting。py同级目录下创建是什么意思,再建一个views?

helpme 05-24 14:00

以懂

对这一段的评论会显示在这里

默认情况下, TEMPLATE_CONTEXT_PROCESSORS 设置如下:

对这一段的评论会显示在这里
TEMPLATE_CONTEXT_PROCESSORS = (
    'django.core.context_processors.auth',
    'django.core.context_processors.debug',
    'django.core.context_processors.i18n',
    'django.core.context_processors.media',
)
tonic 11-12 06:36

这个设置是在哪里?

lastinglate 04-22 07:53

楼上,这个是指是为settings.py里面

ode2free 05-09 16:06

settings.py 里为什么没有这个选项, django 1.3.0

ok 05-11 07:35

要自己加上去吗?还是怎样?

snyh 05-14 01:52

在django的conf/global_settings.py里面有 这些是默认设置 一般情况是在python的site-package/django/conf/global_settings.py

Black Glory 06-25 09:04

修改Django内部的设置没问题么?

Francois 07-25 12:10

在我的ubuntu中,是在以下目录中: “/usr/local/lib/python2.7/dist-packages/Django-1.3-py2.7.egg/django/conf/global_settings.py”

melzg 03-24 11:30

1.3.1中,./usr/local/src/Django-1.3.1/django/conf/global_settings.py 定义有变化。 198: 'django.contrib.auth.context_processors.auth', ... 204: 'django.contrib.messages.context_processors.messages',

王洁 07-06 07:56

global_settings是默认配置文件,也就是说你的Django工程中没有settings也没问题,他会从global_settings中读取,所以只要在setting文件中加入需要的配置名就OK了

robin_chenyu 08-11 02:08

TEMPLATE_CONTEXT_PROCESSORS如果配置到工程的settings.py,可以覆盖global_settings.py中的这个变量吗?

robin_chenyu 08-11 02:16

网上关于这个问题的讨论很多,个人认为在工程settings.py中用这种方法配置最好 from django.conf import global_settings TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + ( "myapp.processor.foos", )

jwfy 09-30 12:47

这也挺纳闷的。。。。

tommy 10-11 11:23

django.core.context_processors.auth应该设置为 django.contrib.auth.context_processors.auth

agon 10-27 00:12

看完后面还是robin_chenyu说的对

mihello 08-21 08:51

Ls 说的对,Django 1.6.5 TEMPLATE_CONTEXT_PROCESSORS Default: ("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.core.context_processors.tz", "django.contrib.messages.context_processors.messages")

horizonshd 09-16 05:30

在Django-1.11.3中,django.conf.global_settings.TEMPLATE_CONTEXT_PROCESSORS似乎已经不存在……是怎么解决的?

wangyue 10-12 09:13

>>> 使用Pycharm可以很方便的找到你配置工作环境里global_setting

匿名读者 09-10 05:15

django 2.1 的配置在TEMPLATES ---> OPTIONS ---> context_processors 例如: 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ],

对这一段的评论会显示在这里

这个设置项是一个可调用函数的元组,其中的每个函数使用了和上文中我们的 custom_proc 相同的接口,它们以request对象作为参数,返回一个会被合并传给context的字典: 接收一个request对象作为参数,返回一个包含了将被合并到context中的项的字典。

ajianrelease 07-14 05:38

第二个处理器添加了同样名字的变量,应该是不会覆盖第一个吧,而是第一个覆盖第二个吧?

ajianrelease 07-14 06:13

看错了,的确是后面的会覆盖前面的

对这一段的评论会显示在这里

每个处理器将会按照顺序应用。 也就是说如果你在第一个处理器里面向context添加了一个变量,而第二个处理器添加了同样名字的变量,那么第二个将会覆盖第一个。

对这一段的评论会显示在这里

Django提供了几个简单的context处理器,有些在默认情况下被启用的。

对这一段的评论会显示在这里

django.core.context_processors.auth

zxw 07-27 00:25

为什么我在这个文件里没有这个视图函数

test 07-06 04:12

新版本导入路径: django.contrib.auth.context_processors.auth

mihello 08-22 02:19

LS 正解 1.6.5适用

对这一段的评论会显示在这里

如果 TEMPLATE_CONTEXT_PROCESSORS 包含了这个处理器,那么每个 RequestContext 将包含这些变量:

horizonshd 09-16 05:38

在Django-1.11.3中,的定义: def auth(request): """ Returns context variables required by apps that use Django's authentication system. If there is no 'user' attribute in the request, uses AnonymousUser (from django.contrib.auth). """ if hasattr(request, 'user'): user = request.user else: from django.contrib.auth.models import AnonymousUser user = AnonymousUser() return { 'user': user, 'perms': PermWrapper(user), }

horizonshd 09-16 05:39

1\n 2<br> 3

horizonshd 09-16 05:40

这个评论不能格式化输出啊…………

对这一段的评论会显示在这里
  • user :一个 django.contrib.auth.models.User 实例,描述了当前登录用户(或者一个 AnonymousUser 实例,如果客户端没有登录)。
  • messages :一个当前登录用户的消息列表(字符串)。 在后台,对每一个请求,这个变量都调用 request.user.get_and_delete_messages() 方法。 这个方法收集用户的消息然后把它们从数据库中删除。
  • permsdjango.core.context_processors.PermWrapper 的一个实例,包含了当前登录用户有哪些权限。
王洁 07-06 08:05

1.4中message已经取消了

王洁 07-06 08:34

并非取消,放到了django.contrib.messages.context_processors.messages中

mihello 08-22 06:27

1.6中 是perms是django.contrib.auth.context_processors.PermWrapper的实例

朱文标 11-06 09:00

数据库中还有吗,为啥要从数据库中移除

horizonshd 09-16 05:45

在Django-1.11.3中,在django.contrib.messages.context_processors.messages中也还存在

对这一段的评论会显示在这里

关于users、permissions和messages的更多内容请参考第14章。

对这一段的评论会显示在这里

django.core.context_processors.debug

对这一段的评论会显示在这里

这个处理器把调试信息发送到模板层。 如果TEMPLATE_CONTEXT_PROCESSORS包含这个处理器,每一个RequestContext将包含这些变量:

对这一段的评论会显示在这里
  • debug :你设置的 DEBUG 的值( TrueFalse )。你可以在模板里面用这个变量测试是否处在debug模式下。
  • sql_queries :包含类似于 [](#id3){‘sql’: …, ‘time’: 的字典的一个列表, 记录了这个请求期间的每个SQL查询以及查询所耗费的时间。 这个列表是按照请求顺序进行排列的。 System Message: WARNING/2 (<string>, line 315); backlink Inline literal start-string without end-string.
Black Glory 06-25 10:38

System Message: WARNING/2 (<string>, line 315); backlink Inline literal start-string without end-string.

ode2free 05-22 10:23

[ {'sql': ..., 'time': ...} , {'sql': ..., 'time': ...} , ... ]

mihello 08-22 09:18

1.6中 如果在template使用 {{ debug }} 即使你settings.py里 DEBUG = True, 依旧是不显示的(也就是False),看源码: if settings.DEBUG and request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS: context_extras['debug'] = True 所以如果你是本地访问不妨在settings.py设置 INTERNAL_IPS = ('127.0.0.1',) 注意是个元组,这样就生效可以显示 {{ debug }} 为True了

对这一段的评论会显示在这里

由于调试信息比较敏感,所以这个context处理器只有当同时满足下面两个条件的时候才有效:

对这一段的评论会显示在这里
  • DEBUG 参数设置为 True
  • 请求的ip应该包含在 INTERNAL_IPS 的设置里面。
对这一段的评论会显示在这里

细心的读者可能会注意到debug模板变量的值永远不可能为False,因为如果DEBUGFalse,那么debug模板变量一开始就不会被RequestContext所包含。

dawn 07-12 02:26

debug模板变量是什么东东?在哪个位置涅?

苏哲 02-18 15:35

哈哈, 这段有意思, 确实是这样

fc 03-01 11:16

debug模板变量指,在模板(template)中的 {{ debug }}变量;\n这段话的意思是说:真正传递给模板的参数request中,要么有request[debug] = True; 要么request[debug]根本就不存在,不可能有request[debug]=False。 但就这个process来说,在模板中如果写 {% if debug is False %} 这样的分支是没有意义的,永远不能实现(除非有其他的 中间件 或者 模块 设置了request[debug]=False)。

zxw 07-27 00:27

if settings.DEBUG and request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS: context_extras['debug'] = True

匿名读者 08-19 05:09

楼上好像没说到重点,我是这样理解的,如果setting文件里面Debug=False,那么django.core.context_processors.debug这一项就不可能包含在TEMPLATE_CONTEXT_PROCESSORS里面,也就RequestContext就不会包含debug变量了

mr.liu 08-13 09:52

如果DEBUG=False,那么context处理器“django.core.context_processors.debug”就不会生效,就不会有debug变量!

对这一段的评论会显示在这里

django.core.context_processors.i18n

zxw 07-27 00:29

我的还有一个变量LANGUAGE_BIDI

对这一段的评论会显示在这里

如果这个处理器启用,每个 RequestContext 将包含下面的变量:

对这一段的评论会显示在这里
  • LANGUAGESLANGUAGES 选项的值。
  • LANGUAGE_CODE :如果 request.LANGUAGE_CODE 存在,就等于它;否则,等同于 LANGUAGE_CODE 设置。
对这一段的评论会显示在这里

附录E提供了有关这两个设置的更多的信息。

对这一段的评论会显示在这里

django.core.context_processors.request

qin 10-28 08:12

这里应该是笔误吧?'django.core.context_processors.media',

liuyi 05-08 14:13

没错。def request(request): return {'request': request}

fc 03-01 11:20

就是在request中包含自身,好让模板中的 {{ request }}及其相关的变量 能够利用自身。

对这一段的评论会显示在这里

如果启用这个处理器,每个 RequestContext 将包含变量 request , 也就是当前的 HttpRequest 对象。 注意这个处理器默认是不启用的,你需要激活它。

对这一段的评论会显示在这里

如果你发现你的模板需要访问当前的HttpRequest你就需要使用它:

ode2free 05-22 10:29

下面是一个在模板中 调用 request 中 ip地址 的例子:

zxw 07-27 00:31

You might want to use this if you find your templates needing to access attributes of the current HttpRequest such as the IP address:

zxw 07-27 00:33

应该这样理解:如果启用这个处理器,每个 RequestContext 将包含变量 request , 也就是当前的 HttpRequest 对象。如果你想访问IP地址,就这样做:{{request.REMOTE_ADDR}}

roc pass 10-12 05:00

1.5下只能得到{{ request }}

sangoly 11-15 07:20

这个和之前的把request.GET['message']传递给下一个html模板的例子原理是一样的,都是在下一个模板中访问当前返回的HttpRequest对象

mihello 08-23 03:04

1.6 是 {'request': request} 这里应该是request.META['REMOTE_ADDR'] 模板中应该是request.META.REMOTE_ADDR

freesquirrel 10-20 04:00

>1.6版本应该是{{ request.META.REMOTE_ADDR }}

对这一段的评论会显示在这里
{{ request.REMOTE_ADDR }}
对这一段的评论会显示在这里

写Context处理器的一些建议

fc 03-01 11:22

processor的整体作用,就是能够让web 服务器中传出的request对象中包含一些 补充或者自定义 的字段(信息),好方便后面的各种编程的实现

对这一段的评论会显示在这里

编写处理器的一些建议:

对这一段的评论会显示在这里
  • 使每个context处理器完成尽可能小的功能。 使用多个处理器是很容易的,所以你可以根据逻辑块来分解功能以便将来复用。
  • 要注意 TEMPLATE_CONTEXT_PROCESSORS 里的context processor 将会在基于这个settings.py的每个 模板中有效,所以变量的命名不要和模板的变量冲突。 变量名是大小写敏感的,所以processor的变量全用大写是个不错的主意。
  • 不论它们存放在哪个物理路径下,只要在你的Python搜索路径中,你就可以在 TEMPLATE_CONTEXT_PROCESSORS 设置里指向它们。 建议你把它们放在应用或者工程目录下名为 context_processors.py 的文件里。
walker 03-29 03:51

我要到哪里去找

呜呜祖拉 08-05 07:40

写的太含糊了,能不能聚几个例子啊?

z 11-08 02:41

context processors 这一段没看懂……

lastinglate 04-22 08:32

“不论它们存放在哪个物理路径下,只要在你的Python搜索路径中,你就可以在 TEMPLATE_CONTEXT_PROCESSORS 设置里指向它们。 建议你把它们放在应用或者工程目录下名为 context_processors.py 的文件里。”怎么指向,在哪里指向?

ode2free 05-09 19:45

@lastinglate 比如你的站点叫mysite,可以在TEMPLATE_CONTEXT_PROCESSORS 的设置元组里加上一行 'mysite.context_processors', 应该是这样

ok 05-11 07:42

同意楼上的说法。譬如:你想要使用template扩展模板的时候,在头文件里from mysite.context_processors(工程目录下的文件名) import ××(import什么我也不知道,就用通配符代替..)不知道是否是这样?

Black Glory 06-25 11:01

这一章都很难懂啊,是翻译的问题吗?

匿名读者 08-29 02:07

这样不会增大开销么

xiaocainiaok 04-25 06:46

默认的设置 是在 python的 库目录里面,意思应该是在python库目录里面添加一个新的文件 ?

ooxx 02-01 06:12

把你写的context_process.py的模块导入路径路径,放在django的全局global_settings.py的TEMPLATE_CONTEXT_PROCESSORS的元祖里面,比如你的处理器函数的导入路径是testdjango.contact.context_processors.custom_proc,就在TEMPLATE_CONTEXT_PROCESSORS后面添加一行 'testdjango.contact.context_processors.custom_proc'(注意不要在自己的应用程序的settings.py新建 TEMPLATE_CONTEXT_PROCESSORS变量添加)

avyou 07-22 05:46

哎,ConText processor 这一大段内容几乎没看懂,怎么办

avyou 07-29 03:04

复习了前面的内容,再看了一遍才明白

agon 10-27 00:20

在global_setting里加不好吧,那所有创建的工程不是都可以看到,应该项目的setting里加载,前面几段里有一个,之前有人评论用这种方法, from django.conf import global_settings TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + ( "myapp.processor.foos", )

苏国峰 11-05 15:46

我觉得,应该是首先在本项目settings.py中查找TEMPLATE_CONTEXT_PROCESSORS,如果没有,就去global_settings中查找。

李大嘴 12-01 15:03

As variable names are case-sensitive, it’s not a bad idea to use all caps for variables that a processor provides.

bobo1732 05-15 07:35

完全没必要在setting里面设置来设置去 直接把所谓的处理器放到 context_processors.py中,在视图中用import导入进来用就是了!

匿名读者 12-10 05:56

楼上真有意思

haobo 12-10 05:56

楼上真有意思+1

www 02-26 04:25

没事,接着往下看,context processor要和template结合起来才看得懂,光看view这一块确实很难懂的

wangyue 10-12 09:30

>>> @agon 提及的方式是比较合理的,之前的有人提及过了

对这一段的评论会显示在这里

html自动转意

wgzhao 05-20 08:19

转义,不是转意。这样更大众一些

sephiroth 06-29 07:16

'变量包了含' -> '变量包含了'

楼上这两个错误到现在还没被改过来,╮(╯▽╰)╭ 11-06 12:17

Johnpang 04-05 05:49

这个是有关安全的问题

goat 06-03 07:56

防xss攻击,但是估计没有卵用,目前还没有什么网站敢说自己完全没有xss漏洞。

对这一段的评论会显示在这里

从模板生成html的时候,总是有一个风险——变量包了含会影响结果html的字符。 例如,考虑这个模板片段:

对这一段的评论会显示在这里
Hello, {{ name }}.
对这一段的评论会显示在这里

一开始,这看起来是显示用户名的一个无害的途径,但是考虑如果用户输入如下的名字将会发生什么:

对这一段的评论会显示在这里
<script>alert('hello')</script>
对这一段的评论会显示在这里

用这个用户名,模板将被渲染成:

对这一段的评论会显示在这里
Hello, <script>alert('hello')</script>
对这一段的评论会显示在这里

这意味着浏览器将弹出JavaScript警告框!

对这一段的评论会显示在这里

类似的,如果用户名包含小于符号,就像这样:

对这一段的评论会显示在这里

用户名

Oliver 09-12 13:13

这个用户名不用翻译,应该是

django 06-22 03:35

原文此处应该是:<b>username

对这一段的评论会显示在这里

那样的话模板结果被翻译成这样:

对这一段的评论会显示在这里
Hello, <b>username
对这一段的评论会显示在这里

页面的剩余部分变成了粗体!

对这一段的评论会显示在这里

显然,用户提交的数据不应该被盲目信任,直接插入到你的页面中。因为一个潜在的恶意的用户能够利用这类漏洞做坏事。 这类漏洞称为被跨域脚本 (XSS) 攻击。 关于安全的更多内容,请看20章

ode2free 05-09 19:49

Cross Site Script

suzj801 09-10 03:27

很容易就忘记转意数据 => 很容易就忘记转义数据

Harry 01-16 01:55

看到这里有点不明白教程中说的 “转意”的概念,以及为什么要“转意”? 理解的同学能讲解一下么?

junsure 03-16 05:15

Johnpang 04-05 05:51

本来应该显示字符串的但是浏览器认为这个不是名字而是一个模版的一部分 所以给转意了,就好比你就是想打出/n 但是结果打出了回车是一样的

匿名读者 10-02 04:38

我感觉,此处的转义是指,传递给模板的一些可以被浏览器执行的特殊字符,在每个字符前面加上转义符,从而避免被当作脚本执行,可以原封不动地展现在html页面上

huhu 06-28 09:02

难道没有类似 r''的处理么,里面写的啥都不管。

对这一段的评论会显示在这里

为了避免这个问题,你有两个选择:

对这一段的评论会显示在这里
  • 一是你可以确保每一个不被信任的变量都被escape过滤器处理一遍,把潜在有害的html字符转换为无害的。 这是最初几年Django的默认方案,但是这样做的问题是它把责任推给(开发者、模版作者)自己,来确保把所有东西转意。 很容易就忘记转意数据。
  • 二是,你可以利用Django的自动html转意。 这一章的剩余部分描述自动转意是如何工作的。
对这一段的评论会显示在这里

在django里默认情况下,每一个模板自动转意每一个变量标签的输出。 尤其是这五个字符。

noooop 04-19 16:24

不觉得django特别伟大吗

对这一段的评论会显示在这里
  • [`](#id5)\ `` System Message: WARNING/2 (`, line 491); backlink Inline literal start-string without end-string.
  • 被转换为>

  • '(单引号)被转换为'
  • "(双引号)被转换为"
  • & is converted to &
AlexNeko 01-24 10:35

这里有问题、 & is converted to &、 后面的&被unescape了

Black Glory 06-25 11:04

System Message: WARNING/2 (<string>, line 491); backlink Inline literal start-string without end-string.

Gitree 08-17 16:11

< 被转意为 &lt; > 被转意为 &gt; ' (single quote) 被转意为 &#39; " (double quote) 被转意为 &quot; & 被转意为 &amp;

i love gittree 08-11 12:44

good

athos 03-16 12:46

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

fandyst 05-11 14:04

这里的&nbsp等符号被转义啦,哎哟

李扬 08-22 11:49

这几出转化翻译的不对原文不是这么写的 < is converted to &lt; > is converted to &gt; ' (single quote) is converted to &#39; " (double quote) is converted to &quot; & is converted to &amp;

azurefang 11-07 14:05

不是翻译的不对,是翻译者打出来后被浏览器给转义了。

zzZ 03-11 09:40

转移无处不在呀

牛三金 04-24 17:22

http://www.djangobook.com/en/2.0/chapter09.html 此为英文教程地址。

www 02-26 04:28

这的符号自己都被转义了...

andrew 07-13 03:14

django原版: https://djangobook.com/writing-context-processors/

对这一段的评论会显示在这里

另外,我强调一下这个行为默认是开启的。 如果你正在使用django的模板系统,那么你是被保护的。

对这一段的评论会显示在这里

如何关闭它

对这一段的评论会显示在这里

如果你不想数据被自动转意,在每一站点级别、每一模板级别或者每一变量级别你都有几种方法来关闭它。

对这一段的评论会显示在这里

为什么要关闭它? 因为有时候模板变量包含了一些原始html数据,在这种情况下我们不想它们的内容被转意。 例如,你可能在数据库里存储了一段被信任的html代码,并且你想直接把它嵌入到你的模板里。 或者,你可能正在使用Django的模板系统生成非html文本,比如一封e-mail。

对这一段的评论会显示在这里

用safe过滤器为单独的变量关闭自动转意:

对这一段的评论会显示在这里
This will be escaped: {{ data }}
This will not be escaped: {{ data|safe }}
大头 06-29 12:43

怎么我加了 |safe 后才会有粗体呢?

hekun 08-01 14:54

我加不加|safe都显示 Hello, <b>test, 看上去都没有转意。这是为什么?

Aaron 09-25 11:12

@大头 例子说了关闭转义后才会被渲染成HTML内容 @hekun 加了safe之后应该<b>应该会被翻译成粗体标签,我测试是没问题的。

苏国峰 11-05 16:06

呵呵,大头真粗心。

nonikka 04-01 13:23

注意哦,用syntaxhl的话记得加safe才能显示出代码

zmrenwu 07-01 11:58

意思就是,加了safe,按html格式输出,不加safe,原样输出,所以说默认情况下你是被保护的。

wangyue 10-12 09:36

>>> safe代表你认为数据安全,而不是数据本身是安全的,相当于你手动检查一遍数据,认为没有恶意脚本插入啦(其实你可能偷懒根本没有检查)~ 然后你加了个safe,表示"我认为数据是安全可信的!"

wangyue 10-12 09:37

>>> 然后django就不会转义这段数据了。

对这一段的评论会显示在这里

你可以把safe当做safe from further escaping的简写,或者当做可以被直接译成HTML的内容。在这个例子里,如果数据包含'',那么输出会变成:

ode2free 05-09 19:57

如果数据包含 <b>

Silent 10-04 04:43

所以,这些文章里的‘括号里没有应该有的字符’的错误是都被escape掉了吗,哈哈,突然想到~

对这一段的评论会显示在这里
This will be escaped: &lt;b&gt;
This will not be escaped: <b>
对这一段的评论会显示在这里

为了控制模板的自动转意,用标签autoescape来包装整个模板(或者模板中常用的部分),就像这样:

对这一段的评论会显示在这里
{% autoescape off %}
    Hello {{ name }}
{% endautoescape %}
对这一段的评论会显示在这里

autoescape 标签有两个参数on和off 有时,你可能想阻止一部分自动转意,对另一部分自动转意。 这是一个模板的例子:

Saito 07-15 05:11

转意 => 转义

huhu 06-28 09:16

为啥我没反应,都是转义过去了

对这一段的评论会显示在这里
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 %}
对这一段的评论会显示在这里

auto-escaping 标签的作用域不仅可以影响到当前模板还可以通过include标签作用到其他标签,就像block标签一样。 例如:

mr.liu 08-14 03:52

是extends 继承吧。

p1 07-11 09:31

这是翻译反了吧

对这一段的评论会显示在这里
# 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 %}
对这一段的评论会显示在这里

由于在base模板中自动转意被关闭,所以在child模板中自动转意也会关闭.因此,在下面一段HTML被提交时,变量greeting的值就为字符串Hello!

lei 03-27 05:46

因为自动转意关闭了,所以此时greeting应该为字符串“<b>Hello</b>”

李旭章 12-31 02:46

“自动转意”应改为“自动转义”。后文类似。

小白 08-04 06:29

翻译错了,原文是说如果greeting字符串的如下值,并且关闭了auto-eascaping,那么渲染后的html是这样的:

roger 11-23 08:37

test <b>Hello!</b>

roger 11-23 08:37

test 'Hello'

匿名读者 08-15 08:00

还是看原文,翻译的凌乱了

Aaron 09-25 11:18

下面显示的是html的文件内容,是关闭转义后的。如果没有关闭的话,第二行应该为 &lt;b&gt;Hello&lt;/b&gt;

qin 11-01 08:08

while auto-escaping is turned off , the value of greeting variable should be: <b>Hello!</b>

mr.liu 08-14 05:39

greeting传入的变量为‘<b>Hello!</b>’,如果关闭转义,就会当作html执行,Hello会变成粗体显示;如果是开启转义,会当做普通文本显示,<b>Hello!</b>。

杜浩 05-27 02:10

mr.liu说反了 如果关闭了转义 就是目前的情况 就是原来啥样就输出啥样,没关闭转义就是按HTML规则输出

杜浩 05-27 02:45

mr.liu没说说反是我理解有误 这里的是HTML文件的内容不是显示内容 。通过两步来理解,首先是转义 然后HTML去解析。不转义的话 <b>hello</b> 直接交给HTML页面解析 会呈现出来粗体hello .转义的话 页面会把这个代码转变成&lt;b&gt;Hello&lt;/b&gt; 然后交给HTML解析 就解析成<b>hello</b>.上文的safe也是说不转义 直接给HTML解析 所以加了safe的字段 如果输入<b>hello</b>也会被HTML解析成粗体hello

YANG WANG 09-20 01:40

因为自动转意关闭了 原来的HTML又占领高地了!

对这一段的评论会显示在这里
<h1>This & that</h1>
<b>Hello!</b>
对这一段的评论会显示在这里

备注

对这一段的评论会显示在这里

通常,模板作者没必要为自动转意担心. 基于Pyhton的开发者(编写VIEWS视图和自定义过滤器)只需要考虑哪些数据不需要被转意,适时的标记数据,就可以让它们在模板中工作。

对这一段的评论会显示在这里

如果你正在编写一个模板而不知道是否要关闭自动转意,那就为所有需要转意的变量添加一个escape过滤器。 当自动转意开启时,使用escape过滤器似乎会两次转意数据,但其实没有任何危险。因为escape过滤器不作用于被转意过的变量。

小白 08-04 06:28

大哥,翻译的有点离谱了吧。原文的意思是说,如果你在编写模板时可能用于其他地方,而不知道是否已经开启了自动转义的话,可以用escape来确保安全,而且你不必担心如果已经开启了自动转义时会两次转义。

Brad 03-22 06:15

到底哪个对啊?

匿名读者 06-15 08:30

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.

cjyfff 03-08 18:04

1L是正解

对这一段的评论会显示在这里

过滤器参数里的字符串常量的自动转义

Aaron 09-25 11:29

讲的什么意思。

mihello 08-23 09:58

意思是 过滤器 |default:"xxx" 这里"xxx" 是safe过滤。default过滤器就是当变量为空就用"xxx"

对这一段的评论会显示在这里

就像我们前面提到的,过滤器也可以是字符串.

对这一段的评论会显示在这里
{{ data|default:"This is a string literal." }}
mr.liu 08-14 06:03

‘|’管道符号代表使用过滤器,default的意思就是,如果data变量是空,那么就输出default后面的内容

对这一段的评论会显示在这里

所有字符常量没有经过转义就被插入模板,就如同它们都经过了safe过滤。 这是由于字符常量完全由模板作者决定,因此编写模板的时候他们会确保文本的正确性。

mr.liu 08-14 06:10

字符常量指过滤器后面的内容。

mr.liu 08-14 06:40

字符常量是关闭了转义的

对这一段的评论会显示在这里

这意味着你必须这样写

对这一段的评论会显示在这里
{{ data|default:"3 &lt; 2" }}
对这一段的评论会显示在这里

而不是这样

对这一段的评论会显示在这里
{{ data|default:"3 < 2" }}  <-- Bad! Don't do this.
虎头蔓 01-21 03:56

为啥我两种形式都OK结果都是3<2呢?

zmrenwu 07-01 12:09

应该是这样,默认情况下已经自动转义,也就是默认下输出3<2,如果我们手动转义,把<写成&lt,那么模板系统不会再次给你转义回去,所以仍然显示的是<

mr.liu 08-14 06:13

一楼,结果应该看网页源码。

kknd li 12-31 16:59

&lt;在页面渲染的结果就是<

yamei 08-09 10:15

我是这样理解的:字符常量在插入模板的时候不会被转义

对这一段的评论会显示在这里

这点对来自变量本身的数据不起作用。 如果必要,变量内容会自动转义,因为它们不在模板作者的控制下。

对这一段的评论会显示在这里

模板加载的内幕

asuka 06-26 07:18

这标题说得好像有什么不可告人的秘密一样。。。

haobo 12-10 07:37

翻译者真的懂代码么

匿名读者 01-19 20:38

估计是故意开玩笑的,之前译文中还出现了“下面来看这个东东”之类的句子

徐云东 11-29 09:39

例如《深入理解LINUX网络内幕》 感觉内幕这个词经常被程序员使用啊。。。

对这一段的评论会显示在这里

一般说来,你会把模板以文件的方式存储在文件系统中,但是你也可以使用自定义的 template loaders 从其他来源加载模板。

对这一段的评论会显示在这里

Django有两种方法加载模板

对这一段的评论会显示在这里
  • django.template.loader.get_template(template_name)get_template 根据给定的模板名称返回一个已编译的模板(一个 Template 对象)。 如果模板不存在,就触发 TemplateDoesNotExist 的异常。
  • django.template.loader.select_template(template_name_list)select_template 很像 get_template ,不过它是以模板名称的列表作为参数的。 它会返回列表中存在的第一个模板。 如果模板都不存在,将会触发TemplateDoesNotExist异常。
对这一段的评论会显示在这里

正如在第四章中所提到的,默认情况下这些函数使用 TEMPLATE_DIRS 的设置来载入模板。 但是,在内部这些函数可以指定一个模板加载器来完成这些繁重的任务。

haobo 12-10 07:52

这段译得没sei了,第二句应该是"在内部,这些函数其实指定了一个模板加载器去完成这些繁重的任务"

对这一段的评论会显示在这里

一些加载器默认被禁用,但是你可以通过编辑 TEMPLATE_LOADERS 设置来激活它们。 TEMPLATE_LOADERS 应当是一个字符串的元组,其中每个字符串都表示一个模板加载器。 这些模板加载器随Django一起发布。

对这一段的评论会显示在这里

django.template.loaders.filesystem.load_template_source : 这个加载器根据 TEMPLATE_DIRS 的设置从文件系统加载模板。它默认是可用的。

王洁 07-09 08:01

两个默认的加载器在1.4中已经改名 'django.template.loaders.filesystem.Loader', 'django.template.loaders.app_directories.Loader',

cosmos 02-20 07:59

是的!

hekun 08-02 14:37

还有django.template.loaders.eggs.Loader

匿名读者 08-15 08:20

多次一举,你们觉得呢?

mr.liu 08-14 06:51

看到这里明白了,之前在应用中直接创建template,而不用在setting中添加应用中template路径了。

AlexJason 01-10 12:30

在这里多说一句: django.template.loaders.app_directories.load_template_source加载器机制是对于settings文件里面的INSTALLED_APPS中的每个应用, 查找应用下的templates子目录,子目录存在就在加载找到的模板。 另外INSTALLED_APPS应用模板加载有顺序之分。查找最先找到的模板并应用。

对这一段的评论会显示在这里

django.template.loaders.app_directories.load_template_source : 这个加 载器从文件系统上的Django应用中加载模板。 对 INSTALLED_APPS 中的每个应用,这个加载器会查找templates 子目录。 如果这个目录存在,Django就在那里寻找模板。

对这一段的评论会显示在这里

这意味着你可以把模板和你的应用一起保存,从而使得Django应用更容易和默认模板一起发布。 例如,如果 INSTALLED_APPS 包含 ('myproject.polls','myproject.music') ,那么 get_template('foo.html') 会按这个顺序查找模板:

mihello 08-23 13:43

按照官网的例子可以这样 polls/templates/polls/foo.html, music/templates/music/foo.html。get_template('polls/foo.html'),get_template('music/foo.html')。那么即使其他app有同名的模板,或者INSTALLED_APPS 加载顺序问题都不会有影响了。

对这一段的评论会显示在这里
  • /path/to/myproject/polls/templates/foo.html
  • /path/to/myproject/music/templates/foo.html
对这一段的评论会显示在这里

请注意加载器在首次被导入的时候会执行一个优化: 它会缓存一个列表,这个列表包含了 INSTALLED_APPS 中带有 templates 子目录的包。

对这一段的评论会显示在这里

这个加载器默认启用。

对这一段的评论会显示在这里

django.template.loaders.eggs.load_template_source : 这个加载器类似 app_directories ,只不过它从Python eggs而不是文件系统中加载模板。 这个加载器默认被禁用;如果你使用eggs来发布你的应用,那么你就需要启用它。 Python eggs可以将Python代码压缩到一个文件中。

hekun 08-02 14:39

“Python eggs可以将Python代码压缩到一个文件中”==》哪位大虾可以举个例子吗?

taogogo 10-20 02:15

我也不懂,求举个栗子

cjyfff 03-08 18:08

Python eggs are a way of compressing Python code into a single file.

匿名读者 08-03 03:31

i need a egg to eat

对这一段的评论会显示在这里

Django按照 TEMPLATE_LOADERS 设置中的顺序使用模板加载器。 它逐个使用每个加载器直至找到一个匹配的模板。

zlleah 01-16 00:49

“Django按照 TEMPLATE_LOADERS 设置中的顺序使用模板加载器。 它逐个使用每个加载器直至找到一个匹配的模板。”也就是说如果django.template.loaders.filesystem.load_template_source的TEMPLATE_DIRS与django.template.loaders.app_directories.load_template_source的INSTALLED_APPS的templates子目录有同名的模板时,如foo.html,会优先加载TEMPLATE_DIRS中的模板是吗?

sevear 02-16 10:26

回楼上,是的。

mr.liu 08-14 07:07

经测试,确实如此。

对这一段的评论会显示在这里

扩展模板系统

对这一段的评论会显示在这里

既然你已经对模板系统的内幕多了一些了解,让我们来看看如何使用自定义的代码来扩展这个系统吧。

对这一段的评论会显示在这里

绝大部分的模板定制是以自定义标签/过滤器的方式来完成的。 尽管Django模板语言自带了许多内建标签和过滤器,但是你可能还是需要组建你自己的标签和过滤器库来满足你的需要。 幸运的是,定义你自己的功能非常容易。

对这一段的评论会显示在这里

创建一个模板库

对这一段的评论会显示在这里

不管是写自定义标签还是过滤器,第一件要做的事是创建模板库(Django能够导入的基本结构)。

对这一段的评论会显示在这里

创建一个模板库分两步走:

athknal 05-04 19:05

这句话翻译的非常有喜感

对这一段的评论会显示在这里

第一,决定模板库应该放在哪个Django应用下。 如果你通过 manage.py startapp 创建了一个应用,你可以把它放在那里,或者你可以为模板库单独创建一个应用。 我们更推荐使用后者,因为你的filter可能在后来的工程中有用。

对这一段的评论会显示在这里

无论你采用何种方式,请确保把你的应用添加到 INSTALLED_APPS 中。 我们稍后会解释这一点。

Eric 03-22 10:54

在适当的Django应用包里创建一个 templatetags 目录 这句话很重要,包括templatetags的名字和位置都必须按照要求写

Alex Jason 01-11 14:10

在这里为写点东西,后面的同学如果按照例子书写显示自定义过滤器(标签)未注册。我刚刚经历了(我用的django 2.0)。好像django1.9后这个机制改了。不用在INSTALLED_APPS注册了,需要在settings --> TEMPLATES里面的context_processors后面加上 'libraries':{ '自定义过滤器(标签)文件名': 'app名称.templateags.自定义过滤器(标签)文件名', },在模板页面还是通过{% load 自定义过滤器(标签)文件名 %}

对这一段的评论会显示在这里

第二,在适当的Django应用包里创建一个 templatetags 目录。 这个目录应当和 models.pyviews.py 等处于同一层次。 例如:

libo 01-24 06:33

这个适当的包括第一条里提到的“后者”方式创建的app

mihello 08-24 03:28

可以参看官方: https://docs.djangoproject.com/en/1.6/howto/custom-template-tags/

对这一段的评论会显示在这里
books/
    __init__.py
    models.py
    templatetags/
    views.py
对这一段的评论会显示在这里

templatetags 中创建两个空文件: 一个 __init__.py (告诉Python这是 一个包含了Python代码的包)和一个用来存放你自定义的标签/过滤器定义的文件。 第二个文件的名字稍后将用来加载标签。 例如,如果你的自定义标签/过滤器在一个叫作 poll_extras.py 的文件中,你需要在模板中写入如下内容:

qing 02-19 03:46

你需要在模板中写入如下内容: 这个模板指的是什么地方啊?

tom 04-08 07:00

这个地方应该指的是你自己定义的模板啊,比如base.html里面

对这一段的评论会显示在这里
{% load poll_extras %}
Peer Xu 01-05 07:02

如果我的过滤器是放在app里面的,例如apps/templatetags/my_filter.py里面的一个myOwnCut方法呢? 那么我在template里面应该怎么引入?

lastmayday 10-07 08:54

@Peer Xu 直接{% load my_filter %}

对这一段的评论会显示在这里

{% load %} 标签检查 INSTALLED_APPS 中的设置,仅允许加载已安装的Django应用程序中的模板库。 这是一个安全特性;它可以让你在一台电脑上部署很多的模板库的代码,而又不用把它们暴露给每一个Django安装。

xiaohe 09-14 15:49

INSTALLED_APPS 添加了但是{% load aaa %}还是找不到aaa? 修改成 from django import template template.add_to_builtins('app.templatetags.xxx') 这样就能在任何地方用了。

小五 01-30 07:13

真他妈的绕口

weetao 05-15 18:36

没有用load也生效了,怎么回事?

Jason 06-06 15:27

也可以使用register.filter(filter_name)

PythonPig 01-13 15:51

而又不用把它们暴露给每一个Django安装?--->而又不用把它们暴露给每一个Django应用

对这一段的评论会显示在这里

如果你写了一个不和任何特定模型/视图关联的模板库,那么得到一个仅包含 templatetags 包的Django应用程序包是完全正常的。 对于在 templatetags 包中放置多少个模块没有做任何的限制。 需要了解的是:{%load%}语句是通过指定的Python模块名而不是应用名来加载标签/过滤器的。

对这一段的评论会显示在这里

一旦创建了Python模块,你只需根据是要编写过滤器还是标签来相应的编写一些Python代码。

对这一段的评论会显示在这里

作为合法的标签库,模块需要包含一个名为register的模块级变量。这个变量是template.Library的实例,是所有注册标签和过滤器的数据结构。 所以,请在你的模块的顶部插入如下语句:

对这一段的评论会显示在这里
from django import template

register = template.Library()
bobo1732 05-15 08:34

django1.6.2中是: from django.template.base import Labrary register=Library() 感觉这文档还是落后不少了!!

horizonshd 09-16 07:39

在Django-1.11.3中,【from django.template.library import Library】 【register = Library()】

对这一段的评论会显示在这里

注意

对这一段的评论会显示在这里

请阅读Django默认的过滤器和标签的源码,那里有大量的例子。 他们分别为: django/template/defaultfilters.py 和 django/template/defaulttags.py 。django.contrib中的某些应用程序也包含模板库。

血衫非弧 07-13 08:03

这个东西在哪?==! django/template/defaultfilters.py

血衫非弧 07-13 08:56

找到了==! /usr/local/lib/python2.6/dist-packages/django/template

hLongQ 06-05 02:53

使用命令whichis python不就找到了吗?

LeonWong 05-15 07:18

defaultfilter没有 from django import template register = template.Library() 这些代码

mr.liu 08-14 08:11

from django.template.base import Library

mr.liu 08-14 08:11

register = Library()

对这一段的评论会显示在这里

创建 register 变量后,你就可以使用它来创建模板的过滤器和标签了。

对这一段的评论会显示在这里

自定义模板过滤器

对这一段的评论会显示在这里

自定义过滤器就是有一个或两个参数的Python函数:

对这一段的评论会显示在这里
  • (输入)变量的值
  • 参数的值, 可以是默认值或者完全留空
对这一段的评论会显示在这里

例如,在过滤器 {{ var|foo:"bar" }} 中 ,过滤器 foo 会被传入变量 var 和默认参数 bar

burn.py 06-23 07:45

{{ 传入的变量|过滤器:"默认参数" }}

wood 10-15 08:15

静静地失败……不在沉默中爆发,就在沉默中死亡

轻轻的我走了,正如我轻轻的来 10-23 09:25

对这一段的评论会显示在这里

过滤器函数应该总有返回值。 而且不能触发异常,它们都应该静静地失败。 如果出现错误,应该返回一个原始输入或者空字符串,这会更有意义。

对这一段的评论会显示在这里

这里是一些定义过滤器的例子:

对这一段的评论会显示在这里
def cut(value, arg):
    "Removes all values of arg from the given string"
    return value.replace(arg, '')
Jchuan 11-06 12:47

这里的这些双引号的,都是伪代码么?

匿名读者 01-19 21:10

貌似是注释(虽然格式不对)...但不是伪代码,因为return后面的函数已经把功能实现了啊...

zoro 09-28 03:30

"xxx"是函数的字符串文档吧 cut.__doc__会显示其内容

对这一段的评论会显示在这里

下面是一个可以用来去掉变量值空格的过滤器例子:

对这一段的评论会显示在这里
{{ somevariable|cut:" " }}
徐云东 11-29 12:34

注意冒号后面别跟空格!

对这一段的评论会显示在这里

大多数过滤器并不需要参数。 下面的例子把参数从你的函数中拿掉了:

对这一段的评论会显示在这里
def lower(value): # Only one argument.
    "Converts a string into all lowercase"
    return value.lower()
对这一段的评论会显示在这里

当你定义完过滤器后,你需要用 Library 实例来注册它,这样就能通过Django的模板语言来使用了:

钟大天才 07-02 07:40

我发现没有经过注册的过滤器也能够正常使用

atom 07-10 10:20

cut,lower默认有的过滤器,当然不用注册

对这一段的评论会显示在这里
register.filter('cut', cut)
register.filter('lower', lower)
对这一段的评论会显示在这里

Library.filter() 方法需要两个参数:

对这一段的评论会显示在这里
  • 过滤器的名称(一个字串)
  • 过滤器函数本身
对这一段的评论会显示在这里

如果你使用的是Python 2.4或者更新的版本,你可以使用装饰器register.filter()

对这一段的评论会显示在这里
@register.filter(name='cut')
def cut(value, arg):
    return value.replace(arg, '')

@register.filter
def lower(value):
    return value.lower()
heliar 07-23 17:16

第二个装饰器的filter后面要加空括号么

mihello 08-24 05:29

第二个不用加括号

vienan 02-26 12:33

如果你想第二个例子-->如果你像第二个例子

lalala 02-09 10:29

这是一个装饰器,不是函数调用,比如pytest里有@test,就是test的时候会去跑之后定义的函数。你可以看成是一类声明。

lalala 02-09 10:29

就是这样哒。

对这一段的评论会显示在这里

如果你想第二个例子那样不使用 name 参数,那么Django会把函数名当作过滤器的名字。

对这一段的评论会显示在这里

下面是一个完整的模板库的例子,它包含一个 cut 过滤器:

对这一段的评论会显示在这里
from django import template

register = template.Library()

@register.filter(name='cut')
def cut(value, arg):
    return value.replace(arg, '')
sangoly 11-15 08:03

语法糖是以函数名子做参数的

雪雪 10-21 02:43

越来越难了。。

对这一段的评论会显示在这里

自定义模板标签

对这一段的评论会显示在这里

标签要比过滤器复杂些,因为标签几乎能做任何事情。

对这一段的评论会显示在这里

第四章描述了模板系统的两步处理过程: 编译和呈现。 为了自定义一个模板标签,你需要告诉Django当遇到你的标签时怎样进行这个过程。

对这一段的评论会显示在这里

当Django编译一个模板时,它将原始模板分成一个个 节点 。每个节点都是 django.template.Node 的一个实例,并且具备 render() 方法。 于是,一个已编译的模板就是 节点 对象的一个列表。 例如,看看这个模板:

对这一段的评论会显示在这里
Hello, {{ person.name }}.

{% ifequal name.birthday today %}
    Happy birthday!
{% else %}
    Be sure to come back on your birthday
    for a splendid surprise message.
{% endifequal %}
曙光旋冰 06-30 08:26

应该是{ifequal person.birthday today}吧

carrot 07-01 07:18

应该是

DOM文档树 11-20 13:29

对这一段的评论会显示在这里

被编译的模板表现为节点列表的形式:

对这一段的评论会显示在这里
  • 文本节点: "Hello, "
  • 变量节点: person.name
  • 文本节点: ".\n\n"
  • IfEqual节点: name.birthdaytoday
对这一段的评论会显示在这里

当你调用一个已编译模板的 render() 方法时,模板就会用给定的context来调用每个在它的节点列表上的所有节点的 render() 方法。 这些渲染的结果合并起来,形成了模板的输出。 因此,要自定义模板标签,你需要指明原始模板标签如何转换成节点(编译函数)和节点的render()方法完成的功能 。

zxw 07-27 01:02

这里给定的context是否就是编译视图函数最后的返回值中子类赋予的???知道的请给我邮件,谢谢!!2218799487@qq.com

凉茶 12-11 07:13

翻译成解析不好吗?

ifaint 01-04 05:46

解析似乎是parse

mr.liu 08-14 09:05

说得好绕口

对这一段的评论会显示在这里

在下面的章节中,我们将详细解说写一个自定义标签时的所有步骤。

对这一段的评论会显示在这里

编写编译函数

horizonshd 09-16 14:16

这部分有一点抽象啊,要是再具体一点就好了

对这一段的评论会显示在这里

当遇到一个模板标签(template tag)时,模板解析器就会把标签包含的内容,以及模板解析器自己作为参数调用一个python函数。 这个函数负责返回一个和当前模板标签内容相对应的节点(Node)的实例。

对这一段的评论会显示在这里

例如,写一个显示当前日期的模板标签:{% current_time %}。该标签会根据参数指定的 strftime 格式(参见:http://www.djangoproject.com/r/python/strftime/)显示当前时间。首先确定标签的语法是个好主意。 在这个例子里,标签应该这样使用:

haobo 12-12 09:03

这个URL的内容为404

对这一段的评论会显示在这里
<p>The time is {% current_time "%Y-%m-%d %I:%M %p" %}.</p>
haobo 12-12 08:57

这是什么意思,在模版里这样写?为什么还要后面""中的内容?

zoro 09-28 04:01

参数,指定输出格式

对这一段的评论会显示在这里

注意

对这一段的评论会显示在这里

没错, 这个模板标签是多余的,Django默认的 {% now %} 用更简单的语法完成了同样的工作。 这个模板标签在这里只是作为一个例子。

horizonshd 09-16 13:32

具体怎么用啊?这儿的 now 是自己定义的变量吗?

纸上往事 08-06 09:36

now就是在django中获取的当前的时间,now = datetime.datetime.now(),然后渲染模板中的变量。

对这一段的评论会显示在这里

这个函数的分析器会获取参数并创建一个 Node 对象:

对这一段的评论会显示在这里
from django import template

register = template.Library()

def do_current_time(parser, token):
    try:
        # split_contents() knows not to split quoted strings.
        tag_name, format_string = token.split_contents()
    except ValueError:
        msg = '%r tag requires a single argument' % token.split_contents()[0]
        raise template.TemplateSyntaxError(msg)
    return CurrentTimeNode(format_string[1:-1])
呜呜祖拉 07-21 02:36

将current_time "%Y-%m-%d %I:%M %p"分割成两个字符串,current_time和"%Y-%m-%d %I:%M %p"分别赋值给tag_name, format_string这两个变量。 这个函数返回一个CurrentTimeNode类的对象,初始化参数为:(format_string[1:-1]),其实就是%Y-%m-%d %I:%M %p了。

Black Glory 06-26 09:54

register = template.Library() 是做什么的?

匿名读者 07-11 08:11

register = template.Library()用于注册自定义的tag到模板里

avyou 07-23 06:22

contents.split() 是哪里来的? token.contents 为什么是原始内容字符串?

avyou 07-23 06:22

split_contents() 是哪里来的? token.contents 为什么是原始内容字符串?

heliar 07-23 15:35

这里最后直接返回的是一个传入format_string[1:-1]参数的CurrentTimeNode的实例吧。。

latyas 01-02 18:02

等价的一个用法 token.contents.split(' ', 1)

ken 12-15 08:13

这个模板是用python写的?不是html?那如果html和python都需要呢?

对这一段的评论会显示在这里

这里需要说明的地方很多:

对这一段的评论会显示在这里
  • 每个标签编译函数有两个参数,parsertokenparser是模板解析器对象。 我们在这个例子中并不使用它。 token是正在被解析的语句。
  • token.contents 是包含有标签原始内容的字符串。 在我们的例子中,它是 'current_time "%Y-%m-%d %I:%M %p"'
  • token.split_contents() 方法按空格拆分参数同时保证引号中的字符串不拆分。 应该避免使用 token.contents.split() (仅使用Python的标准字符串拆分)。 它不够健壮,因为它只是简单的按照所有空格进行拆分,包括那些引号引起来的字符串中的空格。
  • 这个函数可以抛出 django.template.TemplateSyntaxError ,这个异常提供所有语法错误的有用信息。
  • 不要把标签名称硬编码在你的错误信息中,因为这样会把标签名称和你的函数耦合在一起。 token.split_contents()[0]总是记录标签的名字,就算标签没有任何参数。
  • 这个函数返回一个 CurrentTimeNode (稍后我们将创建它),它包含了节点需要知道的关于这个标签的全部信息。 在这个例子中,它只是传递了参数 "%Y-%m-%d %I:%M %p" 。模板标签开头和结尾的引号使用 format_string[1:-1] 除去。
  • 模板标签编译函数 必须 返回一个 Node 子类,返回其它值都是错的。
zxw 07-27 00:55

这里返回的子类应该就是一个字典,赋值给后面的render()中的context参数了

sanyang 01-03 09:29

这里的 token.split_contents()[0] 可以用 tag_name代替

heliar 07-23 15:10

楼上的,好像不可以吧,如果赋值错误的话,tag_name是空值。

愚者自知 10-28 08:46

为什么是format_string[1:-1]而不是token [1:-1]

对这一段的评论会显示在这里

编写模板节点

对这一段的评论会显示在这里

编写自定义标签的第二步就是定义一个拥有 render() 方法的 Node 子类。 继续前面的例子,我们需要定义 CurrentTimeNode

对这一段的评论会显示在这里
import datetime

class CurrentTimeNode(template.Node):
    def __init__(self, format_string):
        self.format_string = str(format_string)

    def render(self, context):
        now = datetime.datetime.now()
        return now.strftime(self.format_string)
呜呜祖拉 07-21 02:48

定义一个类CurrentTimeNode,这个类是从template.Node派生过来的子类。传入的初始化参数相当于上文的format_string[1:-1]。CurrentTimeNode类额对象可以调用render()方法。

呜呜祖拉 07-21 03:18

render函数的context参数由哪里得到呢?大佬帮忙解下惑吧!

roger 11-23 10:02

回楼上,render的context参数是你在你的view函数里面调用render的时候加上去的,或者你用render_to_response的时候(这个还是会调用render)

匿名读者 08-19 05:39

我是这样猜测的,这里的context参数指的是,如果你定义的标签里面有变量的话,context很可能以所有变量名的字典,然后你实例化模版也就是调用模版的render()方法的时候你需要传递一个字典作为参数,Django的模版系统会根据你的每个标签render方法的context参数的键值查找你传递的字典里面的同名键值对应的值,然后调用你的Node的render()

mr.liu 08-14 09:58

“当你调用一个已编译模板的 render() 方法时,模板就会用给定的context来调用每个在它的节点列表上的所有节点的 render() 方法。 这些渲染的结果合并起来,形成了模板的输出。” 以上那段话是前文说的,这context应该是views方法中传递的context吧!

对这一段的评论会显示在这里

这两个函数( __init__()render() )与模板处理中的两步(编译与渲染)直接对应。 这样,初始化函数仅仅需要存储后面要用到的格式字符串,而 render() 函数才做真正的工作。

对这一段的评论会显示在这里

与模板过滤器一样,这些渲染函数应该静静地捕获错误,而不是抛出错误。 模板标签只允许在编译的时候抛出错误。

对这一段的评论会显示在这里

注册标签

对这一段的评论会显示在这里

最后,你需要用你模块的Library 实例注册这个标签。 注册自定义标签与注册自定义过滤器非常类似(如前文所述)。 只需实例化一个 template.Library 实例然后调用它的 tag() 方法。 例如:

对这一段的评论会显示在这里
register.tag('current_time', do_current_time)
呜呜祖拉 07-21 02:48

定义一个类CurrentTimeNode,这个类是从template.Node派生过来的子类。传入的初始化参数相当于上文的format_string[1:-1]。CurrentTimeNode类额对象可以调用render()方法。

呜呜祖拉 07-21 02:50

理解成一旦碰到current_time这个标签名,就调用do_current_time方法

hekun 08-03 15:13

是否if,for就是用这种方法实现的呢?

heliar 07-23 18:16

这个注册标签的作用是什么?为了让django在模板解析的时候能解析到这个tag么

对这一段的评论会显示在这里

tag() 方法需要两个参数:

对这一段的评论会显示在这里
  • 模板标签的名字(字符串)。
  • 编译函数。
对这一段的评论会显示在这里

和注册过滤器类似,也可以在Python2.4及其以上版本中使用 register.tag装饰器:

对这一段的评论会显示在这里
@register.tag(name="current_time")
def do_current_time(parser, token):
    # ...

@register.tag
def shout(parser, token):
    # ...
对这一段的评论会显示在这里

如果你像在第二个例子中那样忽略 name 参数的话,Django会使用函数名称作为标签名称。

对这一段的评论会显示在这里

在上下文中设置变量

zxw 07-27 01:04

应该是在context中设置变量!!!

匿名读者 08-19 05:51

我怀疑是不是直接拷到google里面翻译的,这种专有名词不用直译吧,搞得人都糊涂了

对这一段的评论会显示在这里

前一节的例子只是简单的返回一个值。 很多时候设置一个模板变量而非返回值也很有用。 那样,模板作者就只能使用你的模板标签所设置的变量。

对这一段的评论会显示在这里

要在上下文中设置变量,在 render() 函数的context对象上使用字典赋值。 这里是一个修改过的 CurrentTimeNode ,其中设定了一个模板变量 current_time ,并没有返回它:

对这一段的评论会显示在这里
class CurrentTimeNode2(template.Node):
    def __init__(self, format_string):
        self.format_string = str(format_string)

    def render(self, context):
        now = datetime.datetime.now()
        context['current_time'] = now.strftime(self.format_string)
        return ''
heliar 07-23 16:53

这里定义的变量是怎么传出去的。。。就是那个context['current_time']

Yufogchan 03-30 13:04

回复楼上,通过render返回的

horizonshd 09-18 01:48

这里的context是在什么时候定义的?相当于一个全局变量吗?

horizonshd 09-18 02:01

def render(self, context):中的contex是默认的参数吗?

Alex Jason 01-12 14:23

ls这里的context是上下文处理器,接收当前的 HttpRequest 作为参数,并返回一个字典(包含node节点和节点所对应的值)渲染模板。自定义的方法是用来解析模板的。楼上可能看不到了。给下面的同学讲一下我的理解思路。

nickduan 03-16 15:33

这东西怎么使用?

对这一段的评论会显示在这里

(我们把创建函数do_current_time2和注册给current_time2模板标签的工作留作读者练习。)

对这一段的评论会显示在这里

注意 render() 返回了一个空字符串。 render() 应当总是返回一个字符串,所以如果模板标签只是要设置变量, render() 就应该返回一个空字符串。

对这一段的评论会显示在这里

你应该这样使用这个新版本的标签:

对这一段的评论会显示在这里
{% current_time2 "%Y-%M-%d %I:%M %p" %}
<p>The time is {{ current_time }}.</p>
horizonshd 09-19 05:07

【<p>The time is {{ current_time }}.</p>】能这样用吗?为什么不传递格式化字符串参数呢?前面定义的编译函数中不是需要这个参数吗?

对这一段的评论会显示在这里

但是 CurrentTimeNode2 有一个问题: 变量名 current_time 是硬编码的。 这意味着你必须确定你的模板在其它任何地方都不使用 {{ current_time }} ,因为 {% current_time2 %} 会盲目的覆盖该变量的值。

对这一段的评论会显示在这里

一种更简洁的方案是由模板标签来指定需要设定的变量的名称,就像这样:

对这一段的评论会显示在这里
{% get_current_time "%Y-%M-%d %I:%M %p" as my_current_time %}
<p>The current time is {{ my_current_time }}.</p>
phonty 04-13 03:44

get ... as ... django真不是给程序员设计的。

phonty 04-13 03:48

看错了没get ... as ...

phonty 04-13 03:49

看错了没get,只有 as 。不要误导了大家。

张超群 06-29 05:35

此处将do_current_time 注册给get_current_time2模板标签 register.tag{'get_current_time',do_current_time}

wolfy 07-22 13:59

时间的格式里的月应该是%m而不是%M

YANG WANG 09-22 11:28

对的 Ymd 一楼别瞎说 多看几遍再评论~~

对这一段的评论会显示在这里

为此,你需要重构编译函数和 Node 类,如下所示:

对这一段的评论会显示在这里
import re

class CurrentTimeNode3(template.Node):
    def __init__(self, format_string, var_name):
        self.format_string = str(format_string)
        self.var_name = var_name

    def render(self, context):
        now = datetime.datetime.now()
        context[self.var_name] = now.strftime(self.format_string)
        return ''

def do_current_time(parser, token):
    # This version uses a regular expression to parse tag contents.
    try:
        # Splitting by None == splitting by spaces.
        tag_name, arg = token.contents.split(None, 1)
    except ValueError:
        msg = '%r tag requires arguments' % token.contents[0]
        raise template.TemplateSyntaxError(msg)

    m = re.search(r'(.*?) as (\w+)', arg)
    if m:
        fmt, var_name = m.groups()
    else:
        msg = '%r tag had invalid arguments' % tag_name
        raise template.TemplateSyntaxError(msg)

    if not (fmt[0] == fmt[-1] and fmt[0] in ('"', "'")):
        msg = "%r tag's argument should be in quotes" % tag_name
        raise template.TemplateSyntaxError(msg)

    return CurrentTimeNode3(fmt[1:-1], var_name)
dawn 07-16 16:44

没怎么看明白这段代码

ooxx 02-01 09:37

就是在编译的时候把as的后面的名字,传到渲染时候的上下文中作为变量

rcompass 10-12 09:37

do_current_time函数中, msg = '%r tag requires arguments' % token.contents[0] 应改为 msg = '%r tag requires arguments' % token.contents.split(None, 1)[0]

asuka 06-26 08:52

我来分析一下好了。 CurrentTimeNode3类比较简单,就不说了。 do_current_time函数里面,第一部分比较简单,用split将token分解成两部分。这里split()的参数None表示按照空格分割,1表示分割1次。 第二部分是用正则进行匹配,m.groups()将以元组形式返回匹配的(.*?)和(\w+)中的内容。 最后一部分判断fmt的开头与结尾是否同时为双引号或者单引号,以确保fmt的内容是在引号中的。 以上。

www 02-27 02:07

楼上解释的好,受教了

对这一段的评论会显示在这里

现在 do_current_time() 把格式字符串和变量名传递给 CurrentTimeNode3

对这一段的评论会显示在这里

分析直至另一个模板标签

mac 12-13 07:38

翻译的什么啊。。

mr.liu 08-27 11:53

也就是说,这类标签,有开始于结束标志,从开始解析到结束

匿名读者 07-02 01:17

翻译成块级模板标签,如何

对这一段的评论会显示在这里

模板标签可以像包含其它标签的块一样工作(想想 {% if %}{% for %} 等)。 要创建一个这样的模板标签,在你的编译函数中使用 parser.parse()

对这一段的评论会显示在这里

标准的 {% comment %} 标签是这样实现的:

对这一段的评论会显示在这里
def do_comment(parser, token):
    nodelist = parser.parse(('endcomment',))
    parser.delete_first_token()
    return CommentNode()

class CommentNode(template.Node):
    def render(self, context):
        return ''
对这一段的评论会显示在这里

parser.parse() 接收一个包含了需要分析的模板标签名的元组作为参数。 它返回一个django.template.NodeList实例,它是一个包含了所有Node对象的列表,这些对象是解析器在解析到任一元组中指定的标签之前遇到的内容.

对这一段的评论会显示在这里

因此在前面的例子中, nodelist 是在 {% comment %}{% endcomment %} 之间所有节点的列表,不包括 {% comment %}{% endcomment %} 自身。

vayn 03-05 05:16

“之间所有节点的列表” 应该是之间吧

YG 07-04 13:32

@vayn 确实是之前,在这个例子中不包含'endcomment',包含 comment

对这一段的评论会显示在这里

parser.parse() 被调用之后,分析器还没有清除 {% endcomment %} 标签,因此代码需要显式地调用 parser.delete_first_token() 来防止该标签被处理两次。

匿名读者 08-19 05:55

这里没看懂,被处理两次是什么意思

jf 03-27 02:16

这里应该是清除{% comment %}吧

mac 12-13 07:42

这里为什么不 自动去掉第一条标签 而是要手动啊

ifaint 01-04 09:28

@jf 如果原文没错,应该是指解析到目前为止,token指向的是{{% endcomment %},此即first_token()

zbli 08-17 08:46

楼上正解

horizonshd 09-18 02:37

@ifaint 说的应该没错,在parse的过程中,就像有一个“指针”随着解析逐渐向后续的节点移动。因此, parser.delete_first_token()应该就是跳过解析到最后碰到的{% endcomment %}

Alex Jason 01-17 14:07

还是说一下自己的理解吧。看这里的教程把自己理解的都说了下,为后面觉得对自己理解有用的朋友指下路吧!“不包括 {% comment %} 和 {% endcomment %} 自身”这句话就说明了parse.parse()是清除不到{% endcomment %}标签的。源码是这表标注的。别被delet_first_token的first误解了。

Alex Jason 01-17 14:09

对这一段的评论会显示在这里

之后 CommentNode.render() 只是简单地返回一个空字符串。 在 {% comment %}{% endcomment %} 之间的所有内容都被忽略。

对这一段的评论会显示在这里

分析直至另外一个模板标签并保存内容

对这一段的评论会显示在这里

在前一个例子中, do_comment() 抛弃了{% comment %}{% endcomment %} 之间的所有内容。当然也可以修改和利用下标签之间的这些内容。

对这一段的评论会显示在这里

例如,这个自定义模板标签{% upper %},它会把它自己和{% endupper %}之间的内容变成大写:

对这一段的评论会显示在这里
{% upper %}
    This will appear in uppercase, {{ user_name }}.
{% endupper %}
对这一段的评论会显示在这里

就像前面的例子一样,我们将使用 parser.parse() 。这次,我们将产生的 nodelist 传递给 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()
songchen 03-02 06:04

要注册 要重启服务

对这一段的评论会显示在这里

这里唯一的一个新概念是 UpperNode.render() 中的 self.nodelist.render(context) 。它对节点列表中的每个 Node 简单的调用 render()

对这一段的评论会显示在这里

更多的复杂渲染示例请查看 django/template/defaulttags.py 中的 {% if %}{% for %}{% ifequal %}{% ifchanged %} 的代码。

对这一段的评论会显示在这里

简单标签的快捷方式

对这一段的评论会显示在这里

许多模板标签接收单一的字符串参数或者一个模板变量引用,然后独立地根据输入变量和一些其它外部信息进行处理并返回一个字符串。 例如,我们先前写的current_time标签就是这样一个例子。 我们给定了一个格式化字符串,然后它返回一个字符串形式的时间。

对这一段的评论会显示在这里

为了简化这类标签,Django提供了一个帮助函数simple_tag。这个函数是django.template.Library的一个方法,它接受一个只有一个参数的函数作参数,把它包装在render函数和之前提及过的其他的必要单位中,然后通过模板系统注册标签。

对这一段的评论会显示在这里

我们之前的的 current_time 函数于是可以写成这样:

对这一段的评论会显示在这里
def current_time(format_string):
    try:
        return datetime.datetime.now().strftime(str(format_string))
    except UnicodeEncodeError:
        return ''

register.simple_tag(current_time)
mihello 08-29 08:09

测试通过

对这一段的评论会显示在这里

在Python 2.4中,也可以使用装饰器语法:

对这一段的评论会显示在这里
@register.simple_tag
def current_time(token):
    # ...
对这一段的评论会显示在这里

有关 simple_tag 辅助函数,需要注意下面一些事情:

对这一段的评论会显示在这里
  • 传递给我们的函数的只有(单个)参数。
  • 在我们的函数被调用的时候,检查必需参数个数的工作已经完成了,所以我们不需要再做这个工作。
  • 参数两边的引号(如果有的话)已经被截掉了,所以我们会接收到一个普通Unicode字符串。
对这一段的评论会显示在这里

包含标签

对这一段的评论会显示在这里

另外一类常用的模板标签是通过渲染 其他 模板显示数据的。 比如说,Django的后台管理界面,它使用了自定义的模板标签来显示新增/编辑表单页面下部的按钮。 那些按钮看起来总是一样的,但是链接却随着所编辑的对象的不同而改变。 这就是一个使用小模板很好的例子,这些小模板就是当前对象的详细信息。

对这一段的评论会显示在这里

这些排序标签被称为 包含标签 。如何写包含标签最好通过举例来说明。 让我们来写一个能够产生指定作者对象的书籍清单的标签。 我们将这样利用标签:

对这一段的评论会显示在这里
{% books_for_author author %}
horizonshd 09-18 03:04

{% books_for_author author %}会自动调用下面的 books_for_author(author)编译函数,并自动将模板中的author作为参数传入吗?

对这一段的评论会显示在这里

结果将会像下面这样:

对这一段的评论会显示在这里
<ul>
    <li>The Cat In The Hat</li>
    <li>Hop On Pop</li>
    <li>Green Eggs And Ham</li>
</ul>
对这一段的评论会显示在这里

首先,我们定义一个函数,通过给定的参数生成一个字典形式的结果。 需要注意的是,我们只需要返回字典类型的结果就行了,不需要返回更复杂的东西。 这将被用来作为模板片段的内容:

对这一段的评论会显示在这里
def books_for_author(author):
    books = Book.objects.filter(authors__id=author.id)
    return {'books': books}
qigan 01-11 09:38

出现maximum recursion depth exceeded while calling a Python object

Jack 06-15 11:43

author 变量应该传什么?后面的author.id表示他需要一个author model, 怎么传这个model?

mihello 08-29 08:49

变量author是从{% books_for_author author %} 传过来

mihello 08-29 09:56

from books.models. import Author, Book {% books_for_author 'mihello' %} @register.inclusion_tag('book_snippet.html') def books_for_author(name): author = Author.objects.get(first_name=name) books = Book.objects.filter(authors__id=author.id) return {'books': books}

mihello 08-29 09:56

from books.models. import Author, Book \n @register.inclusion_tag('book_snippet.html') def books_for_author(name): author = Author.objects.get(first_name=name) books = Book.objects.filter(authors__id=author.id) return {'books': books}

mihello 08-29 09:57

居然无法换行!!!

mr.liu 08-26 08:50

提示“maximum recursion depth exceeded while calling a Python object”,有解决方法么

mr.liu 08-26 09:23

如果后面的读者出现这种错误“maximum recursion depth exceeded while calling a Python object”,请看这个链接“http://blog.163.com/lyjlyj517@126/blog/static/16686350120133265235891/” 在一个模板里引入标签,为标签传入变量,然后渲染所制定的模板。

对这一段的评论会显示在这里

接下来,我们创建用于渲染标签输出的模板。 在我们的例子中,模板很简单:

对这一段的评论会显示在这里
<ul>
{% for book in books %}
    <li>{{ book.title }}</li>
{% endfor %}
</ul>
对这一段的评论会显示在这里

最后,我们通过对一个 Library 对象使用 inclusion_tag() 方法来创建并注册这个包含标签。

对这一段的评论会显示在这里

在我们的例子中,如果先前的模板在 polls/result_snippet.html 文件中,那么我们这样注册标签:

dawn 07-16 17:13

没明白

roger 11-23 11:01

简单的说,这个inclusion tag的作用就是插入一段根据context内容渲染过的html

garvenshen 03-08 02:26

写错了吧? 应该是 polls/book_snippet.html

iamzzz 03-10 12:06

错了吧。原文是“Following our example, if the preceding template is in a file called book_snippet.html, we register the tag like this:”怎么翻译出polls来了。。。

vienan 02-26 13:00

在我们的示例中,如果前面的模板文件中被称为book_snippet.html,那么我们这样注册标签:

mr.liu 08-26 08:58

提示“maximum recursion depth exceeded while calling a Python object”,有解决方法么

mr.liu 08-26 09:23

如果后面的读者出现这种错误“maximum recursion depth exceeded while calling a Python object”,请看这个链接“http://blog.163.com/lyjlyj517@126/blog/static/16686350120133265235891/” 在一个模板里引入标签,为标签传入变量,然后渲染所制定的模板。

horizonshd 09-18 03:07

这里还是比较好理解的

对这一段的评论会显示在这里
register.inclusion_tag('book_snippet.html')(books_for_author)
对这一段的评论会显示在这里

Python 2.4装饰器语法也能正常工作,所以我们可以这样写:

对这一段的评论会显示在这里
@register.inclusion_tag('book_snippet.html')
def books_for_author(author):
    # ...
对这一段的评论会显示在这里

有时候,你的包含标签需要访问父模板的context。 为了解决这个问题,Django为包含标签提供了一个 takes_context 选项。 如果你在创建模板标签时,指明了这个选项,这个标签就不需要参数,并且下面的Python函数会带一个参数: 就是当这个标签被调用时的模板context。

dasdad 05-22 08:35

这你麻痹是人说的话吗?

zy_sunshine 07-04 12:05

这里访问父模板中的context有必要吗? 子模板继承父模板,在给子模板的context中不是一定要包含父模板中需要的{{}}变量吗? 那位大哥知道给我一个答案,呵呵 zy.netsec <AT> gmail <dot> com

mihello 08-30 05:33

搞清了,这里的父模板指的是调用{% jump_link %}所在的文件,比如你在 hello.html里面 用了 {% jump_link %} 那么 'link': context['home_link'], 'title': context['home_title'], 里面的 home_link & home_title 就是从hello.html获取

www 02-28 00:21

楼上正解,@一楼:人家给你免费翻译已经很好了,你还骂人。你觉得作者说的不是人话你看英文版去,别在这乱叫。

zbli 08-17 09:10

顶楼上

mr.liu 08-26 08:58

提示“maximum recursion depth exceeded while calling a Python object”,有解决方法么

mr.liu 08-26 09:48

如果后面的读者出现这种错误“maximum recursion depth exceeded while calling a Python object”,请看这个链接“http://blog.163.com/lyjlyj517@126/blog/static/16686350120133265235891/” 在一个模板里引入标签,为标签传入变量,然后渲染所制定的模板。

匿名读者 04-15 07:56

美剧鸟

对这一段的评论会显示在这里

例如,你正在写一个包含标签,该标签包含有指向主页的 home_linkhome_title 变量。 Python函数会像这样:

对这一段的评论会显示在这里
@register.inclusion_tag('link.html', takes_context=True)
def jump_link(context):
    return {
        'link': context['home_link'],
        'title': context['home_title'],
    }
对这一段的评论会显示在这里

(注意函数的第一个参数 必须context 。)

对这一段的评论会显示在这里

模板 link.html 可能包含下面的东西:

对这一段的评论会显示在这里
Jump directly to <a href="{{ link }}">{{ title }}</a>.
对这一段的评论会显示在这里

然后您想使用自定义标签时,就可以加载它的库,然后不带参数地调用它,就像这样:

对这一段的评论会显示在这里
{% jump_link %}
对这一段的评论会显示在这里

编写自定义模板加载器

对这一段的评论会显示在这里

Djangos 内置的模板加载器(在先前的模板加载内幕章节有叙述)通常会满足你的所有的模板加载需求,但是如果你有特殊的加载需求的话,编写自己的模板加载器也会相当简单。 比如:你可以从数据库中,或者利用Python的绑定直接从Subversion库中,更或者从一个ZIP文档中加载模板。

Mark Wen 09-03 01:33

第一个单词 Djangos 应改为 Django

对这一段的评论会显示在这里

模板加载器,也就是 TEMPLATE_LOADERS 中的每一项,都要能被下面这个接口调用:

horizonshd 09-18 03:35

在Django-1.11.3中,找不到该项配置

对这一段的评论会显示在这里
load_template_source(template_name, template_dirs=None)
对这一段的评论会显示在这里

参数 template_name 是所加载模板的名称 (和传递给 loader.get_template() 或者 loader.select_template() 一样), 而 template_dirs 是一个可选的代替TEMPLATE_DIRS的搜索目录列表。

对这一段的评论会显示在这里

如果加载器能够成功加载一个模板, 它应当返回一个元组: (template_source, template_path) 。在这里的 template_source 就是将被模板引擎编译的的模板字符串,而 template_path 是被加载的模板的路径。 由于那个路径可能会出于调试目的显示给用户,因此它应当很快的指明模板从哪里加载。

对这一段的评论会显示在这里

如果加载器加载模板失败,那么就会触发 django.template.TemplateDoesNotExist 异常。

对这一段的评论会显示在这里

每个加载函数都应该有一个名为 is_usable 的函数属性。 这个属性是一个布尔值,用于告知模板引擎这个加载器是否在当前安装的Python中可用。 例如,如果 pkg_resources 模块没有安装的话,eggs加载器(它能够从python eggs中加载模板)就应该把 is_usable 设为 False ,因为必须通过 pkg_resources 才能从eggs中读取数据。

对这一段的评论会显示在这里

一个例子可以清晰地阐明一切。 这儿是一个模板加载函数,它可以从ZIP文件中加载模板。 它使用了自定义的设置 TEMPLATE_ZIP_FILES 来取代了 TEMPLATE_DIRS 用作查找路径,并且它假设在此路径上的每一个文件都是包含模板的ZIP文件:

对这一段的评论会显示在这里
from django.conf import settings
from django.template import TemplateDoesNotExist
import zipfile

def load_template_source(template_name, template_dirs=None):
    "Template loader that loads templates from a ZIP file."

    template_zipfiles = getattr(settings, "TEMPLATE_ZIP_FILES", [])

    # Try each ZIP file in TEMPLATE_ZIP_FILES.
    for fname in template_zipfiles:
        try:
            z = zipfile.ZipFile(fname)
            source = z.read(template_name)
        except (IOError, KeyError):
            continue
        z.close()
        # We found a template, so return the source.
        template_path = "%s:%s" % (fname, template_name)
        return (source, template_path)

    # If we reach here, the template couldn't be loaded
    raise TemplateDoesNotExist(template_name)

# This loader is always usable (since zipfile is included with Python)
load_template_source.is_usable = True
heliar 07-24 03:01

想问下菊苣们这个getattr函数第三个参数是干啥用的,文档里只看到传入两个参数。。

heliar 07-24 03:02

额,理解了,是自己指定的缺省值

匿名读者 12-11 00:58

getattr 如果 settings 中有 属性 “TEMPLATE_ZIP_FILES”则打印,否则打印“[]”

对这一段的评论会显示在这里

我们要想使用它,还差最后一步,就是把它加入到 TEMPLATE_LOADERS 。 如果我们将这个代码放入一个叫mysite.zip_loader的包中,那么我们要把mysite.zip_loader.load_template_source加到TEMPLATE_LOADERS中。

zxw 07-27 01:49

上述内容如果不怎么明白,建议看下这个,讲的不错 http://xiao80xiao.iteye.com/blog/519394

freesquirrel 10-20 16:08

自定义加载器这块没懂。。。郁闷

对这一段的评论会显示在这里

配置独立模式下的模板系统

对这一段的评论会显示在这里

注意:

对这一段的评论会显示在这里

这部分只针对于对在其他应用中使用模版系统作为输出组件感兴趣的人。 如果你是在Django应用中使用模版系统,请略过此部分。

对这一段的评论会显示在这里

通常,Django会从它的默认配置文件和由 DJANGO_SETTINGS_MODULE 环境变量所指定的模块中加载它需要的所有配置信息。 (这点在第四章的”特殊的Python命令提示行”一节解释过。)但是当你想在非Django应用中使用模版系统的时候,采用环境变量并不方便,因为你可能更想同其余的应用一起配置你的模板系统,而不是处理配置文件并通过环境变量指向他们。

对这一段的评论会显示在这里

为了解决这个问题,你需要使用附录D中所描述的手动配置选项。概括的说,你需要导入正确的模板中的片段,然后在你访问任一个模板函数之前,首先用你想指定的配置访问Django.conf.settings.configure()。

Wing Shine 11-29 07:52

...这翻译太难懂了。总的来说,在你调用任何django的模板功能(函数)之前,你都必须先(在配置文件中)导入关于模板正确的配置片段,然后调用django.conf.setting.configure()函数来使你(希望)指定的配置生效。

匿名读者 10-14 15:06

完全晕了

对这一段的评论会显示在这里

你可能会考虑至少要设置 TEMPLATE_DIRS (如果你打算使用模板加载器), DEFAULT_CHARSET (尽管默认的 utf-8 编码相当好用),以及 TEMPLATE_DEBUG 。所有可用的选项在附录D中都有详细描述,所有以 TEMPLATE_ 开头的选项都可能使你感兴趣。

对这一段的评论会显示在这里

接下来做什么?

对这一段的评论会显示在这里

延续本章的高级话题,下一章 会继续讨论Django模版的高级用法。

BYVoid 11-19 14:59

應該是下一章模型吧?

vayn 03-05 07:21

回楼上,按照下章标题《数据模型高级进阶》来说,的确应该是“Django模型的高级用法”

ode2free 05-09 20:30

模型高级进阶

zagfai 08-02 14:44

好困啊!!

ode2free 05-22 14:03

自定义模板标签都略过了...感觉基本不用

scarl 08-14 01:38

同感……我也是略过了自定义标签

J_z10 09-03 07:45

这一章,看得云里雾里

cjf 03-30 00:52

看不下去了 没例子

Damien 12-04 07:32

吐血..下一章还是高级模板

nickduan 03-16 15:55

前后端分离的时代,模板用处大吗?

wan ze 01-31 10:20

也想过楼上的问题

对这一段的评论会显示在这里