把模板的过程、语法、标签、反向地址解析、过滤器、模板继承与HTML转义记下笔记
1、概述及demo
动态生成HTML
模板的设计实现业务逻辑(View)和显示内容(template)的分离
一个模板可以给多个视图去使用,模板所使用的语法称为DTL(Django Template Language)定义在django.template包
如果要调用模板,则需要如下步骤:
- 加载模板。通过loader的get_template方法指定一个模板
- 渲染模板。
- 指定一个上下文对象,RequestContext()
- 用模板的render()方法接收上下文对象。作为HttpResponse()的参数
demo示例的过程:创建工程 - 添加数据库 - 创建应用 - 添加模板路径 - 设置路由 - 编写urls.py - 编辑视图 - 添加模板
添加模板路径:修改settings.py配置,在TEMPLATES中修改DIRS。指定TEMPLATES的目录
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], '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', ], }, },
添加模板:
booktest/views.py
from django.http import HttpResponse from django.template import loader, RequestContext
def resp(request): t1 = loader.get_template('booktest/resp.html') context = RequestContext(request, {"text":"helloworld"}) return HttpResponse(t1.render(context))
templates/booktest/resp.html
<body> {{text}} </body>
实际上上面的代码通过会被封装到一个函数render()中,所以我们一般简写为
from django.shortcuts import render from django.http import HttpResponse def resp(request): context = {"text":"helloworld"} return render(request, "booktest/resp.html", context)
2、模板语法
- 变量。两个大括号括起来的 {{变量名}}
- 标签。代码段 {% 代码块 %}
- 过滤器。就是一个竖线(|)
- 注释。{# 这里是注释 #}
变量
模板碰到变量的时候,计算这个变量的值,然后将结果输出
变量名有字母、数字、下划线组成。必须以字母或下划线开头
当模板引擎碰到圆点的时候,会按照如下的顺序进行查询:
- 字典。把圆点前面的内容理解为一个字典,圆点后面的内容理解为键
- 属性或方法查询。把圆点前面的内容理解为一个对象,圆点后面的内容理解为对象里面的属性或方法(访问方法不能加括号,方法也不能定义参数)
- 数字索引查询。把圆点前面的内容理解为列表或元组。圆点后面的内容理解为下标(下标只能为数字,不能为字符串)
比如访问{{book.id}},会按照如下顺序解析:
- 把book当做字典。访问book[‘id’],如果book字典中有id的键,则查询成功,并返回,如果没有id的键,或者甚至没有book字典,查询失败,往下
- 把book当做对象,把id当做属性或方法,尝试访问book.id或book.id()如果book对象中有id的属性或方法,则查询成功,调用并返回,否则往下
- 把book当做列表或元组,把id当做索引,访问book[id],如果访问成功,则输出,否则往下
- 把book.id作为空字符串’’来输出
3、引入模型
在models.py中定义类HeroInfo
booktest/models.py
from django.db import models # Create your models here. class BookInfo(models.Model): btitle = models.CharField(max_length = 20) bpub_date = models.DateTimeField(db_column = 'pub_date') bread = models.IntegerField() bcomment = models.IntegerField() isDelete = models.BooleanField() class Meta(): db_table = 'bookinfo' class HeroInfo(models.Model): hname = models.CharField(max_length = 10) hgender = models.BooleanField() hcontent = models.CharField(max_length = 1000) isDelete = models.BooleanField() book = models.ForeignKey('BookInfo', on_delete = models.CASCADE) def showname(self): return self.hname
生成迁移,数据库中生成bookinfo和booktest_heroinfo的表
>>>python manage.py makemigrations
>>>python manage.py migrate
把book和hero的sql语句插入到数据库中
修改视图类 booktest/views.py
from django.shortcuts import render from booktest.models import BookInfo, HeroInfo # Create your views here. def index(request): hero = HeroInfo.objects.get(pk=1) context = {'hero': hero} return render(request, 'booktest/index.html', context)
模型中,既可以访问属性,也可以访问方法(访问方法不能加括号)
booktest/index.html
<body> {{hero.hname}} {{hero.showname}} </body>
4、使用标签
语法:{% 标签 %}。注意标签中写的是代码
作用:
- 在输出中创建文本
- 循环或条件判断等逻辑
- 加载外部信息
for标签
{% for ... in ... %}
{{ forloop.counter }} 表示当前是第几次循环
{% empty %}
列表是空或不存在的时候,执行这里
{% endfor %}
修改views.py booktest/views.py
# Create your views here. def index(request): heroList = HeroInfo.objects.filter(isDelete=False) context = {'heroList': heroList} return render(request, 'booktest/index.html', context)
模板的代码中添加for标签 templates/booktest/index.html
<ul> {% for hero in heroList %} <li>{{ forloop.counter }}: {{hero.showname}}</li> {% empty %} <li>找不到</li> {% endfor %} </ul>
if标签
{% if ... %}
逻辑1
{% elif ... %}
逻辑2
{% else %}
逻辑3
{% endif %}
比如,想奇数行显示红色,偶数行显示蓝色
<ul> {% for hero in heroList %} {% if forloop.counter|divisibleby:"2" %} <li style="color:red">{{ forloop.counter }}: {{hero.showname}}</li> {% else %} <li style="color:blue">{{ forloop.counter }}: {{hero.showname}}</li> {% endif %} {% empty %} <li>找不到</li> {% endfor %} </ul>
4、反向地址解释
{% url name p1 p2 ... %}
先创建一个页面
路由: booktest/urls.py
urlpatterns = [ url('^$',views.index), # 路由到views.py中的index()函数 url('^(d+)$',views.show), ]
视图: booktest/views.py
urlpatterns = [ url('^$',views.index), # 路由到views.py中的index()函数 url('^(d+)$',views.show), ]
模板:templates/booktest/show.html
<body> {{id}} </body>
接下来考虑在index中,添加一个链接,点击后可以跳转到123。一般是这样写
<a href="/booktest/123">跳转</a>
但是万一路由被改变了
urlpatterns = [ url(r'^admin/', include(admin.site.urls)), url('booktest1/', include('booktest.urls')) ]
此时,链接的地址就发生了改变。如果要随时去修改链接的地址,就有点麻烦了
因此考虑使用地址的反向解释:
正向:在浏览器中输入url后,用该url去匹配规则(URL_PATTERNS)
反向:根据URL规则,生成一个地址
首先,在urls的根中,使用namespace
django4/urls.py
urlpatterns = [ url(r'^admin/', include(admin.site.urls)), url('booktest/', include('booktest.urls', namespace='booktest')) ]
在子urls中,添加name
booktest/urls.py
urlpatterns = [ url('^$',views.index), # 路由到views.py中的index()函数 url('^(d+)$',views.show, name="show"), ]
在模板中使用反向地址解释
{% url name p1 p2 ... %}
其中,这里的name是指namspace:name
<a href="{% url 'booktest:show' 123 %}">跳转</a>
5、过滤器
# 语法: {{ 变量|过滤器 }} # 比如: {{ name|lower }} # 表示将变量name的值全部变成小写 # 竖线|可以理解为python中的圆点(.) # 可以在if标签中,使用过滤器,并结合运算符一起使用 {% if name|length > 2 %} # 过滤器可以用来串联,构成过滤器链 name | lower | upper # 过滤器可以传递参数 list | join:”,” # 设置默认值的过滤器 value | default:”0” #设置日期 value | date:”YYYY-mm-dd”
常用的django过滤器参考:https://www.cnblogs.com/huangxm/p/6286144.html
6、模板继承
很多网站的头部(页头)、底部(页脚)都是一样的。
把多个页面通用的部分抽取出来,做成父模板。然后在子模板中继承父模板,再实现各自的差异。
相关的概念:
block标签。在父模板中预留区域,在子模板中进行填充
定义父模板base.html
{% block block_name %}
这里可以定义默认值。如果不定义默认值,表示默认值为空
{% endblock block_name %}
extends继承。写在子模板文件的第一行
定义子模板index.html
{% extends ‘base.html’ %} {% block block_name %} 实际填充内容 {% endblock block_name %}
如下例子:
定义父模板
在templates/booktest目录下创建base.html
templates/booktest/base.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>标题</title> {% block head %} {% endblock head %} </head> <body> <h1>页头:logo</h1> <hr/> {% block content %} <h1>默认的内容</h1> {% endblock content %} <hr/> <h1>页脚:联系我们</h1> </body> </html>
子模板
templates/booktest/index2.html
{% extends 'booktest/base.html' %}
路由和视图略写了。
如果要修改中间的内容:
可以在index2.html中添加
{% extends 'booktest/base.html' %} {% block content %} <h1>修改后的内容</h1> {% endblock content %}
7、HTML转义
通过视图往模板输出带html标签的内容的时候
booktest/views.py
def htmlTest(request): context = {'text1':'<h2>123</h2>'} return render(request, 'booktest/htmlTest.html', context)
对应的模板捕获
templates/booktest/htmlTest.html
<body>
{{text1}}
</body>
其实这里是做了html的转义。当我们给一段html格式的字符串的时候,到了模板中会自动的转义成特殊字符。最终原封不动的输出
实际上它使用的就是escape过滤器进行自动转义,所以下面两句是一样的效果
{{text1}}
{{text1 | escape }}
当需要按照它指定的格式输出的时候,考虑使用safe过滤器,目的是关闭转义
{{text1 | safe }}
如果有一整块代码都需要转义,考虑使用autoescape,并通过设置on打开转义,设置off关闭转义
{% autoescape off %}
{{text1}}
{% endautoescape %}
如果是在模板代码中直接写html字符串
{{ text2 | default:'<h2>456</h2>' }}
则直接输出456,不需要转义