zoukankan      html  css  js  c++  java
  • django框架学习:三十五.Django模板语言详解

    前言

    前面写过几篇模板语言的语法,今天总结一下django模板语言的用法。

    如果你有过其他变成编程背景,或者使用过一些在HTML中直接混入程序代码的语言,那么你需要记住,django的模板系统并不是简单的python嵌入到HTML中。

    一.模板

    模板是纯文本文件,可以生成任何基于文本的文件格式,比如HTML,XML,CSV等。

    下面是一个小模板,它展示了一些基本的元素。

    {% extends "base.html" %}
    {% block title %}{{ section.title }}{% endblock %}
    {% block content %}
    <h1>{{ secion.title }}</h1>
    {% for story in story_list %}
        <h2>
          <a h="{{ story.get_absolute_url }}"> {{ story.headline | upper }}</a>
    
        </h2>
        <p>{{ story.tease | truncatewords:"100"}}</p>
    {% endfor %}
    {% endblock %}

    二.变量

    变量看起来就像这样:{{  variable }}。

    当模板引擎遇到一个变量,它将从上下文的context中获取这个变量的值。然后用值替换掉它本身。变量的命名包含任何字母数字以及下划线的组合。点("."),它将以这样的顺序查询这个原点具体代表的功能:

    》字典查询

    》属性或者方法查询

    》数字索引查询

    如果你使用的变量不存在,模板系统将插入string_if_invaild选项的值,默认设置为空字符串。

    注意:像{{ foo.bar }}这种模板表达式中的 " bar",如果在模板上下文中存在,将解释为一个字面意义的字符串,而不是使用变量bar的值。

    三.过滤器

    过滤器一般是这样的:{{ name | lower }},使用管道符号来使用过滤器,该过滤器将文本转换为小写。

    过滤器可以"链接",一个过滤器的输出应用下一个过滤器,例如:{{ text | escape | linebreaks }} 就是一个常用的过滤器链,他首先转因文本内容,然后把文本行转成<p>标签。一些过滤器带有参数,过滤器的参数看起来是这样: {{ bio | truncatewords :30}}  这将显示 truncatewords前30个词。

    过滤器有空格的话必须用引号抱起来,例如使用逗号和空格去连接一个列表中的元素,你需要使用{{ list | join:", "}},dnago提供了大概60个内置的模板过滤器,很多你想要的的他都已经提供了,下面是一些常用的模板过滤器:

    1.default:为false或者空变量提供默认值,像这样:

    {{ value | default :"nothing" }}

    2.length:返回值的长度,他对于字符串和列表都起作用。

    {{ value | length}}

    如果value是[1,2,3,3,5],那么输出5.

    3.filesizeformat

    格式化为"人类可读"文件大小单位(即'13KB','4.1MB','102bytes'等)

    {{ value | filesizeformat }} 如果value是123456789,输出将会是117.7MB。

    我们可以创建子弟昂一的么模板过滤器和标签。

    四.标签

    标签看起来像是这样的:{ % tag %},标签比变量复杂的多,有些用于在输出中创建文本,有些用于控制循环或者逻辑判断,有些用于加载外部信息到末班中供以后的变量使用。一些标签需要开始标签和结束标签(即 { % 标签 % }  ...标签内容...{ % ENDTAG %})。

    django自带了24个内置的模板标签,下面是一些常用的标签:

    1.for循环标签

    循环对象中每个元素,需要结束标签{ % endfor %},例如,显示athlete_list中提供的运动员列表:

    <ul>
        {% for athlete in athlete_list %}
        <li>{{ athlete.name }}</li>
        {% endfor %}
    </ul> 

    2.if,elif和else标签

    计算一个表达式,并且表达式的值"True"时,显示块中的内容。需要{ % endif % }结束标签。整体逻辑非常类似python中的if,elif和else,如下所示:

    {% if athlete_list %}
        Number of athletes:{{ athlete_list | length }}
    {% elif athlete_in_locker_room_list %}
        Athletes shoule be out of the locker room soon
    {% else %}
        No athletes
    {% endif %}

    在上面的例子中,如果athlete_list不是空的,运动员的数量将显示为{{ athlete_list | length }},如果athlete_in_locker_room_list不为空,将显示Athletes shoule be out of the locker room soon,如果两个列表都显示空的,将显示No athletes。

    还可以在if标签中使用过滤器和多种运算:

    {% if athlete_list| length > 1 %}
    Team:{% for athlete in athlete_list %}  { % endfor % }
    {% else %}
    Athlete:{{ athlete_list.0.name }}
    {% endif %}
    

    3.block和extends标签

    继承和复写模板,类似python的雷吉成和重写机制。

    五.模板继承 

    django模板引擎中最强大的也是最复杂的部分就是模板继承了,模板继承允许你创建一个包含骨架的父亲末班,它包含站点中的共有元素,并且可以定义能够被子模板覆盖的blocks。通过下面这个例子,理解继承末班的概念:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="style.css"/>
        <title>{% block titile %} My amazing site {% endblock %}</title>
    </head>
    <body>
    <div id="sidebar">
        {% block sidebar %}
            <ul>
                <li><a href="/">Home</a></li>
                <li><a href="/blog/">Blog</a></li>
            </ul>
        {% endblock %}
    </div>
    <div id="content">
        {% block content %} {% endblock %}
    </div>
    </body>
    </html>

    这个模板通常被命名为"base.html",他定义了一个可以用于两列排版页面的HTML骨架。"子模板"需要的是先继承父模板base.html,然后腹泻,填充,或者说实现其中的blocks。block是在自末班中可能会被覆盖掉的位置,在上面的例子中,block标签定义了三个可以被自模版内容填充的block,分别是title,content,和sidebar。

    再看看下面的例子,子模板可能看起来是这样的:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    {% extends "base.html" %}
    {% block title %}My LiuShui blog{% endblock %}
    {% block content %}
        {% for entry in entry_list %}
            <h2>{{ entry.title }}</h2>
            <p>{{ entry.body }}</p>
        {% endfor %}
    {% endblock %}
    </body>  

    extends标签是这里的关键,他告诉模板引擎这个模板继承了base.html模板,当模板系统处理这个模板时,首先会去加载父模板。加载过程中,模板引擎注意到base.html的三个block标签,并用子模板的内容替换这些block,根据block_entries的值,最终输出的值可能会是这样的:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <link rel="stylesheet" href="style.css" />
        <title>My amazing blog</title>
    </head>
    
    <body>
        <div id="sidebar">
            <ul>
                <li><a href="/">Home</a></li>
                <li><a href="/blog/">Blog</a></li>
            </ul>
        </div>
    
        <div id="content">
            <h2>Entry one</h2>
            <p>This is my first entry.</p>
    
            <h2>Entry two</h2>
            <p>This is my second entry.</p>
        </div>
    </body>
    </html>
    

    请注意,上面例子中的子模版并没有定义sidebar block,这种情况下,将使用父模版中的内容。父模版的{% block %}标签中的内容总是被用作默认内容。 

    django支持多级竭诚,常用方式是类似下面的三级结构:

    》创建一个base.html模板,用来控制整个站点的主要视觉和体验。

    》为站点的每一个app,创建一个base_SECTIONNAME.html模板,例如:base_news.html,base_sports.html,这些模板都集成了base.html,并且包含了各自特有的样式和设计。

    》为每一个页面类型,创建独立的模板,例如新闻或者博客,这模板继承对应的app模板。

    上面的方式可以是代码的到最大程度的复用,并且使得添加内容到共享的内容区域更加简单,例如app范围内的导航条。

     下面是使用继承的一些相关说明:

    》如果在模板中使用{% extends %}标签,他必须是模板中的第一个标签,必须放在文件首行。

    》在base模板中设置越多的{% block %}标签越好,子模板不必定义全部父模板中的blocks,所以可以再大多数blocks中填充合理的默认内容,然后只定义你使用的哪一个就可以了。

    》如果你发现自己在复制大量重复的模板内容,那意味着你应该把重复的内容添加到父类的一个block中。

    》如果需要获取父模板中到的block内容,可以使用{{ block.super }}变量,如果想要在父block中新增内容而不是完全覆盖内容,这将十分有用,使用{{ block.super }}插入的数据不会被自动转义,因为父模板中的内容已经被转义。

    》在{% block %}之外创建的变量使用模板标签的as语法,不能再块内使用。

    {% trans "Title" as title %}
    {% block content %}{{ title }}{% endblock %}
    

    》为了更好的可读性,可以给{% endblock %}取一个标签名,像这样{% block content%} ....

    在大型模板中,有助于你清楚的看到哪一个{% block %}标签被关闭了。

    》注意,不能再一个模板中定义多个相同名字的block标签。

    六、自动转义HTNL

    当从模板中生成HTML文件时,总是会存在各种风险,比如xss代码注入等恶意攻击,比如下面的模板片段:

    hello,{{ name }} 

    首先他看起来无害,用来显示用户的名字,但是设想一下,如果用户想下面这样输入他的名字,会发生什么:

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

    这意味着浏览器会谈书一个JavaScript警告框!

    类似的如果一个名字包含一个“<”符号,会发生什么?

    <b> username

    这将会导致网页的其余部分被粗体化。

    显然用户提交的数据都不应该被盲目的信任,并且被直接插入到网页中,因为一个怀有恶意的用户可能会使用这样的漏洞来做一些坏事,这种类型的安全问题被叫做跨站脚本攻击(xss)。

    怎么避免以上问题,有两个选择:

    》第一,对于每个不信任的值运行escape过滤器,这将把潜在的有害的HTML字符转换成危害的字符串。,但问题是他将责任放在开发人员/模板作者身上,以确保转义了所有内容,而且很容易忘记转义数据。

    》第二,利用django自动的HTML转义功能,默认情况下django的每个模板会自动转义每个变量,也就是说下面的五个字符将被转义:

    <会转换为<
    >会转换为>
    '(单引号)转换为'
    "(双引号)会转换为"
    &会转换为&amp; 

    将第二种功能作为默认打开的设置,不要关闭。但是凡事都具有两面,有事模板变量含有一些你想打算渲染成原始HTML的数据,并不想转义这些数据,例如,你可能会在数据库中存储一些HTML代码,并且直接在模板中嵌入它们,或者你可能使用django模板来生成不是HTML的模板,比如邮件信息,怎么办呢?

    对于单个变量:

    使用safe过滤器来关闭变量上的自动转义:

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

    在上面的例子中如果data含有<b>,输出会是:

    This will be escaped: <b>
    This will not be escaped: <b>
    

    也就是说<b>标签被禁止转义了。

    对于模板块:

    要控制no板块上的自动转义,将模板包裹在autoescaoe标签中,像这样:

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

    autoescape标签接受on或者off作为他的参数。下面是一个模板的示例:

    {% autoescape off %}
        This will not be auto-escaped: {{ data }}.
    
        Nor this: {{ other_data }}
        {% autoescape on %}
            Auto-escaping applies again: {{ name }}
        {% endautoescape %}
    {% endautoescape %}
    

    自动转义标签autoescape还会作用于扩展(extend)了当前模板的模板,以及通过include标签包含的模板,就像所有block标签那样。 看下面的例子:

    # 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模板中关闭,导致当greeting变量含有<b>Hello!</b>字符串时,会渲染HTML。

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

    过滤器的字符串参数:

    之前我们展示过,过滤器的参数  

    可以是字符串:

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

    要注意,所有这种字符串参数在插入模板时都不会进行任何自动转义。原因是,模板的作者可以控制字符串字面值的内容,所以可以确保在模板编写时文本经过正确转义。白话讲,就是,你个程序员对自己传递的参数心里要有数!

    也即是说你应该这样编写:

    {{ data|default:"3 < 2" }}

    而不是这样写:

    {{ data|default:"3 < 2" }}  {# 错误的做法#}

    七.方法调用

    大多数对象上的方法调用同样可用于模板中,这意味着模版能够访问到的不仅仅是对象的数行,比如字段名称和使徒传入的变量,还可以执行对象的方法。例如django orm提供了"entry_set"语法用于查找关联到外检的对象集合。所以如果墨香"comment"有一个外键关联到模型"task",可以根据task遍历其所有的comments,像这样:

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

    与之类似,QuerySets提供了count()方法来计算含有对象的总数,因此,你可以想着想获取所有当前任务的评论总数:

    {{ task.comment_set.all.coun }}

    当然还可以访问已经显示定义在模型上的方法:

    #models.py
    class Task(models.Model):
        def foo(self):
             return "bar"
    
    
    #template.html
    {{task.foo}}  

    由于django有意限制了模板语言中的处理逻辑,不能够在模板中传递参数来调用方法,数据应该在视图中处理,然后传递给模板用于展示,这点不同于django的orm操作。

     八.多对多调用

    from django.db import models
    
    # Create your models here.
    
    class Student(models.Model):
        name = models.CharField(max_length=128)
    
    class Course(models.Model):
        name = models.CharField(max_length=128)
        students = models.ManyToManyField('Student')

    模型Course有一个多对多内容指向Student。

    正向查询:

    假设编写了如下视图:

    def test(request):
        course = models.Course.objects.get(pk=1)
        return render(request, 'course.html', locals()) 

     获取id=1的course对象,并将它传递给course.html模板,模版代码如下:

    {% for student in course.students.all %}
        <p>{{ student.name }} </p>
    {% endfor %}

    首先通过course.students.all查询到course对象关联的students对象集,然后利用for循环标签玄幻,获取每个student对象,再用student模型的定义,访问其各字段的属性。

    正向查询:

    对于反向查询,就是从students往course查,假设如下视图:

    def test2(request):
        student = models.Student.objects.get(pk=1)
        return render(request, 'student.html', locals())

    获取id=1的student对象,并将它传递给student.html模板,模板代码如下:

    {% for course in student.course_set.all %}
        {{ course.name }}
    {% endfor %}    

    通过student.course_set.all,反向获取到student实例对应的所有course对象,然后再for标签循环每个course,调用course的各种字段属性。

    对于外键ForeignKey,其用法基本类似。只不过正向是obj.fk,且只有1个对像,不是集合。反向则是obj.fk_set,类似多对多。

    九.使用自定义标签和过滤器

    某些应用提供了自定义的标签和过滤器,想要在模板中使用他们,首先要确保该应用已经在INSTALL_APPS中,(比如在下面的例子中,我们添加了'django.contrib.humanize'),值后再模板中使用{% load %}标签。

    {% load humanize %}
    {{ 45000 | intcoma}}

    上面的例子中,load标签加载了 humanizeapp 的标签库,之后我们可以使用他的intcomma过滤器。当你开启了django.contrib.admindocs,可以查询admin站点中的文档,查看你安装的自定义库列表。load标签可以同时加载多个库名称,由空格分隔,例如:

    {% load humanize i18n %}

    自定义库和模板继承:

    当你加载一个自定义标签或过滤器库时,标签或过滤器只在当前模板中有效--并不是带有模板继承关系的任何父模板或者子模板都有效,也就是你在父模板可能加载了自定义标签,你在子模板还要在加载一次》

    例如你在一个模板foo.html带有{% load humanize %},子模板(例如,带有{% extends  "foo.html" %})中不能访问humanize模板标签和过滤器,子模板需要在添加自己的{% load humanize %}。

    这个特性是出于保持可维护性和逻辑性的目的。

  • 相关阅读:
    利用JSGrid创建甘特图
    Using the SharePoint 2010 Client Object Model_part_2
    Using the SharePoint 2010 Client Object Model_part_1
    如何利用Featue对特定的文档库或列表添加listviewtoolbar上的button
    SharePoint 2010 工作流解决方案:创建带有关联窗体和启动窗体的工作流
    Wireshark过滤器
    first blog
    DataAdapter.update如果处理自动增长列的数据
    ms sql 中关于spid=2的问题
    利用ADO.NET RowUpdating更新DataRow信息
  • 原文地址:https://www.cnblogs.com/liushui0306/p/12883387.html
Copyright © 2011-2022 走看看