Django 学习笔记2 模板

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

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

模板标签被 {% 和 %} 包围:

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

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

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

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


模版变量(variable) : {{person_name}}

 模版系统引用变量{{ foo.bar }}时的,句点查找规则 

 字典类型查找 (比如 foo["bar"] )

 属性查找 (比如 foo.bar )

 方法调用 (比如 foo.bar() ) #仅在方法无需传入参数时,其调用才有效。 否则将会转移到下一个查找类型

 列表类型索引查找 (比如 foo[bar] )

 注意:句点查找可以多级深度嵌套,例:poson.name.upper,如果一个变量不存在,模板系统会把它展示为空字符串

避免方法被模版调用 

   def delete(self):
    delete.alters_data = True #方法的alters_data属性可避免方法被模板调用

模版标签(template tag):{%ifordered_warranty%} .......{% endif %} 

 if...and....or....not... 注:and 和 or不能同用,不支持用圆括号,不支持elif,一定要用 endif关闭

   for..in...[reversed]....endfor, ,一定要用 endfor关闭,reversed(反向迭代)是可选的,不支持break,continue

   forloop.counter 总是一个表示当前循环的执行次数的整数计数器,从1始

   orloop.counter0 类似于 forloop.counter ,但是它是从0计数的。

 forloop.revcounter 是表示循环中剩余项的整型变量。forloop.revcounter0 =forloop.revcounter -1

   forloop.first 是一个布尔值,如果该迭代是第一次执行,它为真

 forloop.parentloop 是一个指向当前循环的上一级循环的 forloop 对象的引用(在嵌套循环的情况下)

 ifequal/ifnotequal....endifequal 比较两个变量的值是相等/不等 支持可选的 {%else%},只有模板变量,字符串,整数,小数可作为 参数


模版系统示例

>>> from django.template import Context, Template

>>> t = Template('My name is {{ name }}.')#也可引用对象方法(无法传参)、属性,使用列表索引等


>>> c = Context({'name': 'Stephane'})# Context 类,可选的参数:一个字典类型映射变量和它们的值(值=任意对象)


# Context 类可使用字典语法修改


>>> t.render(c)#返回的值是一个Unicode对象,不是普通的Python字符串t.render()输出是不经python解释的源码,print t.render()输出的是经python解释过的效果。

u'My name is Stephane.'

模版注释:

This is a

