四方显神

导航

Django开发笔记(五)四件套之三:模板

模板引擎是一种可以让开发者把服务端数据填充到html网页中完成渲染效果的技术。它实现了把前端代码和服务端代码分离的作用,让项目中的业务逻辑代码和数据表现代码分离,让前端开发者和服务端开发者可以更好的完成协同开发。

静态网页:页面上的数据都是写死的,万年不变

动态网页:页面上的数据是从后端动态获取的(比如后端获取当前时间;后端获取数据库数据然后传递给前端页面)

Django框架中内置了web开发领域非常出名的一个DjangoTemplate模板引擎(DTL)。DTL官方文档

要在django框架中使用模板引擎把视图中的数据更好的展示给客户端,需要完成3个步骤:

  1. 在项目配置文件中指定保存模板文件的模板目录。一般模板目录都是设置在项目根目录或者主应用目录下。
  2. 在视图中基于django提供的渲染函数绑定模板文件和需要展示的数据变量。
  3. 在模板目录下创建对应的模板文件,并根据模板引擎内置的模板语法,填写输出视图传递过来的数据。

配置模板目录:在当前项目根目录下创建了模板目录templates. 然后在settings.py, 模板相关配置,找到TEMPLATES配置项,填写DIRS设置模板目录。(这一步可以按照默认的,不需要改动)

一、简单案例:

新建一个项目DLT,项目下新建一个应用app01。

DLT项目总路由DLT/urls.py:

from django.urls import path,include

urlpatterns = [
    path('app01/', include("app01.urls")),
]

子应用app01路由app01/urls.py:

from django.urls import path
from app01.views import index

urlpatterns = [
    path('index/', index),
]

app01/views.py视图函数:

from django.shortcuts import render
from django.shortcuts import HttpResponse
from django.template.loader import get_template

# Create your views here.

def index(request):

    '''

    # (1)获取模板文件
    # get_template会从项目配置中找到模板目录,我们需要填写的参数就是补全模板文件的路径
    template = get_template("index.html")

    # (2)获取数据
    content = { "name":"Tom","age0":18}

    # (3)渲染
    html = template.render(content,request)

    # print(":::",html)

    return HttpResponse(html)
    # return HttpResponse(html,content_type="text/plain") # 这个是纯文本格式
    '''

    # 以上三步可以用render一步:
    return render(request,"index.html",{ "name":"Tom","age0":18})

templates/index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>  </title>
</head>
<body>
<h1>welcome,  {{ name }}</h1>

</body>
</html>

DTL模板文件与普通html文件的区别在哪里?

DTL模板文件是一种带有特殊语法的HTML文件,这个HTML文件可以被Django编译,可以传递参数进去,实现数据动态化。在编译完成后,生成一个普通的HTML文件,然后发送给客户端。

开发中,我们一般把开发中的文件分2种,分别是静态文件和动态文件。

静态文件,数据保存在当前文件,不需要经过任何处理就可以展示出去。普通html文件,图片,视频,音频等这一类文件叫静态文件。

动态文件,数据并不在当前文件,而是要经过服务端或其他程序进行编译转换才可以展示出去。 编译转换的过程往往就是使用正则或其他技术把文件内部具有特殊格式的变量转换成真实数据。 动态文件,一般数据会保存在第三方存储设备,如数据库中。django的模板文件,就属于动态文件。

二、模板语法

  • 变量渲染(深度查询、过滤器) :
    {{val}}
    {{val|filter_name:参数}}
  • 标签 :
    {% tag_name %}
  • 嵌套和继承

 

1.深度查询

取列表或字典的某个元素用的是一个点"  .  ",这个东西叫句点符,比如下面例子里取列表里某个值: booklist.1,再比如取字典里的某个值:zhangsan.age

示例:

app01/views.py:

from django.shortcuts import render

def index(request):

    name = "root"
    age = "22"
    isMarried = False

    boollist = ["浪潮之巅","数学之美","计算之魂"]

    zhangsan = {"name":"张三","age":26}

    class Book():
        def __init__(self,title,price):
            self.title = title
            self.price = price
        def __str__(self): # 用这个魔法方法,打印的就不是对象了
            return self.title

    book01 = Book("机器学习",78)
    book02 = Book("Java编程思想",55)
    book03 = Book("数据挖掘",98)

    books =[book01,book02,book03]

    # locals()的作用是将所有局部变量全部作为上下文对象传进去,开发的时候建议不用这个locals函数,用什么传什么
    return render(request,"index.html",locals())

