《精通Django》 第三章 Django模板

3.1 模板系统基础
  • 模板系统的设计目的是呈现表现,而不是呈现逻辑
  • 模板标签:只要能让模板系统"做些事"的就是标签
  • 模板标签和变量
    • {``{ person_name }} 是变量
    • {% if ordered_warranty %} 是模板标签,同理{% else %}{% for item in item_list %}也是
    • {``{ ship_date|date:"F j, Y" }} 是过滤器,方便调整变量格式
      • 把ship_date 变量传给date过滤器,并且为date过滤器指定"F j, Y"参数。
      • date过滤器使用参数指定的格式格式化日期
      • 过滤器使用管道符号(|)依附,类似于Unix管道
使用模板系统
  • Django经过配置后可以使用一个或多个模板引擎
  • Django自带了一个内置的后端,用于支持自身的模板引擎,DTL(Django Template Language)
    • Django 1.8 支持Jinja2
  • 在python代码中使用Django的模板系统的方式
    • 以字符串形式提供原始的模板代码,创建Template对象
    • 在template对象上调用render()方法,传入一系列变量(上下文)。返回的是完全渲染模板后得到的字符串,模板中的变量和模板标签已经根据上下文求出值了
  • 例子,需要用python manage.py shell进入到命令行,否则会报错
python 复制代码
from django import template
t = template.Template("My name is {{ name }}")
c = template.Context({"name": "Nige"})
print(t.render(c))
c = template.Context({"name": "xujie"})
print(t.render(c))


# 输出:
My name is Nige
My name is xujie
创建Template对象
  • 遇到下列情况,模板系统都会抛出TemplateSyntaxError
    • 无效标签
    • 有效标签的无效参数
    • 无效过滤器
    • 有效过滤器的无效参数
    • 无效模板句法
    • 未关闭的标签
3.2.2 渲染模板
  • 创建Template对象后,可以为他提供上下文,把数据传给他

  • 上下文就是一系列模板变量和相应的值

  • 模板使用上下文填充变量,求值标签

  • 在Django中,上下文使用Django.template模块中的Context类表示

    • 构造方法接受一个可选参数:一个字典,把变量名映射到值上
  • 填充模板的方式:在Template对象上调用render()方法,并传入上下文

  • python 和 python manage.py shell区别:

    • 在启动解释器前,告诉Django使用哪个设置文件
3.3 字典和上下文
3.3.1 多个上下文,同一个模板
3.3.2 上下文变量查找
  • 模板系统能优雅处理很多复杂的数据结构,如列表,字典和自定义的对象,遍历Django模板中复杂数据结构的关键是点号(.)
  • 点号可以访问字典的键、属性、方法或对象的索引。
    • 访问字典的键
python 复制代码
In [2]: from django.template import Template, Context
In [3]: person = {'name': 'xujie', 'age': '24'}
In [4]: t = Template('{{ person.name }} is {{ person.age }} years old.')
In [5]: c = Context({'person': person})
In [6]: t.render(c)
Out[6]: 'xujie is 24 years old.'
python 复制代码
In [7]: from django.template import Template, Context
In [8]: import datetime
In [9]: d = datetime.date(1995, 1, 6)
In [10]: d.year
Out[10]: 1995
In [11]: d.month
Out[11]: 1
In [12]: d.day
Out[12]: 6
In [13]: t = Template('The month is {{ date.month }} and the year is {{ date.year }}')
In [14]: c = Context({'date': d})
In [15]: t.render(c)
Out[15]: 'The month is 1 and the year is 1995'
python 复制代码
In [16]: from django.template import Template, Context
In [17]: class Person(object):
    ...:     def __init__(self, first_name, last_name):
    ...:         self.first_name, self.last_name = first_name, last_name
    