{# this is not a comment #} #只能单行

{% comment %}#可多行

This is a

multi-line comment.


{% endcomment %}


test.





过滤器(filter) :{{ ship_date|date:"F j, Y" }} #便捷的转换变量输出格式方式 将变量ship_date传递给date过滤器,


                   同时指定参数”F j,Y”。date根据参数进行格式输出。过滤器用管道符(|)来调用


{{ name|lower }}#显示的内容是变量name被过滤器lower处理后的结果,它功能是转换文本为小写。


{{ my_list|first|upper }}#查找列表的第一个元素并将其转化为大写


{{ bio|truncatewords:"30" }}显示变量 bio 的前30个词


addslashes : 添加反斜杠到任何反斜杠、单引号或者双引号前面。这在处理包含JavaScript的文本时是非常有用的


{{ pub_date|date:"F j, Y" }}date : 按指定的格式字符串参数格式化 date 或者 datetime 对象


length : 返回变量的长度,任何知道怎么测定长度的Python 对象(也就是说有 __len__() 方法的对象)





使用模版文件 #get_template可以使用子目录


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')#在文件系统中找出模板,并返回一个编译好的 Template  


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


  return HttpResponse(html)


模版文件路径设置


TEMPLATE_DIRS = (    #在settings.py里


       os.path.join(os.path.dirname(__file__), 'templates').replace('\\','/'), #当前项目路径下templates,单元素元组中必须使用逗号


 )


更方便的使用模版文件 #render_to_response() 只是对 get_template() 的简单封装


from django.shortcuts import render_to_response


import datetime


def current_datetime(request):


now=datetime.datetime.now()


return render_to_response('mytp.html',{'current_time':now})


可以使用locals()返回的字典对所有局部变量的名称与值进行映射,还包含了 request


 current_date = datetime.datetime.now()


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


嵌套模版 #所包含的模版执行时的 context 和包含它们的模版是一样的。


{%include%} :允许在(模版中)包含其它的模板的内容。


模版继承


#base.html

<body>{% block content %}{% endblock %}</body>#block标签中可设置默认内容,基模板中的 block标签越多越好

#excents.html

{% extends "base.html" %}#模版引擎以 {%extends%}判断是否子模版,必须为模版中的第一个模版标记

{% block content %}


<p>It is now {{ current_date }}.</p>


{% endblock %}

 #{{block.super}}标签可访问父模板中的块的内容,可用于追加内容

#extends的参数一般是字符串,但如果直到运行时方能确定父模板名,这个参数也可是变量。 这使得能够实现很酷的动态功能!


继承不改变context的工作方式,可以使用多层继承,一般是下面的3层法

1,创建base.html 模版,在其中定义站点的主要外观感受,这些都是不常修改的部分

2,为网站的每个区域创建base_section.html模版,这些模版对base.html进行扩展,并包含该区域特定的风格与设计

3,为每种类型的页面设置独立的模版,如论坛页面或者图片库,这些模版拓展相应区域模版。


高级模板功能:

from django.template import RequestContext

def custom_proc(request): #context处理器接收 HttpRequest ,返回字典,字典中包含可在模板context中使用的变量
  return {  'app': 'My app', 'user': request.user, 'ip_address': request.META['REMOTE_ADDR'}

def view_1(request):# RequestContext processors版本  processors(处理器)
  t = loader.get_template('template1.html')
  c = RequestContext(request, {'message': 'I am view 1.'},processors=[custom_proc])  # RequestContext=RC

# 1. RC 第一参数需要 HttpRequest 对象( request );2. RC 可选参数 processors ,context处理器函数的列表或元组
  return t.render(c)

def view_1(request):# render_to_response context_instance版本
  return render_to_response('template1.html',{'message': 'I am view 1.'},

context_instance=RequestContext(request, processors=[custom_proc]))#context_instance=CI

全局 context处理器的支持

TEMPLATE_CONTEXT_PROCESSORS(模板上下文处理器) 指定了哪些contextprocessors总是默认被使用。这样就省去了每次使用 RequestContext 都指定 processors 的麻烦

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

TEMPLATE_CONTEXT_PROCESSORS = (
  'django.core.context_processors.auth',  # 用户信息,权限等
  'django.core.context_processors.debug',#是否debug模式,记录请求期间的每个SQL查询以及查询所耗费的时间
  'django.core.context_processors.i18n',  #全球化

  'django.core.context_processors.request',每个 RequestContext 将包含变量 request,默认是不启用
  'django.core.context_processors.media', 
)

每个函数使用了和上文中我们的 custom_proc 相同的接口,它们以request对象作为参数,返回一个会被合并传给context的字典: 接收一个request对象作为参数,返回一个包含了将被合并到context中的项的字典。注意:顺序引用,同名覆盖


写Context处理器的一些建议

编写处理器的一些建议:

每个context处理器完成尽可能小的功能。 使用多个处理器是容易的,可以根据逻辑块来分解功能以便将来复用。

要注意 TEMPLATE_CONTEXT_PROCESSORS 里的context processor 将会在基于这个settings.py的每个 模板中有效,所以变量的命名不要和模板的变量冲突。 变量名是大小写敏感的,所以processor的变量全用大写是个不错的主意。

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


转义 

data='<br>'

{{ data|safe }} #变量级转义关闭

{% autoescape off %} #模板级转义关闭
Hello {{ name }}

   {% autoescape on %}#模板级转义打开 嵌套  可以通过include标签作用到其他标签,

           Hello{{say}}
   {% endautoescape %}
{% endautoescape %}


模板加载的内幕

两种方法加载模板

1.django.template.loader.get_template(template_name) : get_template 根据给定的模板名称返回一个已编译的模板(一个 Template 对象)。 如果模板不存在,就触发 TemplateDoesNotExist 的异常。

2.django.template.loader.select_template(template_name_list) : select_template 很像 get_template ,不过它是以模板名称的列表作为参数的。 它会返回列表中存在的第一个模板。如果模板都不存在,将会触发TemplateDoesNotExist异常。


django.template.loaders.filesystem.load_template_source : 此加载器根据 TEMPLATE_DIRS 设置从文件系统加载模板,默认可用。

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

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

               /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代码压缩到一个文件中。

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


扩展模板系统

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

1.创建一个模板库

模板库可以放在某个app下,也可以单独做为一个app,并添加到 INSTALLED_APPS,

在app下创建templatetags目录, 这个目录应当和 models.py 、 views.py 等处于同一层次.

在 templatetags 中创建两个空文件:

一个 __init__.py (告诉Python这是 一个包含了Python代码的包)

一个用来存放你自定义的标签/过滤器定义的文件。 第二个文件的名字将用来加载标签。

如自定义标签/过滤器在一个叫作 poll_extras.py 的文件中,你需要在模板中写入如下内容:

{% load poll_extras %}

2.自定义模板过滤器

完整的模板库例子
from django import template
register = template.Library() #作为合法的模板库,模块需要包含一个名为register的模块级变量
@register.filter(name='cut')
def cut(value, arg):            #一个cut过滤器
   return value.replace(arg, '')

3.自定义模板标签

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

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

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

自定义标签时的所有步骤

(1)编写编译函数

from django import template
register = template.Library()
def do_current_time(parser, token):  #parser和token,parser是模板解析器对象,token是正在被解析的语句
  try:
      tag_name, format_string = token.split_contents() 

     #token.contents 是包含有标签原始内容的字符串; token.split_contents() 方法按空格拆分参数同时保证引号中的字符串不拆分
  except ValueError:
      msg = '%r tag requires a single argument' % token.split_contents()[0]  #token.split_contents()[0]总是记录标签的名字
      raise template.TemplateSyntaxError(msg)  #语法错误的有用信息
  return CurrentTimeNode(format_string[1:-1])    #CurrentTimeNode ,包含了节点需要知道的关于这个标签的全部信息

(2)编写模板节点

编写自定义标签的第二步就是定义一个拥有 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)

这两个函数( __init__() 和 render() )与模板处理中的两步(编译与渲染)直接对应。

(3)注册标签

@register.tag(name="current_time")  #register.tag装饰器


def do_current_time(parser,token): #编译函数 parser模板解析器对象token正在解析的语句


  try:


    tag_name,format_string=token.split_contents()#按空格拆分参数同时保证引号中的字符串不拆分


  except ValueError:


    msg='%r tag requires a single argment' % token.split_contents()[0]


    #token.split_contents()[0]总是记录标签的名字


    raise template.TemplatesSyntaxError(msg)


  return CurrentTimeNode(format_string[1:-1])


   #包含节点需知道的关于这个标签的全部信息。 此例中只传递了参数 "%Y-%m-%d %I:%M %p" 。模板标签开头和结尾的引号使用 format_string[1:-1] 除去


   #模板标签编译函数 必须 返回一个 Node 子类,返回其它值都是错的

#register.tag('current_time', do_current_time) # 模板标签的名字  编译函数


class CurrentTimeNode(template.Node): #拥有 render() 方法的 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)

(4)在上下文中设置变量

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_time2']=now.strftime(self.format_string)
    return ''  #render() 总是返回一个字符串,如果模板标签只是设置变量,就返回空字符串

(5)分析直至另一个模板标签
@register.tag(name="comment2") 
def do_comment2(parser,token):
  nodelist=parser.parse(('endcomment2',))#接收需要分析模板标签名元组,返回django.template.NodeList实例
  parser.delete_first_token()#清除 endcomment2 防止重复处理
  return CommentNode()
class CommentNode(template.Node):
  def render(self,context): #只返回一个空字符,在标签间的内容被忽略
      return ''

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

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)#对节点列表中的每个 Node 简单的调用 render() 。
     return output.upper()