templates/index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>  </title>
</head>
<body>
<h1>深度查询</h1>

<p> 姓名: {{ name }}</p>
<p> 年龄: {{ age }}</p>
<p> 婚姻状况: {{ isMarried }}</p>

<p> 吴军老师的书:{{ boollist }}</p>
<p> 第二本书:{{  boollist.1}}</p>

<p> 个人信息:{{ zhangsan }}</p>
<p> 张三的年龄:{{ zhangsan.age }}</p>

<p> {{ book01 }}</p>
<p> {{ book01.price }}</p>

<p> 书籍列表:{{ books  }}</p>
<p> 书籍列表:{{ books.1.title  }}</p>


</body>
</html>

 

2.内置过滤器

语法:

{{obj|过滤器名称:过滤器参数}}

内置过滤器:重点也不在这些,重点在对于过滤器在干什么的理解

过滤器用法代码
last 获取列表/元组的最后一个成员 {{list | last}}
first 获取列表/元组的第一个成员 {{list | first }}
length 获取数据的长度 {{list | length }}
defualt 当变量没有值的情况下, 系统输出默认值, {{str| default="默认值"  }}
safe 让系统不要对内容中的html代码进行实体转义 {{htmlcontent|  safe }}
upper 字母转换成大写 {{str | upper  }}
lower 字母转换成小写 {{str |  lower }}
title 每个单词首字母转换成大写 {{str | title  }}
date 日期时间格式转换 {{ value| date:'D d M Y'  }}
cut 从内容中截取掉同样字符的内容 {{content |  cut:"hello" }}
list 把内容转换成列表格式 {{content |  list }}
add 加法 {{num|  escape }}
filesizeformat 把文件大小的数值转换成单位表示 {{filesize |  filesizeformat }}
join 按指定字符拼接内容 {{list|  join("-") }}
random 随机提取某个成员 {list| random  }}
slice 按切片提取成员 {{list | slice:":-2"  }}
truncatechars 按字符长度截取内容 {{content | truncatechars:30  }}
truncatewords 按单词长度截取内容 同上

示例:

app01/views.py:

from django.shortcuts import render

def index(request):

    class Book():
        def __init__(self,title,price):
            self.title = title
            self.price = price
        def __str__(self):
            return self.title

    book01 = Book("机器学习",78)
    book02 = Book("Java编程思想",55)
    book03 = Book("python",98)

    books =[book01,book02,book03]
    books2 = []

    import datetime
    now = datetime.datetime.now()

    filesize = 122324 # 这玩意是字节数哈

    link = "<a href='http://www.baidu.com'> baidu </a>"

    # locals()的作用是将所有局部变量全部作为上下文对象传进去,开发的时候建议不用这个locals函数,用什么传什么
    return render(request,"index.html",locals())

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>  </title>
</head>
<body>
<h1>内置过滤器</h1>

<p> 最后一本书籍:{{ books|last  }}</p>
<p> 书籍大写:{{ books.2.title|upper }}</p>
<p> 列表中共有{{ books|length }}本书籍 }</p>

<p> books2展示:{{ books2 }}</p>
<p> books2展示:{{ books2|default:"暂时没有书籍" }}</p> <!-- FALSE时default生效-->

<p> 日期:{{ now }}</p>
<p> 日期:{{ now|date:"Y/M/d D" }}</p>  <!-- 格式随便,想怎么用就怎么用,d和D自己试试,反正不一样的东西-->

<p> 文件大小:{{ filesize }}</p>   <!--文件大小:122324-->
<p> 文件大小:{{ filesize|filesizeformat }}</p>  <!--文件大小:119.5 KB-->

<p>展示链接:{{ link }}</p> <!--django自己做了安全机制,把链接转化成其他字符,反正就是这个意思-->
<p>展示链接:{{ link|safe }}</p>  <!--这样Django就不会处理这个链接了,当成安全的直接出去-->