In [22]: c = Context({'person': Person('gin', 'xu')})
In [23]: t = Template("Hello, {{ person.first_name }} {{ person.last_name }}")
In [24]: t.render(c)
Out[24]: 'Hello, gin xu'
  • 通过点号引用对象的方法
    • 方法调用中没有括号
    • 不能给方法传递变量,只能调用无需参数的方法
python 复制代码
In [25]: from django.template import Template, Context
In [26]: t = Template('{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}')
In [27]: t.render(Context({'var': 'hello'}))
Out[27]: 'hello -- HELLO -- False'
In [28]: t.render(Context({'var': '123'}))
Out[28]: '123 -- 123 -- True'
  • 使用点号访问列表索引
    • 不允许使用复数索引
python 复制代码
In [29]: from django.template import Template, Context

In [30]: t = Template('Item 2 is {{ items.2 }}')

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

In [32]: t.render(c)
Out[32]: 'Item 2 is carrots'
  • 模板系统遇到变量名中的点号会按下列顺序常识查找
    • 字典查找
    • 属性查找
    • 方法调用
    • 列表索引查找
3.3.3 方法调用的行为
  • 在方法查找过程中,如果方法抛出异常,异常会向上冒泡
    • 除非异常有silient_variable_failure属性,而且值为True
    • 如果异常确实有silient_variable_failure属性,使用引擎的string_if_invalid配置选项(默认为空串)渲染变量
  • 方法不能有必须的参数
    • 否则,模板系统向后移动到下一种查询类型(列表索引查询)
    • 在模板中不能给方法传递参数
      • 按照设计,Django限制了在模板中可以处理的逻辑量,
      • 数据应该在视图中计算之后再传给模板显示
  • 有些方法有副作用
  • 在方法上设定函数属性alters_data = True,渲染模板时不会执行delete()方法,会用string_if_invalid的值替换那个变量
3.3.4 如何处理无效的变量
  • 如果变量不存在,模板系统在变量处插入引擎的string_if_invalid配置选项
    • 默认为空串