(7)注册简单标签的快捷方式

@register.simple_tag
class CurrentTimeNode(template.Node): 
    #....


(8)包含标签

# poll_extras.py
@register.inclusion_tag('book_snippet.html')
def books_for_author(author):
    books = Book.objects.filter(authors__id=author)
    return {'books': books}
#register.inclusion_tag('book_snippet.html')(books_for_author)

# book_snippet.html

<ul>{% for book in books %}<li>{{ book.title }}</li>{% endfor %}</ul>

访问父模板的context

@register.inclusion_tag('link.html', takes_context=True)
def jump_link(context):
   return {'link': context['home_link'],'title': context['home_title'],}

模板 link.html 可能包含下面的东西
Jump directly to <a href="{{ link }}">{{ title }}</a>.

不带参数地调用自定义标签
{% jump_link %}


(9)编写自定义模板加载器
load_template_source(template_name, template_dirs=None)

#template_name 加载模板的名称,  template_dirs 可选的代替TEMPLATE_DIRS的搜索目录列表

加载成功: 它返回一个元组:(template_source,template_path) 。

在这里的 template_source 就是将被模板引擎编译的的模板字符串,而 template_path 是被加载的模板的路径。

加载失败:触发 django.template.TemplateDoesNotExist 异常

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


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


(10)配置独立模式下的模板系统

......

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值