</body>
</html>

3.自定义过滤器

虽然官方已经提供了许多内置的过滤器给开发者,但是很明显,还是会有存在不足的时候。例如:希望输出用户的手机号码时, 13912345678 ---->> 139*****678,这时我们就需要自定义过滤器。要声明自定义过滤器并且能在模板中正常使用,需要完成2个前置的工作:

1)需要在项目的settings.py里确定INSTALL_APPS里有我们用的应用也就是app01:

 2)自定义过滤器函数必须被 template.register进行装饰使用.而且过滤器函数所在的模块必须在templatetags包里面保存。

在app01子应用下创建templatetags包,这个包名字必须是这个,因为Django在找自定义过滤器的时候,就会在注册的app里找一个templatetags的文件夹,找到了之后就会去读里面的py文件。

里面的模块叫什么倒是无所谓,我叫my_filters.py:

from django import template

register = template.Library()

@register.filter("mobile_fmt")
def mobile_fmt(content):
    print(type(content))
    return content[:3]+"****"+ content[-4:]

我们的index.html开头需要加上一行{%  load my_filters %}将自定义过滤器注册到里面:

<!--文件注册到模板里 -->
{%  load my_filters %}
<!--django会去每一个注册的应用里找一个templatetags文件夹里找一个my_filters文件,所以不用加前缀,所以不同应用的这个文件定义的时候不要重名-->

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>  </title>
</head>
<body>
<h1>自定义过滤器</h1>

<p> 手机号:{{ my_tel|mobile_fmt }}</p>

</body>
</html>

这样就完成了。

4.if标签

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>  </title>
</head>
<body>
<h1>标签</h1>
{% if age > 100 %}  <!--模板语法里大于小于号前后要有一个空格-->
    <p> 唐诗 </p>
{% elif age > 70 and age <= 100 %}
    <p> 宋词 </p>
{% else  %}
    <p> 元曲 </p>
{% endif %}

在python里的if直接通过业务逻辑决定执行哪些代码,而我们在模板引擎的if决定了哪些字符串替换了整套标签的内容,根本上还是在渲染的过程中做了字符串替换。

5.for标签

from django.shortcuts import render

def index(request):

    class Book():
        def __init__(self, title, price,chubanshe):
            self.title = title
            self.price = price
            self.chubanshe = chubanshe

        def __str__(self):  # 用这个魔法方法,打印的就不是对象了
            return self.title

    book01 = Book("机器学习", 78,"洋柿子出版社")
    book02 = Book("Java编程思想", 55,"洋柿子出版社")
    book03 = Book("数据挖掘", 98,"西瓜出版社")

    books = [book01, book02, book03]

    # locals()的作用是将所有局部变量全部作为上下文对象传进去,开发的时候建议不用这个locals函数,用什么传什么
    return render(request,"index.html",locals())

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>  </title>
</head>
<body>
<h1>for标签</h1>

<table width="800" border="1">
    <tr>
        <th>书籍名称</th>
        <th>价格</th>
        <th>出版社</th>
    </tr>

    {% for book in books %}
    <tr>
        <th> {{ book.title }}</th>
        <th> {{ book.price }} </th>
        <th> {{ book.chubanshe }}</th>
    </tr>
    {% endfor %}
</table>

</body>
</html>

效果如下:

如果我们想要在这个表格前面再给它加上一个序号,那么就需要使用到for标签里的内置变量forloop.counter:

 最终效果:

循环中, 模板引擎提供的forloop对象,用于给开发者获取循环次数或者判断循环过程的.

属性描述
forloop.counter 显示循环的次数,从1开始
forloop.counter0 显示循环的次数,从0开始
forloop.revcounter0 倒数显示循环的次数,从0开始
forloop.revcounter 倒数显示循环的次数,从1开始
forloop.first 判断如果本次是循环的第一次,则结果为True
forloop.last 判断如果本次是循环的最后一次,则结果为True
forloop.parentloop 在嵌套循环中,指向当前循环的上级循环

6.include嵌入

{% include "模板文件名"%} # 模板嵌入

新建一个ad.html文件:(里面内容不重要)(这里是做了一个右下角的广告框)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

     <style>
        .advertise{
            width:200px;
            hight:150px;
            background-color: aqua;
            position: fixed;
            bottom:10px;
            right: 10px;
        }
    </style>