3.4基本模板标签和过滤器
3.4.1 标签
is/else
  • {% if %} 支持使用and、or 或not 测试多个变量,或者取反指定的变量
  • 在同一个标签中可以同时使用and 和or,此时,and 的优先级比or 高
    • 在if 标签中使用括号是无效的句法

      • 如果需要通过括号指明优先级,应该使用嵌套的if 标签

      {% if today_is_weekend %}

      Welcome to the weekend!

      {% endif %}

      {% if today_is_weekend %}

      Welcome to the weekend!

      {% else %}

      Get back to work.

      {% endif %}

      {% if athlete_list %}
      Number of athletes: {{ athlete_list|length }}
      {% elif athlete_in_locker_room_list %}

      Athletes should be out of the locker room soon!

      {% elif ... ... {% else %}

      No athletes.

      {% endif %}
for
  • {% for %} 标签用于迭代序列中的各个元素

      {% for athlete in athlete_list %}
    • {{ athlete.name }}
    • {% endfor %}
  • 在标签中添加reversed

    {% for athlete in athlete_list reversed %}
    ...
    {% endfor %}

  • {% for %} 标签可以嵌套

    {% for athlete in athlete_list %}

    {{ athlete.name }}



      {% for sport in athlete.sports_played %}
    • {{ sport }}

    • {% endfor %}

    {% endfor %}

  • for 标签支持一个可选的{% empty %} 子句,用于定义列表为空时显示的内容

    {% for athlete in athlete_list %}

    {{ athlete.name }}


    {% empty %}

    There are no athletes. Only computer programmers.


    {% endfor %}

  • 在循环结束之前,无法"跳出"。如果需要这么做,修改要迭代的变量,只包含需要迭代的值。

  • 也没有"continue"语句,不能立即返回到循环的开头

  • forloop模板变量

    • forloop.counter 的值是一个整数,表示循环的次数。这个属性的值从 1 开始

    • forloop.counter0 与forloop.counter 类似,不过是从零开始

    • forloop.revcounter 的值是一个整数,表示循环中剩余的元素数量

    • forloop.revcounter0 与forloop.revcounter 类似,不过索引是基于零的。第一次循环时,forloop.revcounter0 的值是序列中元素数量减去一

    • forloop.first 是个布尔值,第一次循环时为True。需要特殊处理第一个元素时很方便

    • forloop.last 是个布尔值,最后一次循环时为True。经常用它在一组链接之间放置管道符

    • 在嵌套的循环中,forloop.parentloop 引用父级循环的forloop 对象

    • forloop 变量只在循环内部可用。模板解析器遇到{% endfor %} 时,forloop 随之消失

      {% for link in links %}
      {{ link }}{% if not forloop.last %} | {% endif %}
      {% endfor %}
      输出:Link1 | Link2 | Link3 | Link4

      {% for country in countries %}


      {% for city in country.city_list %}





      {% endfor %}

      Country #{{ forloop.parentloop.counter }} City #{{ forloop.counter }} {{ city }}
      {% endfor %}
ifequal/ifnotequal
  • {% ifequal %} 标签比较两个值

    {% ifequal user currentuser %}

    Welcome!

    {% endifequal %}
  • {% ifequal %} 标签支持可选的{% else %} 子句

    {% ifequal section 'sitenews' %}

    Site News

    {% else %}

    No News Here

    {% endifequal %}
  • {% ifequal %} 的参数只能是模板变量、字符串、整数和小数

  • 如果想测试真假,应该使用{% if %} 标签。

注释
  • 注释使用{# #} 标明

  • 多行注释,使用{% comment %} 模板标签

    {% comment %}
    This is a
    multi-line comment.
    {% endcomment %}

3.4.2 过滤器
  • 模板过滤器是在显示变量之前调整变量值的简单方式
  • 通过lower过滤器把文本转化成小写
    • {``{ name|lower }}
  • 获取列表中的第一个元素,然后将其转换成大写
    • {``{ my_list|first|upper }}
  • 有些过滤器可以接受参数
    • {``{ bio|truncatewords:"30" }}
    • 显示bio变量的前30个词
  • 几个重要的过滤器
    • addslashes,在反斜线,单引号和双引号前面加一个反斜线,用于转移字符串
    • date,根据参数中的格式字符串格式化date或datetime对象,例如
      • {``{ pub_date|date:"F j, Y"}}
    • length: 返回值的长度,
      • 对列表来说,返回元素的数量
      • 对字符串来说,返回字符的数量
      • 如果变量未定义,返回0
3.5 理念和格局
  • DTL的局限是故意为之
  • Django发端于在线新闻站点,特点是大容量,变化频繁,最初设计Django的人对DTL有非常明确的理念预设
  • 理念
    • 表现与逻辑分离
      • 模板系统用于控制表现及其相关的逻辑,仅此而已
    • 避免重复
      • 大多数动态网站都使用某种全站通用的设计,例如通用的页头、页脚、导航栏等。模板继承背后就是这个理念
    • 与HTML解耦
      • 模板系统不应该只能输出HTML,还要能够生成其他基于文本的格式(也就是纯文本)
    • XML不好
      • XML不应该作为模板语言
      • 如果使用XML引擎解析模板,编辑模板时可能引入大量人为错误,而且处理模板有很多额外消耗
    • 不要求具备设计能力
      • 模板系统不应该必须在编辑器(如Dreamweaver)中才能写出
      • Django的目标是让模板编写人员能直接编辑HTML
    • 透明处理空格
      • 不应该特殊处理空格
    • 不重造一门编程语言
      • 模板系统一定不能允许:
        • 为变量赋值
        • 编写高级的逻辑
      • 目标是提供适量的编程功能
      • 模板系统知道模板最常由设计师编写,而不是程序员,因此不要求具备python知识
    • 确保安全有保障
      • 模板系统应该禁止包含恶意代码,
        • 例如删除数据库记录的命令
    • 可扩展
      • 这是自定义模板标签和过滤器背后的理念
3.6 在视图中使用模板
3.7 模板加载机制
  • settings.py里面的配置
python 复制代码
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        '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',
            ],
        },
    },
]
  • 参数说明
    • BACKEND的值是一个点分Python路径,指向实现Django模板后端API的模板引擎类
      • 内置的后端有:
        • django.template.backends.django.DjangoTemplates
        • django.template.backends.jinja2.Jinja2
    • DIRS定义一个目录列表,模板引擎按顺序在里面查找模板源文件
    • APP_DIRS设定是否在安装的应用中查找模板。
      • 设为True时,Django Templates会在INSTALLED_APPS中的各个应用里查找名为templates的子目录
    • OPTIONS是一些针对后端的设置
3.7.1 模板目录
  • DIRS的默认值是一个空列表

    • 如果不是构建没有应用的极简程序,最好留空DIRS。设置文件默认把APP_DIRS设为True,因此最好在Django应用中放一个templates子目录
    • 如果想在项目根目录中放一些主模板(例如在mysite/templates目录中),需要向这样设定DIRS:
      • 'DIRS': [os.path.join(BASE_DIR, 'templates')],
    • 模板目录不一定非得叫'templates'
    • windows中要加上盘符,用Unix风格的正斜线
      • 'DIRS': ['C:/www/django/templates'],
  • views.py增加

python 复制代码
from django.template.loader import get_template
def current_datetime_template(request):
    now = datetime.datetime.now()
    t = get_template('current_datetime.html')
    html = t.render({'current_date': now})
    return HttpResponse(html)
  • urls.py
python 复制代码
from django.contrib import admin
from django.urls import path, re_path
from mysite.views import hello, current_datetime, hours_ahead, current_datetime_template
urlpatterns = [
    path('admin/', admin.site.urls),
    path('hello/', hello),
    path('time/', current_datetime),
    path('time_template/', current_datetime_template),
    path('time_render/', 
    re_path('time/plus/(\d{1,2})/$', hours_ahead),
]
  • settings.py里面更改如下

    • 'DIRS': [os.path.join(BASE_DIR, 'templates')],
  • django.template.loader.get_template() 函数的参数是模板名称

  • get_template()按下列顺序查找

    • 如果APP_DIRS的值是True,而且使用DTL,在当前应用中查找"templates"目录
    • 否则把传入的模板名称添加到DIRS中的各个目录后面,按顺序在各个目录中查找
    • 否则抛出TemplateDoesNotExist异常
3.8 render()
  • 比get_template()更简单的方法

    • django.shortcuts.render()
  • views.py 增加

python 复制代码
from django.shortcuts import render
def current_datetime_render(request):
    now = datetime.datetime.now()
    return render(request, 'current_datetime.html', {'current_date': now})
  • urls.py看着加

    from django.contrib import admin
    from django.urls import path, re_path
    from mysite.views import hello, current_datetime, hours_ahead, current_datetime_template, current_datetime_render
    urlpatterns = [
    path('admin/', admin.site.urls),
    path('hello/', hello),
    path('time/', current_datetime),
    path('time_template/', current_datetime_template),
    path('time_render/', current_datetime_render),
    re_path('time/plus/(\d{1,2})/$', hours_ahead),
    ]

3.9 模板子目录
  • 把模板放到模板目录子目录方法
    • t = get_template('dateapp/current_datetime.html')
    • return render(request, 'dateapp/current_datetime.html', {'current_date': now})
3.10 include模板标签
  • 一个内置的模板标签 {% include %}

    • 作用:引入另一个模板的内容
    • 参数:要引入的模板名称,可以是变量,也可以是硬编码的字符串
  • 如果指定的参数不存在,会

    • DeBUG为True时,渲染Django错误页面,显示TemplateDoesNotExist异常
    • DEBUG为False时,静默
3.11 模板继承
  • 解决引入另一个网页的更好的方法是使用模板继承
  • 子模板可以覆盖块的内容、向块中添加内容,或者原封不动。
  • {% block %} :告诉模板引擎,子模板可以覆盖这部分内容
  • 继承不影响模板的上下文。也就是说,继承树中的任何模板都能访问上下文中的每一个模板变量。根据需要,继承层级的深度不限。继承经常使用下述三层结构:
    • 创建base.html 模板,定义网站的整体外观。这个模板的内容很少变化。
    • 为网站中的各个"区域"创建base_SECTION.html 模板。这些模板扩展base.html,包含各区域专属的样式和设计。
    • 为各种页面创建单独的模板,例如论坛页面或相册。这些模板扩展相应的区域模板。
  • 使用模板继承的一些指导方针:
    • 如果模板中有{% extends %},必须是模板中的第一个标签。否则,模板继承不起作用。
    • 一般来说,基模板中的{% block %} 标签越多越好。记住,子模板无需定义父模板中的全部块,因此可以为一些块定义合理的默认内容,只在子模板中覆盖需要的块。钩子多总是好的。
    • 如果发现要在多个模板中重复编写相同的代码,或许说明应该把那些代码移到父模板中的一个{%block %} 标签里
    • 如果需要从父模板中的块里获取内容,使用{{ block.super }},这是一个"魔法"变量,提供父模板中渲染后的文本。向块中添加内容,而不是完全覆盖时就可以这么做。
    • 在同一个模板中不能为多个{% block %} 标签定义相同的名称。之所以有这个限制,是因为block 标签是双向的。即,block 标签不仅标识供填充的空位,还用于定义填充父模板中空位的内容。如果一个模板中有两个同名的块,那么父模板就不知道使用哪个块里的内容。
    • 传给{% extends %} 的模板名称使用与get_template() 相同的方法加载。即,模板在DIRS 设置定义的目录中,或者在当前 Django 应用的"templates"目录里。
    • 多数情况下,{% extends %} 的参数是字符串,不过如果直到运行时才知道父模板的名称,也可以用变量。通过这一点可以做些动态判断。

最后打个广告

🌟 立即开启您的Python学习之旅!

欢迎来到 Python99网

在这里,我们提供全面的 Python 体系课程,帮助您掌握编程技能,开启职业新篇章。仅需 2999元,即可享受终生课程更新和支持!


🚀 为什么选择我们?

  • 高效学习法

    探索项目实战,通过实践来掌握核心知识,遵循"用20%的时间,学会80%的知识"的原则,助您事半功倍!

  • 终身学习保障

    在快速变化的技术领域,学习永无止境。我们的课程会持续更新,确保您永远走在时代的前沿。

  • 多元化课程选择

    爬虫工程师后端开发 ,再到算法工程师,多样化的课程方向让您能根据个人发展目标灵活选择!

相关推荐
. . . . .9 小时前
SQLite 技术总结:轻量级数据库的本地存储利器
数据库·sqlite
CodeCipher9 小时前
关于Redis单线程问题
数据库·redis·缓存
威风少侠9 小时前
Redis集群配置优化指南
数据库·redis·缓存
企鹅郁金香9 小时前
Gitlab和Gerrit部署后的工作(二)
数据库·gitlab·gerrit域名无法修改·激活gitlab·gitlab注册ldap·nginx反向代理gitlab·nginx反向代理gerrit
悄悄敲敲敲9 小时前
MySQL表的内外连接
数据库·mysql
kaico20189 小时前
MYSQL的各版本对比
数据库·mysql
zgl_200537799 小时前
ZGLanguage 解析SQL数据血缘 之 Python提取SQL表级血缘树信息
大数据·数据库·数据仓库·hive·hadoop·python·sql
rgeshfgreh10 小时前
Python函数全解析:定义、参数与作用域
前端·数据库·python
亮子AI10 小时前
【MySQL】node.js 如何判断连接池是否正确连接上了?
数据库·mysql·node.js