</head>
<body>

<div class="advertise"> 广告 </div>

</body>
</html>

 

在订单order.html页面嵌套上面的ad.html页面 :{% include "ad.html" %}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>订单</title>

</head>
<body>

<h3>嵌入</h3>

<ul>
    {% for order in order_list %}
        <li> {{ order }}</li>
    {% endfor %}
</ul>

{% include "ad.html" %}

</body>
</html>

7.继承

传统的模板分离技术,依靠{% include "模板文件名"%}实现,这种方式,虽然达到了页面代码复用的效果,但是由此也会带来大量的碎片化模板,导致维护模板的成本上升。因此, Django框架中除了提供这种模板分离技术以外,还并行的提供了 模板继承给开发者。

{% include "模板文件名"%} # 模板嵌入
{% extends "base.html" %} # 模板继承

extend比include更加强大,以后也是推荐使用extend。

(1) 继承父模板的公共内容

{% extends "base.html" %}

视图, home.views.py代码:

def index(request):
	"""模板继承"""
	return render(request,"index.html",locals())

子模板, templates/index.html:

{% extends "base.html" %}

父模板, templates/base.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>base.html的头部</h1>
    <h1>base.html的内容</h1>
    <h1>base.html的脚部</h1>
</body>
</html>

(2) 个性展示不同于父模板的内容

{%block %} 独立内容 {%endblock%}

{{block.super}}

视图home.views.py, 代码:

def index(request):
	"""模板继承"""

	return render(request,"index.html",locals())

def home(request):
	"""模板继承"""
	return render(request,"home.html",locals())

路由 home.urls.py,代码:

from django.urls import path
from . import views
urlpatterns = [
	path("", views.index),
	path("home/", views.home),
]

子模板index.html,代码:

{% extends "base.html" %}
{% block title %}index3的标题{% endblock  %}
{% block content %}
    {{ block.super }} {# 父级模板同名block标签的内容 #}
    <h1>index3.html的独立内容</h1>
    {{ block.super }}
{% endblock %}

子模板home.html,代码:

{% extends "base.html" %}
{% block title %}home的标题{% endblock %}

父模板base.html,代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}{% endblock  %}</title>
</head>
<body>
    <h1>base.html的头部</h1>
    {% block content %}
    <h1>base.html的内容</h1>
    {% endblock %}
    <h1>base.html的脚部</h1>
</body>
</html>

如果你在模版中使用 {% extends %} 标签,它必须是模版中的第一个标签。其他的任何情况下,模版继承都将无法工作。

在base模版中设置越多的 {% block %} 标签越好。请记住,子模版不必定义全部父模版中的blocks,所以,你可以在大多数blocks中填充合理的默认内容,然后,只定义你需要的那一个。多一点钩子总比少一点好。

为了更好的可读性,你也可以给你的 {% endblock %} 标签一个 名字 。例如:{%block content%}...{%endblock content%},在大型模版中,这个方法帮你清楚的看到哪一个  {% block %} 标签被关闭了。

不能在一个模版中定义多个相同名字的 block 标签。

三、静态文件

开发中在开启了debug模式时,django可以通过配置,允许用户通过对应的url地址访问django的静态文件。

setting.py,代码:

STATIC_ROOT = BASE_DIR / 'static'
STATIC_URL = '/static/'   # django模板中,可以引用{{STATIC_URL}}变量避免把路径写死。  

总路由,urls.py,代码:

from django.views.static import serve as serve_static
urlpatterns = [
    path('admin/', admin.site.urls), 
    # 对外提供访问静态文件的路由,serve_static 是django提供静态访问支持的映射类。依靠它,客户端才能访问到django的静态文件。
    path(r'static/<path:path>', serve_static, {'document_root': settings.STATIC_ROOT},),
]

注意:项目上线以后,关闭debug模式时,django默认是不提供静态文件的访问支持,项目部署的时候,我们会通过收集静态文件使用nginx这种web服务器来提供静态文件的访问支持。

 

posted on 2023-12-07 18:52  szdbjooo  阅读(30)  评论(0)    收藏  举报